1. 程式人生 > >WebGL切換著色器 繪製不同物體

WebGL切換著色器 繪製不同物體

WebGL切換著色器 繪製不同物體

1、為何切換著色器

WebGL繪製不同的物體需要使用不同的著色器,每個著色器中可能有非常負責的邏輯以實現各種不同的效果。我們可以準備多個著色器,然後根據需要來切換使用它們。

2、如何實現切換著色器

為了切換著色器,首先建立多個著色器程式物件,然後在繪製之前選擇使用的程式物件(使用gl.useProgram()函式來進行切換)。
下面是本次例項程式(例項程式繪製了兩個立方體:一個單色立方體,一個紋理立方體)的流程步驟:

1. 準備繪製單色立方體的著色器

2. 準備繪製紋理立方體的著色器

3. 呼叫createProgram()函式,利用第1步創建出的著色器,建立單色立方體著色器程式物件

4. 呼叫createProgram()函式,利用第2步創建出的著色器,建立紋理立方體著色器程式物件

5. 呼叫gl.useProgram()函式,指定使用第3步建立的繪製單色立方體的著色器程式物件。

6. 通過緩衝區物件向著色器中傳入attribute變數並開啟。

7. 繪製單色立方體。

8. 呼叫gl.useProgram()函式,指定使用第4步建立的繪製紋理立方體的著色器程式物件。

9. 通過緩衝區物件向著色器傳入attribute變數並開啟。

10. 繪製紋理立方體。

3、例項程式

效果圖:在這裡插入圖片描述

例項程式碼:


