WebGL切換著色器 繪製不同物體
阿新 • • 發佈:2018-12-19
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