[WebGL入門]十一,著色器的編譯和連線
注:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。
反覆重複的東西
這已經是第11篇了,因為只說了一些基本的東西,到現在為止連個多邊形也沒繪製出來。哎呀呀呀......
不管怎麼說吧,基礎是很重要的。那就在這些基礎上,來繪製一個多邊形吧。需要準備的知識都已經介紹過了,剩下的就是按照步驟開始繪製多邊形。首先,來確認一下繪製的步驟。
・從HTML中獲取canvas物件
・從canvas中獲取WebGL的context
・編譯著色器
・準備模型資料
・頂點快取(VBO)的生成和通知
・座標變換矩陣的生成和通知
・發出繪圖命令
・更新canvas並渲染
上面所列舉的步驟中,到現在為止完全沒有介紹的是最後兩個,關於繪圖命令的部分和canvas更新部分。稍後,我會說一說它們基本的概念。雖然這些步驟看起來挺複雜的,但是這就是使用WebGL進行渲染的基本步驟。
這次的文章,就從上到下來依次看一下前三個步驟,先說到編譯著色器。
*其中有幾個步驟順序可以有變化,現在先按照這個順序來看一下。
HTML和canvas的處理
關於HTML和canvas的處理,在之前的文章中(七,context的初始化)也已經講過了。基本上也不會有什麼變化,在這裡再說一下。
>HTML程式碼
<html>
<head>
<title>WebGL TEST</title>
<script src="script.js" type="text/javascript"></script>
<script src="minMatrix.js" type="text/javascript"></script>
<script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script>
<script id="fs" type="x-shader/x-fragment">
void main(void){
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
HTML程式碼就是上面這樣,head標籤中引用了兩個javascript檔案,一個是WebGL的處理檔案script.js,另一個是本網站自己寫的矩陣計算的庫minMatrix.js。頂點著色器的處理
接著,該出現頂點著色器和片段著色器的程式碼了。首先,先從type是x-shader/x-vertex的頂點著色器開始。下面是頂點著色器部分的程式碼。
>頂點著色器的程式碼
attribute vec3 position;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(position, 1.0);
}
這裡用到了一個attribute變數和一個uniform變數。變數position的型別是vec3,是一個3維向量。裡面是頂點的位置,向量的三個元素分別是X,Y,Z座標。
另一個uniform宣告的變數mvpMatrix,型別是mat4,所以是一個4x4的方陣。是模型,檢視,投影的各個變換矩陣結合後的座標變換矩陣。
這次的頂點著色器,只是利用座標變換矩陣來變換頂點的座標位置,使用乘法運算。這時候,為了讓position和矩陣相乘,使用vec4函式,先將其變成一個4維的向量,然後相乘,最後將計算結果代入到gl_Position,頂點著色器的處理結束。
片段著色器的處理
接著說片段著色器。
這次,繪製的模型是一個簡單的三角形,先不進行著色,只是使用白色來填充。所以,片段著色器中的處理,就只是將白色資訊傳給gl_FragColor中。下面是片段著色器的程式碼。
>片段著色器的程式碼
void main(void){
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
關於顏色,基本上使用vec3或者vec4的情況比較多。因為一般就是使用RGB或者RGBA,需要3~4個元素。這一次使用的vec4是所有的引數都是1.0的向量,顏色是白色[紅,綠,藍,不透明度的各元素都是最大=白色]。編譯程式碼,生成著色器
接著來看頂點著色器和片段著色器的編譯。
編譯也不需要什麼特別的編譯器,只需要呼叫WebGL內部的函式就可以進行編譯了。準備一個函式,從著色器的編譯,到實際著色器的生成這一連串的流程,都在這一個函式中來完成。下面是這個函式的程式碼。
*下面的函式中的gl,是提前從WebGL的context中獲取的。
>生成和編譯著色器的函式
function create_shader(id){
// 用來儲存著色器的變數
var shader;
// 根據id從HTML中獲取指定的script標籤
var scriptElement = document.getElementById(id);
// 如果指定的script標籤不存在,則返回
if(!scriptElement){return;}
// 判斷script標籤的type屬性
switch(scriptElement.type){
// 頂點著色器的時候
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break;
// 片段著色器的時候
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default :
return;
}
// 將標籤中的程式碼分配給生成的著色器
gl.shaderSource(shader, scriptElement.text);
// 編譯著色器
gl.compileShader(shader);
// 判斷一下著色器是否編譯成功
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
// 編譯成功,則返回著色器
return shader;
}else{
// 編譯失敗,彈出錯誤訊息
alert(gl.getShaderInfoLog(shader));
}
}
使用這個函式的時候,需要傳入script標籤的id作為引數,函式中根據這個id來獲取指定的標籤。生成著色器的時候,使用WebGL中的函式createShader。這個函式在生成著色器的時候,根據頂點著色器和片段著色器的不同來傳入不同的引數。引數指定為gl.VERTEX_SHADER的時候會生成頂點著色器,引數指定為gl.FRAGMENT_SHADER的時候會生成片段著色器。
首先,將程式碼分配給生成的著色器的時候,使用的是shaderSource函式,引數有兩個,第一個引數是著色器物件,第二個引數是著色器的程式碼。這時候,只是把著色器的程式碼分配給了著色器,還沒有進行編譯。編譯的時候,使用的是compileShader函式,將著色器物件作為引數傳給這個函式,就可以將著色器進行編譯。
著色器是否編譯成功,通過著色器中的引數可以判斷,獲取這個引數的時候,使用getShaderParameter函式,並使用WebGL中存在的常量COMPILE_STATUS作為引數。如果這時候的返回值是false,則表示因為什麼原因導致編譯失敗了,要想檢視原因的話,將著色器傳入getShaderInfoLog函式中,就可以確認log了。
這個自定義函式,無論是頂點著色器還是片段著色器,都可以進行編譯。實際上,頂點著色器和片段著色器的處理不同的地方就是createShader函式,其他地方是完全一樣的。
程式物件的生成和連線
著色器生成之後,接著來生成程式物件。這裡突然出現的程式物件,到底是個什麼物件呢?
以前的文章中(八,著色器的說明和基礎)也稍微接觸了一些,使用varying修飾符定義的變數,可以從頂點著色器向片段著色器中傳遞資料。其實,實現從一個著色器向另一個著色器傳遞資料的,不是別的,就是程式物件。程式物件是管理頂點著色器和片段著色器,或者WebGL程式和各個著色器之間進行資料的互相通訊的重要的物件。
那麼,生成程式物件,並把著色器傳給程式物件,然後連線著色器,將這些處理函式化。
>程式物件的生成和著色器連線的函式
function create_program(vs, fs){
// 程式物件的生成
var program = gl.createProgram();
// 向程式物件裡分配著色器
gl.attachShader(program, vs);
gl.attachShader(program, fs);
// 將著色器連線
gl.linkProgram(program);
// 判斷著色器的連線是否成功
if(gl.getProgramParameter(program, gl.LINK_STATUS)){
// 成功的話,將程式物件設定為有效
gl.useProgram(program);
// 返回程式物件
return program;
}else{
// 如果失敗,彈出錯誤資訊
alert(gl.getProgramInfoLog(program));
}
}
這個函式接收頂點著色器和片段著色器兩個引數。然後,首先生成程式物件,分配著色器。生成著色器的時候,使用WebGL中的函式createProgram,將著色器分配給程式物件的時候使用函式attachShader,attachShader函式的第一個引數是程式物件,第二個引數是著色器。著色器分配結束後,根據程式物件,要連線兩個著色器,這時候使用linkProgram函式,引數就是程式物件。
和生成著色器一樣,要判斷著色器的連線是否成功,這時候使用getProgramParameter函式,並傳入常量LINK_STATUS。如果沒有問題的話,為了將程式物件設定為有效,使用useProgram函式。注意,如果不執行這個函式的話,在WebGL中是無法識別這個程式物件的。如果連線失敗,則彈出錯誤資訊,使用getProgramInfoLog函式來得到log。
總結
來簡單總結一下本次的內容。
HTML程式碼中引入必要的javascript檔案,以及描述著色器的程式碼。
準備了著色器的編譯函式和連線著色器的程式物件相關的函式。每個函式中都有是否進行了正確處理的判斷。
下次,準備好頂點資料,也就是說準備好模型資料,然後變換為VBO。按照前面說的步驟,一步步全都理解的話,就應該沒問題了,加油吧。
相關文章
- webgl入門(2)-初識webgl和著色器Web
- [WebGL入門]八,著色器的說明和基礎Web
- webgl 系列 —— 著色器語言Web
- WebGL中的OpenGL著色器語言Web
- Rust 編譯器入門Rust編譯
- JIT 編譯器快速入門編譯
- WebGL著色器渲染小遊戲實戰Web遊戲
- WebGL 3D概念講解(著色器)Web3D
- WebGL:使用著色器進行幾何造型Web
- WebGL之延遲著色Web
- 編譯器背後的故事(入門練習)編譯
- [WebGL入門]一,瀏覽器的準備Web瀏覽器
- 交叉編譯入門編譯
- OpenGL入門第二課--常用的固定儲存著色器
- vc 編譯連線選項編譯
- webgl入門(1)-什麼是webglWeb
- c#入門-編譯的概念C#編譯
- webGL入門-四階貝塞爾曲線繪製Web
- webgl世界 matrix入門Web
- 淺談彙編器、編譯器和直譯器編譯
- 編譯器的自展和自舉、交叉編譯編譯
- CMake入門指南-編譯教程編譯
- WebGL著色器32位浮點數精度損失問題Web
- [WebGL入門]六,頂點和多邊形Web
- OpenGL/OpenGL ES入門: 渲染流程以及固定儲存著色器
- 編譯、彙編、連結、載入、顯示編譯
- 程式的編譯和連結原理分析編譯
- (轉)編譯和連結的區別編譯
- 關於程式的編譯和連結編譯
- 工程中的編譯原理 -- Jison入門篇編譯原理
- [WebGL入門]十,矩陣計算和外部庫Web矩陣
- WebGPU 計算管線、計算著色器(通用計算)入門案例:2D 物理模擬WebGPU
- CesiumJS PrimitiveAPI 高階著色入門 - 從引數化幾何與 Fabric 材質到著色器 - 下篇JSMITAPI
- CesiumJS PrimitiveAPI 高階著色入門 - 從引數化幾何與 Fabric 材質到著色器 - 上篇JSMITAPI
- C編譯器LEX 和 YACC輸入原始檔。 (轉)編譯
- [WebGL入門]四,渲染準備Web
- GCC編譯和連結過程GC編譯
- C語言中編譯和連結C語言編譯