<html lang="zh-CN"> <!--服務器運行地址:http://127.0.0.1:8080/webgl/LearnNeHeWebGL/NeHeWebGL4.html--> <head> <title>NeHe's WebGL</title> <meta charset="UTF-8"/> <!--引入需要的庫文件--> <script type="text/javascript" src="Oak3D_v_0_5.js"></script> <!--片元著色器;為JavaScript片段指定一個ID編號,后面我可以更具這個ID編號來獲取這段片元著色器的JavaScript片段代碼--> <script id="shader-fs" type="x-shader/x-fragment"> precision mediump float;varying vec4 vColor;void main(void) {gl_FragColor = vColor;}</script> <!--頂點著色器;后面可以通過ID編號來獲取這段頂點著色器代碼--> <script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aVertexPosition;attribute vec4 aVertexColor;uniform mat4 uMVMatrix;uniform mat4 uPMatrix;varying vec4 vColor;void main(void) {gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);vColor = aVertexColor;}</script> <script type="text/javascript"> var gl; //初始化WEBGL function initGL(canvas) { try { //獲取WEBGL上下文 gl = canvas.getContext("experimental-webgl"); //gl這個上下文中存放了一些屬性(canvas的寬度、長度和其他相關屬性數據) //設置我的視口的寬度和高度 gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; } catch (e) { } //如果獲取失敗 if (!gl) { alert("Could not initialise WebGL, sorry :-("); } } //獲取我的著色器對象 function getShader(gl, id) { //根據id獲取著色器源程序代碼 var shaderScript = document.getElementById(id); if (!shaderScript) { return null; } var str = ""; var k = shaderScript.firstChild; while (k) { if (k.nodeType == 3) { str += k.textContent; } k = k.nextSibling; } var shader; //1.根據著色器的類型創建相應的著色器對象 if (shaderScript.type == "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (shaderScript.type == "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; } //2.向著色器對象中指定相應的GLSL ES源代碼(以字符串的形式傳遞進去) gl.shaderSource(shader, str); //3.開始編譯著色器(編譯成為二進制的可執行文件) gl.compileShader(shader); //檢查下著色器的狀態(是否編譯成功) if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader)); return null; } return shader; } //一個著色器對象必須包含一個頂點著色器和一個片元著色器 var shaderProgram; //初始化著色器 function initShaders() { //獲取我的頂點著色器和片元著色器 var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs"); //每一個program中可以存放一個頂點著色器和一個片元著色器 //4.創建我的程序對象 shaderProgram = gl.createProgram(); //5.為程序對象分配著色器對象 gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); //6.鏈接程序對象 /** * 1.可以保證頂點著色器和片元著色器同名并且是同類型的 * 2.attribute,uniform和varying變量個數不超過著色器的上限 */ gl.linkProgram(shaderProgram); //檢測是否連接成功 if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Could not initialise shaders"); } //7.告訴WEBGL要使用的程序對象 gl.useProgram(shaderProgram); //指定一個新的屬性;gl.enableVertexAttribArray,我們使用它來告訴WebGL我們會用一個數組來為屬性賦值 //頂點的位置信息 shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); //獲取我的頂點著色器中的attribute顏色變量 shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor"); gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); //從program中取得另外的兩個屬性值(模型視圖投影矩陣) shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); } //定義了我的模型視圖矩陣和投影矩陣 var mvMatrix; var pMatrix; //在這里實現我的矩陣的進棧和出棧操作 var mvMatrixStack = []; function myPushMatrix() { var copy = new okMat4(); mvMatrix.clone(copy); mvMatrixStack.push(copy); } function myPopMatrix() { if (mvMatrixStack.length == 0) { throw "Invalid popMatrix!"; } mvMatrix = mvMatrixStack.pop(); } //把我們新設置的模型視圖投影矩陣傳給頂點著色器中的uniform變量 function setMatrixUniforms() { gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix.toArray()); gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix.toArray()); } //定義我的三角形和矩形緩沖區頂點位置 var pyramidVertexPositionBuffer; var cubeVertexPositionBuffer; //定義我的三角形和矩形緩沖區的頂點顏色 var pyramidVertexColorBuffer; var cubeVertexColorBuffer; //定義我的立方體索引下標 var cubeVertexIndexBuffer; //緩沖區的初始化 function initBuffers() { //1.新建三角形頂點緩沖區對象 pyramidVertexPositionBuffer = gl.createBuffer(); //2.綁定目標對象到緩沖區 gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer); //初始化我的頂點數組 var vertices = [ // Front face 0.0, 1.0, 0.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, // Right face 0.0, 1.0, 0.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, // Back face 0.0, 1.0, 0.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, // Left face 0.0, 1.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0 ]; //3.緩沖區對象中寫入數據 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); //計算頂點數組的大小和頂點個數 pyramidVertexPositionBuffer.itemSize = 3; pyramidVertexPositionBuffer.numItems = 12; //1.創建我的顏色緩沖區 pyramidVertexColorBuffer = gl.createBuffer(); //2.綁定我的顏色緩沖區到目標對象 gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer); //初始化我的顏色數組(對每一個頂點指定相應的顏色) var colors = [ //注意保證在同一個頂點上面的顏色要相同 // Front face 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // Right face 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, // Back face 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // Left face 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0 ]; //3.向緩沖區對象中寫入數據 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); //計算三角形頂點顏色數組的大小和頂點個數 pyramidVertexColorBuffer.itemSize = 4; pyramidVertexColorBuffer.numItems = 12; //1.新建矩形頂點緩沖區對象 cubeVertexPositionBuffer = gl.createBuffer(); //2.綁定目標對象到緩沖區 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer); //立方體的頂點位置數組 vertices = [ // Front face(123第一個三角形, 134第二個三角形) -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, // Back face -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // Top face -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, // Bottom face -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, // Right face 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, // Left face -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0 ]; //3.向緩沖區對象中寫入數據 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); //計算矩形頂點數組每一項數據的大小,和頂點個數(有四個不同的頂點位置,每個頂點由3個數字組成) cubeVertexPositionBuffer.itemSize = 3; cubeVertexPositionBuffer.numItems = 24; //1.創建我的立方體的頂點顏色緩沖區 cubeVertexColorBuffer = gl.createBuffer(); //2.綁定目標對象到緩沖區 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer); //定義我的矩形的顏色數組 colors = [ [1.0, 0.0, 0.0, 1.0], // Front face[red] [1.0, 1.0, 0.0, 1.0], // Back face[yellow] [0.0, 1.0, 0.0, 1.0], // Top face[green] [1.0, 0.5, 0.5, 1.0], // Bottom face[] [1.0, 0.0, 1.0, 1.0], // Right face[] [0.0, 0.0, 1.0, 1.0] // Left face[blue] ]; //對于矩形的四個頂點賦予相同的顏色 //每一個面的四個頂點都是同樣的顏色 /*** * 1.0, 0.0, 0.0, 1.0 * 1.0, 0.0, 0.0, 1.0 * 1.0, 0.0, 0.0, 1.0 * 1.0, 0.0, 0.0, 1.0 * 1.0, 1.0, 0.0, 1.0 * 1.0, 1.0, 0.0, 1.0 * 1.0, 1.0, 0.0, 1.0 * 1.0, 1.0, 0.0, 1.0 * ……………………………… * 0.0, 0.0, 1.0, 1.0 * 0.0, 0.0, 1.0, 1.0 * 0.0, 0.0, 1.0, 1.0 * 0.0, 0.0, 1.0, 1.0 * @type {Array} */ var unpackedColors = []; for (var i in colors) { var color = colors[i]; for (var j=0; j < 4; j++) { unpackedColors = unpackedColors.concat(color); } } //3.向緩沖區對象中寫入數據 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors), gl.STATIC_DRAW); //計算我的正方形的頂點數組 cubeVertexColorBuffer.itemSize = 4; cubeVertexColorBuffer.numItems = 24; //開始定義我的頂點位置數組 //1.創建我的頂點索引緩沖區對象 cubeVertexIndexBuffer = gl.createBuffer(); //2.綁定目標對象到緩沖區 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer); //設置我的頂點索引數組 var cubeVertexIndices = [ 0, 1, 2, 0, 2, 3, // Front face 4, 5, 6, 4, 6, 7, // Back face 8, 9, 10, 8, 10, 11, // Top face 12, 13, 14, 12, 14, 15, // Bottom face 16, 17, 18, 16, 18, 19, // Right face 20, 21, 22, 20, 22, 23 // Left face ]; //3.向緩沖區對象寫入數據 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW); //計算我的頂點索引數組的大小(每一項數據的大小,總共36個數據) /** * 1個不同的頂點位置(numItems),每個頂點由36個數字組成(itemSize) * @type {number} */ cubeVertexIndexBuffer.itemSize = 1; cubeVertexIndexBuffer.numItems = 36; } //定義我的三角形和我的矩形的初始旋轉角度 var rPyramid = 0; var rCube = 0; //繪制我的場景(三角形和矩形) function drawScene() { //設置視口大小 gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); //清空顏色緩存和深度緩存 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); //建立一個透視投影(視場角,視口比例,最近,最遠距離) pMatrix = okMat4Proj(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0); //新建一個模型視圖矩陣 mvMatrix = new okMat4(); //保存矩陣的初始狀態 myPushMatrix(); //設置我的模型視圖矩陣為平移矩陣 //mvMatrix = okMat4Trans(-1.5, 0.0, -7.0); mvMatrix.translate(OAK.SPACE_WORLD, -1.5, 0.0, -8.0, true); //三角形的椎體繞著Y軸旋轉(本地坐標系旋轉) mvMatrix.rotY(OAK.SPACE_LOCAL, rPyramid, true); //1.綁定三角形頂點數據到緩沖區對象 gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, pyramidVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); //2.綁定三角形顏色信息到緩沖區對象,并且傳遞給頂點著色器 gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer); gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, pyramidVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0); //告訴WEBGL當前使用的模型視圖投影矩陣 setMatrixUniforms(); //開始繪制三角形(從第0個位置開始,繪制numItems個頂點) gl.drawArrays(gl.TRIANGLES, 0, pyramidVertexPositionBuffer.numItems); //繪制完畢后再次恢復我的模型視圖矩陣 myPopMatrix(); //再次保存我的模型視圖矩陣 myPushMatrix(); //開始繪制立方體 mvMatrix.translate(OAK.SPACE_WORLD, 1.5, 0.0, -8.0, true); //讓我的矩形繞著XYZ軸旋轉 //mvMatrix.rotX(OAK.SPACE_LOCAL, rCube, true); mvMatrix.rot(OAK.SPACE_LOCAL, rCube, 1.0, 1.0, 1.0, true); //綁定四邊形的頂點信息(與索引下標綁定在一起) gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); //開始傳遞顏色數據 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer); gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0); //開始綁定索引下標信息 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer); setMatrixUniforms(); //開始繪制立方體 //gl.drawArrays(gl.TRIANGLE_STRIP, 0, cubeVertexPositionBuffer.numItems); gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); //繪制完畢后,恢復我的模型視圖矩陣的初始狀態 myPopMatrix(); } //這里是我的主函數 function webGLStart() { //獲取canvas元素 var canvas = document.getElementById("lesson01-canvas"); //初始化WEBGL上下文信息 initGL(canvas); //初始化著色器 initShaders(); //出事阿虎緩沖區 initBuffers(); //指定清空畫布的顏色,開啟隱藏面消除的功能 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); //開始繪制我的場景 //drawScene(); //開始使用動畫效果來繪制我的圖形 tick(); } //實現我的動畫繪制函數 function tick() { //重復調用tick函數 okRequestAnimationFrame(tick); //開始繪制場景 drawScene(); //改變我的三角形和我的矩陣的旋轉角度 animate(); } //開始不斷修改我的旋轉角度 var lastTime = 0; function animate() { var timeNow = new Date().getTime(); if (lastTime != 0) { var elapsed = timeNow - lastTime; //三角形 90/s, 矩形 75/s rPyramid += (90 * elapsed) / 1000.0; rCube -= (75 * elapsed) / 1000.0; } lastTime = timeNow; } </script> </head> <body οnlοad="webGLStart();"> <canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas> </body> </html>