<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>切換著色器-繪製不同立方體<
/title> </head> <body onload="main()"> <canvas id="webgl" width="400" height="400"> Please use the browser supporting "canvas" </canvas> <script src="../lib/webgl-utils.js"></script> <script src="../lib/webgl-debug.js"></script> <script src="../lib/cuon-utils.js"></script> <script src="../lib/cuon-matrix.js"></script> <script> // canvas全屏 var canvas = document.getElementById('webgl'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // 頂點著色器 繪製單色立方體 var SOLID_VSHADER_SOURCE = "attribute vec4 a_Position;\n" + "attribute vec4 a_Normal;\n" + // 法向量 "uniform mat4 u_MvpMatrix;\n" + "uniform mat4 u_NormalMatrix;\n" + "varying vec4 v_Color;\n" + "void main(){\n" + " vec3 lightDirection = vec3(0.0,0.0, 1.0);\n" +//設定燈光的座標位置(世界座標下) " vec4 color = vec4(0.0, 1.0, 1.0, 1.0);\n" + //設定立方體的顏色 " gl_Position = u_MvpMatrix * a_Position;\n" + " vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n" + " float nDotL = max(dot(normal, lightDirection), 0.0);\n" + " v_Color = vec4(color.rgb * nDotL, color.a);\n" + "}\n"; // 片元著色器 繪製單色立方體 var SOLID_FSHADER_SOURCE = "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec4 v_Color;\n" + "void main(){\n" + " gl_FragColor = v_Color;\n" + "}\n"; // 頂點著色器 繪製紋理立方體 var TEXTURE_VSHADER_SOURCE = "attribute vec4 a_Position;\n" + "attribute vec4 a_Normal;\n" + "attribute vec2 a_TexCoord;\n" + "uniform mat4 u_MvpMatrix;\n" + "uniform mat4 u_NormalMatrix;\n" + "varying float v_NdotL;\n" + "varying vec2 v_TexCoord;\n" + "void main(){\n" + " vec3 lightDirection = vec3(0.0, 0.0, 1.0);\n" + " gl_Position = u_MvpMatrix * a_Position;\n" + " vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n" + " v_NdotL = max(dot(normal, lightDirection), 0.0);\n" + " v_TexCoord = a_TexCoord;\n" + "}\n"; // 片元著色器 繪製紋理立方體 var TEXTURE_FSHADER_SOURCE = "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "uniform sampler2D u_Sampler;\n" + "varying vec2 v_TexCoord;\n" + "varying float v_NdotL;\n" + "void main(){\n" + " vec4 color = texture2D(u_Sampler, v_TexCoord);\n" + " gl_FragColor = vec4(color.rgb * v_NdotL, color.a);\n" + "}\n"; // 主函式 function main() { // 獲取WebGL繪圖上下文 var gl = getWebGLContext(canvas); if(!gl) { console.log('獲取WebGL上下文失敗'); return; } // 初始化兩個程式著色器 var solidProgram = createProgram(gl,SOLID_VSHADER_SOURCE, SOLID_FSHADER_SOURCE); // 純色立方體 var textureProgram = createProgram(gl,TEXTURE_VSHADER_SOURCE,TEXTURE_FSHADER_SOURCE); // 紋理立方體 if(!solidProgram || !textureProgram) { console.log('建立程式物件失敗'); return; } // 獲取繪製單色立方體著色器的變數位置 solidProgram.a_Position = gl.getAttribLocation(solidProgram, "a_Position"); solidProgram.a_Normal = gl.getAttribLocation(solidProgram, "a_Normal"); solidProgram.u_MvpMatrix = gl.getUniformLocation(solidProgram, "u_MvpMatrix"); solidProgram.u_NormalMatrix = gl.getUniformLocation(solidProgram, "u_NormalMatrix"); if(solidProgram.a_Position < 0 || solidProgram.a_Normal < 0 || !solidProgram.u_MvpMatrix || !solidProgram.u_NormalMatrix) { console.log('獲取單色立方體相關變數儲存位置失敗'); return; } // 獲取繪製紋理立方體著色器的變數位置 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"); if(textureProgram.a_Position < 0 || textureProgram.a_Normal < 0 || textureProgram.a_TexCoord < 0 || !textureProgram.u_MvpMatrix || !textureProgram.u_NormalMatrix || !textureProgram.u_Sampler) { console.log('獲取紋理立方體相關變數儲存位置失敗'); return; } // 設定單色立方體頂點資訊 存入緩衝區 var cube = initVertexBuffers(gl); // 設定紋理立方體的紋理資料 存入緩衝區 var texture = initTextures(gl, textureProgram); if(!texture) { console.log('無法獲取到紋理'); return; } //開啟隱藏面消除功能,並設定背景色 gl.enable(gl.DEPTH_TEST); gl.clearColor(0.0, 0.0, 0.0, 1.0); // 計算檢視投影矩陣 var viewProjectMatrix = new Matrix4(); viewProjectMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0); viewProjectMatrix.lookAt(0.0, 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // 開始繪製立方體 var currentAngle = 0.0; // 當前立方體的角度 //設定一個定時繪製的函式 var tick = function() { // Start drawing currentAngle = animate(currentAngle); // 更新角度 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 繪製單色立方體 drawSolidCube(gl, solidProgram, cube, -2.0, currentAngle, viewProjectMatrix); // 繪製紋理立方體 drawTexCube(gl, textureProgram, cube, texture, 2.0, currentAngle, viewProjectMatrix); requestAnimationFrame(tick); }; tick(); } function drawTexCube(gl, program, obj, texture, x, angle, viewProjectMatrix) { gl.useProgram(program); // 分配快取物件並開啟賦值 initAttributeVariable(gl, program.a_Position, obj.vertexBuffer); //頂點座標 initAttributeVariable(gl, program.a_Normal, obj.normalBuffer); //法向量 initAttributeVariable(gl, program.a_TexCoord, obj.texCoordBuffer); //紋理座標 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.indexBuffer); //設定好紋理物件,開啟使用0號的紋理 gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); //繪製立方體 drawCube(gl, program, obj, x, angle, viewProjectMatrix) } function drawSolidCube(gl, program, obj, x, angle, viewProjectMatrix) { gl.useProgram(program); //分配緩衝區物件並啟用賦值 initAttributeVariable(gl, program.a_Position, obj.vertexBuffer); //頂點座標 initAttributeVariable(gl, program.a_Normal, obj.normalBuffer); //法向量 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.indexBuffer); //繫結索引 //繪製立方體 drawCube(gl, program, obj, x, angle, viewProjectMatrix); } // 宣告繪製需要變換矩陣變數 var g_modelMatrix = new Matrix4(); var g_mvpMatrix = new Matrix4(); var g_normalMatrix = new Matrix4(); function drawCube(gl, program, obj, x, angle, viewProjectMatrix) { // 計算模型矩陣 g_modelMatrix.setTranslate(x, 0.0, 0.0); g_modelMatrix.rotate(20.0, 1.0, 0.0, 0.0); g_modelMatrix.rotate(angle, 0.0, 1.0, 0.0); // 計算出法向量的方向 並賦值 g_normalMatrix.setInverseOf(g_modelMatrix); g_normalMatrix.transpose(); gl.uniformMatrix4fv(program.u_NormalMatrix, false, g_normalMatrix.elements); // 計算檢視模型投影矩陣 g_mvpMatrix.set(viewProjectMatrix); g_mvpMatrix.multiply(g_modelMatrix); gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements); gl.drawElements(gl.TRIANGLES, obj.numIndices, obj.indexBuffer.type, 0); } function initAttributeVariable(gl, a_attribute, buffer) { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0); gl.enableVertexAttribArray(a_attribute); } var angle_step = 30; // 每秒旋轉角度 var last = +new Date(); // 儲存上次呼叫animate函式的時間 function animate(angle) { var now = +new Date(); var elapsed = now - last; last = now; var newAngle = angle + (angle_step * elapsed) / 1000.0; return newAngle % 360.0; } function initTextures(gl,program) { var texture = gl.createTexture(); if(!texture) { console.log('無法建立紋理緩衝區'); return null; } var img = new Image(); img.onload = function() { // 將圖形資料存入紋理物件 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); // 將紋理存入到第一個紋理緩衝區 gl.useProgram(program); gl.uniform1i(program.u_Sampler, 0); gl.bindTexture(gl.TEXTURE_2D, null); // 解綁當前 } img.src = '../images/wallpaper.jpg'; return texture; } function initVertexBuffers(gl) { var vertices = new Float32Array([ 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, // v0-v1-v2-v3 front 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, // v0-v3-v4-v5 right 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, // v0-v5-v6-v1 up -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, // v1-v6-v7-v2 left -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, // v7-v4-v3-v2 down 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0 // v4-v7-v6-v5 back ]); var normals = new Float32Array([ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down 0.0