因爲對不同的場景以及不同的材質需要使用不同的着色器,因此需要使用多個着色器,而不是單個,單個着色器是無法繪製出很複雜的場景,因爲有的物體沒有紋理而有的有紋理以及別的效果
1、外部文件加載着色器
着色器語言就是 GLSL
格式的代碼,我們可以先把頂點和片元着色器分別寫在.glsl
後綴名的文件中,如果你的編譯器支持這種語法檢測,那麼代碼就會有顏色,而不僅僅是字符串樣式顯示,儘管着色器代碼本身就是字符串
下面是將要繪製的單色立方體的着色器文件
single_vertex_shader.glsl
// 頂點着色器
attribute vec4 a_Position;
attribute vec4 a_Normal;
uniform mat4 u_MvpMatrix;
uniform mat4 u_NormalMatrix;
varying vec4 v_Color;
void main(){
vec3 lightDirection = vec3(0.0, 0.0, 1.0); // 燈光的位置 在世界座標系中
vec4 color = vec4(0.5, 0.5, 0.8, 1.0);// 表面顏色
gl_Position = u_MvpMatrix * a_Position; // 計算後的頂點座標
vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));//標準化法向量
float nDot = max(dot(normal, lightDirection), 0.0);
v_Color = vec4(color.rgb * nDot, color.a);
}
single_fragment_shader.glsl
precision mediump float;
varying vec4 v_Color;
void main(){
gl_FragColor = v_Color
}
從外部文件加載着色器,其實就是加載.glsl
文件,讀取裏面的數據,然後通過頂點着色器和片元着色器創建程序對象
關於如何加載外部文件的方法,在上一篇文章中有介紹,你也可以直接去我的gitee
上倉庫的lib
目錄直接下載加載器
2、創建程序對象
加載完.glsl
文件之後,就可以使用這兩個着色器程序了,創建program
對象,下面是兩個程序對象,通過四個着色器代碼創建得到的
var singleProgram = createProgram(gl, single_vertex_shader, single_fragment_shader);
var textureProgram = createProgram(gl, texture_vertex_shader,texture_fragment_shader);
3、獲取變量存儲地址
採用javascript
對象屬性的方式,把每個變量的存儲地址掛載到程序對象上,便於使用
// 獲取 singleProgram 中的 attribute 和 uniform 變量存儲位置
singleProgram.a_Position = gl.getAttribLocation(singleProgram, 'a_Position');
singleProgram.a_Normal = gl.getAttribLocation(singleProgram, 'a_Normal');
singleProgram.u_MvpMatrix = gl.getUniformLocation(singleProgram, 'u_MvpMatrix');
singleProgram.u_NormalMatrix = gl.getUniformLocation(singleProgram, 'u_NormalMatrix');
// 獲取 textureProgram 中的 attribute 和 uniform 變量存儲位置
textureProgram.a_Position = gl.getAttribLocation(textureProgram, 'a_Position');
textureProgram.a_Normal = gl.getAttribLocation(textureProgram, 'a_Normal');
textureProgram.a_TexCoord = gl.getAttribLocation(textureProgram, 'a_TexCoord');
textureProgram.u_MvpMatrix = gl.getUniformLocation(textureProgram, 'u_MvpMatrix');
textureProgram.u_NormalMatrix = gl.getUniformLocation(textureProgram, 'u_NormalMatrix');
textureProgram.u_Sampler = gl.getUniformLocation(textureProgram, 'u_Sampler');
4、設置頂點顏色以及法線信息
因爲創建的是兩個立方體,所以它們的頂點數據和頂點索引以及法線數據都是可以共用的
var cube = initVertexBuffers(gl);
if (!cube) {
console.log('failed to set the vertex information');
return false;
}
上面通過initVertexBuffers()
方法來設置頂點信息
設置完公用的頂點信息後,一個立方體是需要加載貼圖的,因此需要把貼圖的數據給加載到貼圖立方體的片元着色器上,此處是通過initTexture(gl, program)
方法來進行設置
var texture = initTextures(gl, textureProgram);
if (!texture) {
console.log('failed to intialize the texture.');
return false;
}
設置完以上數據之後,就可以開始繪製
因爲是三維物體,有z
軸深度,需要開啓深度
gl.enable(gl.DEPTH_TEST)
// 設置canvas清空後的顏色
gl.clearColor(0.0, 0.5, 0.5, 1.0)
5、設置視圖矩陣參數
var viewProjMatrix = new Matrix4();
viewProjMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0)
viewProjMatrix.lookAt(0.0, 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
下面就可以在循環渲染方法中,進行幀刷新渲染
實際上在真正的項目開發過程中,重複這些步驟是很繁瑣的,對一些反覆的操作封裝比較好的引擎例如three.js
它隱藏了webgl
的內部執行流程,同時也內置了很多的着色器,每一種材質以及燈光的添加都對應了一種着色器,這些着色器是可以進行修改的,並且three.js
也支持自己去寫着色器以及原生webgl
代碼
https://ithanmang.gitee.io/webgl-notes/home/07-高級技術示例/08-使用不同着色器.html