1 /*在這裏對這段時間學習的3D編程知識作個總結,以備再次出發。計劃分紅「webgl與three.js基礎介紹」、「面向對象的基礎3D場景框架編寫」、「模型導入與簡單3D遊戲編寫」三個部分,其餘零散知識之後有機會再總結。*/ 2 /*第一部分,webgl與three.js基礎介紹,要求讀者掌握JavaScript入門知識*/ 3 //webgl原理:經過JavaScript語言在瀏覽器端生成glsl代碼,把glsl代碼送入顯卡執行,把執行結果顯示在瀏覽器中 4 //簡單例程: 5 //根據Tony Parisi著《WebGL入門指南》第一章修改而來(簡稱T) 6 window.onload=webGLStart; 7 var gl; 8 function webGLStart() 9 { 10 var canvas = document.getElementById("can_main");//canvas是html5下的繪圖標籤,能夠支持3D繪圖 11 gl=initGL(canvas);//初始化「繪製上下文」,之後的繪製都要經過它進行 12 var square=createSquare(gl);//創建一個演示用的四邊形,包括頂點座標,頂點數組格式和頂點繪製方法 13 var matrix=initMatrices();//定義兩個矩陣 14 15 var shaderProgram=initShaders();//定義着色器程序(glsl) 16 17 draw(gl,square,matrix,shaderProgram);//調用顯卡進行繪製 18 onLoad();//稍後用來觸發three.js方式的繪圖 19 } 20 function initGL(canvas){ 21 var gl; 22 try 23 { 24 gl = canvas.getContext("experimental-webgl");//從canvas中獲取webgl上下文 25 gl.viewport(0,0,canvas.width,canvas.height);//設置視口 26 } 27 catch(e) 28 { 29 var msg="Error creating WebGL Context!: "+ e.toString(); 30 alert(msg); //彈出錯誤信息 31 } 32 return gl; 33 } 34 function createSquare(gl) 35 { 36 var vertexBuffer=gl.createBuffer();//創建一個頂點緩存 37 gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); 38 var verts = [ 39 1.0, 1.0, 0.0, 40 -1.0, 1.0, 0.0, 41 1.0, -1.0, 0.0, 42 -1.0, -1.0, 0.0 43 ];//把三維空間中的四個頂點存儲在一個一維數組中 44 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);//把數組元素的存儲方式設爲「32位浮點數」 45 var square={buffer:vertexBuffer,vertSize:3,nVerts:4,primtype:gl.TRIANGLE_STRIP}; //使用一個JavaScript對象返回信息:使用vertexBuffer緩存頂點信息,三維空間頂點,共四個頂點,使用「三角形帶」圖元繪製方法 46 return square; 47 } 48 function initMatrices() 49 { 50 //定義姿態矩陣,即全部物體共用的相對於原點的位置和姿態 51 var modelViewMatrix=new Float32Array( 52 [1,0,0,0, 53 0,1,0,0, 54 0,0,1,0, 55 0,0,-30.333,1] 56 ); 57 //定義投影矩陣,即物體近大遠小的透視程度 58 var projectionMatrix=new Float32Array([ 59 2.41421,0,0,0, 60 0,2.41421,0,0, 61 0,0,-1.002002,-1, 62 0,0,-0.2002002,0 63 ]); 64 var matrix={mvm:modelViewMatrix,pjm:projectionMatrix}; 65 return matrix; 66 } 67 function initShaders() 68 { 69 /*着色器(Shader)位於顯卡上,分爲頂點着色器和片元着色器兩種,數量各以千記。 70 其中頂點着色器運行頂點着色器程序,負責對每一個頂點的位置顏色信息的計算; 71 片元着色器運行片元着色器程序,負責對頂點之間的內容進行「插值」,得出每一個像素的顏色; 72 頂點着色器+片元着色器+光柵化=顯卡渲染管線*/ 73 //頂點着色器 74 var vertexShaderSource= 75 " attribute vec3 vertexPos;\n"+ 76 " uniform mat4 modelViewMatrix;\n"+ 77 " uniform mat4 projectionMatrix;\n"+ 78 " void main(void) {\n"+ 79 " //返回變換並投影后的頂點數據\n"+ 80 " gl_Position=projectionMatrix*modelViewMatrix*vec4(vertexPos,1.0);\n"+ 81 " }\n"; 82 //片元着色器 83 var fragmentShaderSource= 84 " void main(void){\n"+ 85 " //返回像素顏色:永遠輸出白色\n"+ 86 " gl_FragColor=vec4(1.0,1.0,1.0,1.0);\n"+ 87 " }\n"; 88 //glsl的註釋是「//」 89 90 //這裏是對shader代碼的編譯 91 var vertexShader=gl.createShader(gl.VERTEX_SHADER); 92 gl.shaderSource(vertexShader, vertexShaderSource); 93 gl.compileShader(vertexShader); 94 if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { 95 alert(gl.getShaderInfoLog(shader)); 96 } 97 98 var fragmentShader=gl.createShader(gl.FRAGMENT_SHADER); 99 gl.shaderSource(fragmentShader, fragmentShaderSource); 100 gl.compileShader(fragmentShader); 101 if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { 102 alert(gl.getShaderInfoLog(shader)); 103 } 104 105 //對glsl進行鏈接 106 var shaderProgram = gl.createProgram(); 107 gl.attachShader(shaderProgram, vertexShader); 108 gl.attachShader(shaderProgram, fragmentShader); 109 gl.linkProgram(shaderProgram); 110 gl.getProgramParameter(shaderProgram, gl.LINK_STATUS); 111 112 return shaderProgram;//返回編譯鏈接以後的着色器程序 113 } 114 function draw(gl,obj,matrix,shaderProgram)//這裏只進行了一個物體的一次繪製,事實上obj徹底能夠是一個物體數組 115 { 116 gl.clearColor(0.0,0.0,0.0,1.0);//使用徹底不透明的黑色清屏 117 gl.clear(gl.COLOR_BUFFER_BIT); 118 119 gl.bindBuffer(gl.ARRAY_BUFFER,obj.buffer);//將gl對象與這個物體的緩存暫時綁定 120 121 gl.useProgram(shaderProgram);//讓gl使用上面定義的着色器程序 122 123 gl.enableVertexAttribArray(gl.getAttribLocation(shaderProgram, "vertexPos"));//告訴WebGL咱們將經過一個array提供頂點信息 124 gl.vertexAttribPointer(gl.getAttribLocation(shaderProgram, "vertexPos"),obj.vertSize,gl.FLOAT,false,0,0);//提供頂點信息,頂點信息被保存在頂點着色器的vertexPos屬性裏(變量映射) 125 gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, "projectionMatrix"),false,matrix.pjm); 126 gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, "modelViewMatrix"),false,matrix.mvm);//將兩個矩陣的信息發送給顯卡 127 128 gl.drawArrays(obj.primtype,0,obj.nVerts);//通知顯卡按照頂點數組畫圖 129 }
1 //爲提升編程效率,人們編寫了一些基於WebGL的繪圖引擎,Three.js是其中應用較廣的一種: 2 //使用Three.js實現一樣的繪製 3 function onLoad() 4 { 5 var container=document.getElementById("container"); 6 7 //Three.js定義的「場景」對象 8 var scene=new THREE.Scene(); 9 //進行WebGL兼容性判斷 10 if(webglAvailable()){ 11 var renderer=new THREE.WebGLRenderer(); 12 }else{ 13 var renderer=new THREE.CanvasRenderer();//對於環境支持html5但不支持webgl的狀況,能夠嘗試使用更慢一些的2Dcanvas來軟件繪圖,但效果差強人意 14 } 15 renderer.setSize(container.offsetWidth,container.offsetHeight);//render能夠當作是對canvas的一種擴展 16 container.appendChild(renderer.domElement); 17 18 //定義相機,相似於WebGL中的定義投影矩陣, 19 var camera=new THREE.PerspectiveCamera(45,container.offsetWidth/container.offsetHeight,1,4000); 20 camera.position.set(0,0,30.3333);//起原版中姿態矩陣的共用部分的做用,但不一樣的是:這裏是觀察者向相反的方向移動了。 21 scene.add(camera);//相機對象被添加到了場景中,能夠認爲場景對象是用來與顯卡進行交互的東西,其中包含了變量映射 22 23 var geometry=new THREE.PlaneGeometry(2,2);//庫中包含預製的幾何體,經過少許參數便可生成完整的頂點數組,這裏是一個寬高爲二的四邊形 24 var mesh=new THREE.Mesh(geometry,new THREE.MeshBasicMaterial());//賦予這個幾何體材質,材質對應反光性 25 scene.add(mesh); 26 27 renderer.render(scene,camera);// 渲染 28 } 29 function webglAvailable() 30 {//webgl的繪圖是創建在顯卡的基礎上的,若是這臺計算機沒有顯卡,或者瀏覽器不支持和顯卡的通訊,webgl上下文的創建將會失敗 31 try{ 32 var canvas=document.createElement("canvas"); 33 return !!(window.WebGLRenderingContext 34 &&(canvas.getContext("webgl")||canvas.getContext("experimental-webgl")) 35 ); 36 }catch(e){ 37 return false; 38 } 39 }
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title>webgl與three.js基礎介紹</title> 6 </head> 7 <body> 8 <!--在canvas中使用webglAPI繪製--> 9 <canvas id="can_main" width="500" height="500" 10 style="width:500px;height:500px;float: left"></canvas> 11 <!--使用Three.js庫繪製--> 12 <div id="container" style="width:500px;height:500px;background-color: #000000;float: left"></div> 13 </body> 14 15 <script> 16 17 </script> 18 <!----> 19 <script src="mygl1.js"></script> 20 <script src="mygl1b.js"></script> 21 22 <!--引用three.js庫文件--> 23 <!--three庫是由多個文件聯合成的一個大庫,連接方式不一樣、庫版本不一樣會產生不一樣的庫文件!!--> 24 25 <!--這個版本舊一些可是隻須要引用一個文件--> 26 <!--https://github.com/tparisi/WebGLBook/tree/master/libs--> 27 <!--script src="../LIBS/Three.js"></script--> 28 29 <!--這個版本較新但須要引用額外的文件--> 30 <!--https://github.com/mrdoob/three.js/tree/master/build--> 31 <!--https://github.com/mrdoob/three.js/tree/master/examples/js/renderers--> 32 <script src="three.min.js"></script> 33 <script src="Projector.js"></script> 34 <script src="CanvasRenderer.js"></script> 35 36 </html>
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <title>加入光照、紋理、運動效果</title> 5 <meta charset="UTF-8"> 6 <!--稍微複雜一點的場景,根據Giles Thomas的learningwebgl.com第七章修改而來(簡稱G)--> 7 <!--一個數學庫用來實現一些數學計算--> 8 <script src="glMatrix-0.9.5.min.js" type="text/javascript"></script> 9 <!--google的一個幀動畫輔助庫--> 10 <script src="webgl-utils.js" type="text/javascript"></script> 11 12 <!--G把着色器代碼放在了script標籤裏--> 13 <script id="shader-vs" type="x-shader/x-vertex"> 14 attribute vec3 aVertexPosition;//頂點位置數組 15 attribute vec3 aVertexNormal;//法線向量數組 16 attribute vec2 aTextureCoord;//紋理座標數組 17 18 uniform mat4 uMVMatrix;//模型姿態矩陣 19 uniform mat4 uPMatrix;//投影矩陣 20 uniform mat3 uNMatrix; 21 22 uniform vec3 uAmbientColor;//環境光 23 24 uniform vec3 uLightingDirection;//方向光方向 25 uniform vec3 uDirectionalColor;//方向光顏色 26 27 uniform bool uUseLighting;//是否使用光照 28 //在glsl中,attribute和uniform是輸入頂點着色器的只讀常量,varying是從頂點着色器中輸入片元着色器的變量 29 //vec3是含有三個份量的元素組成的數組,mat4是4*4矩陣 30 varying vec2 vTextureCoord; 31 varying vec3 vLightWeighting;//頂點光照 32 33 void main(void) { 34 gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);//頂點位置數組通過模型姿態矩陣和投影矩陣變化後獲得頂點位置 35 vTextureCoord = aTextureCoord; 36 37 if (!uUseLighting) { 38 vLightWeighting = vec3(1.0, 1.0, 1.0);//若是選擇不使用光照則設爲最強白光 39 } else { 40 vec3 transformedNormal = uNMatrix * aVertexNormal;//對法線向量數組進行姿態變換 41 float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0);//按照法線方向和方向光方向計算反射光強度 42 vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;//反射光加環境光獲得頂點光照狀況,這裏使用的是最簡單的光照模型,沒有涉及材質。 43 } 44 } 45 </script> 46 <script id="shader-fs" type="x-shader/x-fragment"> 47 precision mediump float;//顯卡處理浮點數的精度 48 49 varying vec2 vTextureCoord; 50 varying vec3 vLightWeighting; 51 52 uniform sampler2D uSampler;//二維紋理句柄 53 54 void main(void) { 55 vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));//根據紋理座標對紋理進行切割 56 //OpenGL標準顯卡爲提升模型縮放時的渲染效率,須要爲模型紋理創建多個逐漸縮小的縮略圖,受此限制圖片的邊長必須爲2的整數次方,但實際紋理基本不會符合此要求,因此實際紋理圖片會把不規則紋理放在規則圖片中,再使用紋理座標進行切割提取 57 gl_FragColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a);//紋理顏色乘以光照,再加上透明度信息做爲實際片元顏色 58 } 59 </script> 60 <!--gl_FragColor是glsl的保留字,表示片元着色器的輸出--> 61 62 63 <!--程序入口在webGLStart()函數--> 64 <script type="text/javascript"> 65 66 var gl; 67 //初始化weblg上下文 68 function initGL(canvas) { 69 try { 70 gl = canvas.getContext("experimental-webgl"); 71 gl.viewportWidth = canvas.width; 72 gl.viewportHeight = canvas.height; 73 } catch (e) { 74 } 75 if (!gl) { 76 alert("Could not initialise WebGL, sorry :-("); 77 } 78 } 79 80 //生成着色器代碼 81 function getShader(gl, id) { 82 var shaderScript = document.getElementById(id); 83 if (!shaderScript) { 84 return null; 85 } 86 87 var str = ""; 88 var k = shaderScript.firstChild; 89 while (k) { 90 if (k.nodeType == 3) { 91 str += k.textContent; 92 } 93 k = k.nextSibling; 94 } 95 96 var shader; 97 if (shaderScript.type == "x-shader/x-fragment") { 98 shader = gl.createShader(gl.FRAGMENT_SHADER); 99 } else if (shaderScript.type == "x-shader/x-vertex") { 100 shader = gl.createShader(gl.VERTEX_SHADER); 101 } else { 102 return null; 103 } 104 105 gl.shaderSource(shader, str);//根據網頁標籤裏的內容去生成shader program內容 106 gl.compileShader(shader); 107 108 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 109 alert(gl.getShaderInfoLog(shader)); 110 return null; 111 } 112 113 return shader; 114 } 115 116 117 var shaderProgram;//用來和顯卡交互的對象,是不少屬性的結合體 118 //初始化着色器 119 function initShaders() { 120 var fragmentShader = getShader(gl, "shader-fs"); 121 var vertexShader = getShader(gl, "shader-vs");//根據標籤內容編譯着色器代碼 122 123 shaderProgram = gl.createProgram(); 124 gl.attachShader(shaderProgram, vertexShader); 125 gl.attachShader(shaderProgram, fragmentShader);//把兩個着色器的代碼分別裝填到shaderProgram中 126 gl.linkProgram(shaderProgram);//鏈接着色器代碼 127 128 if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {//經過gl的屬性驗證鏈接是否成功 129 alert("Could not initialise shaders"); 130 } 131 132 gl.useProgram(shaderProgram); 133 //下面是須要傳給顯卡的參數(變量映射),經過這種方式把JavaScript中的變量和glsl中的輸入量關聯起來 134 shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); 135 gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); 136 137 shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal"); 138 gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute); 139 140 shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); 141 gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); 142 143 shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); 144 shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); 145 shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix"); 146 shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); 147 shaderProgram.useLightingUniform = gl.getUniformLocation(shaderProgram, "uUseLighting"); 148 shaderProgram.ambientColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientColor"); 149 shaderProgram.lightingDirectionUniform = gl.getUniformLocation(shaderProgram, "uLightingDirection"); 150 shaderProgram.directionalColorUniform = gl.getUniformLocation(shaderProgram, "uDirectionalColor"); 151 } 152 153 154 //處理載入的圖片文件 155 function handleLoadedTexture(texture) { 156 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 157 158 gl.bindTexture(gl.TEXTURE_2D, texture); 159 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);//將圖片對象變成紋理對象 160 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);//圖片放大或扭曲時的插值方式 161 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); 162 gl.generateMipmap(gl.TEXTURE_2D);//縮略圖 163 164 gl.bindTexture(gl.TEXTURE_2D, null); 165 } 166 167 168 var crateTexture; 169 var neheTexture; 170 //載入圖片文件 171 function initTexture() { 172 crateTexture = gl.createTexture(); 173 crateTexture.image = new Image(); 174 crateTexture.image.onload = function () {//onload事件表明圖片載入完成 175 handleLoadedTexture(crateTexture) 176 } 177 crateTexture.image.src = "crate.gif";//使用google chrome進行本地測試時注意跨域設置!! 178 179 neheTexture = gl.createTexture(); 180 neheTexture.image = new Image(); 181 neheTexture.image.onload = function () { 182 handleLoadedTexture(neheTexture) 183 } 184 neheTexture.image.src = "nehe.gif"; 185 //這裏載入了兩個圖片,其實徹底能夠把兩幅圖片合在一塊兒使用紋理座標切割選擇 186 } 187 188 189 var mvMatrix = mat4.create(); 190 var mvMatrixStack = []; 191 var pMatrix = mat4.create(); 192 193 //考慮到場景中存在多個物體,它們的姿態矩陣有公用的部分也有私有的部分,故在設置私有部分時把共有部分入棧 194 function mvPushMatrix() { 195 var copy = mat4.create(); 196 mat4.set(mvMatrix, copy); 197 mvMatrixStack.push(copy); 198 } 199 //設置完私有部分後將共有部分出棧,再去設置下一個物體 200 function mvPopMatrix() { 201 if (mvMatrixStack.length == 0) { 202 throw "Invalid popMatrix!"; 203 } 204 mvMatrix = mvMatrixStack.pop(); 205 } 206 207 //三個矩陣的變量映射 208 function setMatrixUniforms() { 209 gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);//投影 210 gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);//頂點 211 212 var normalMatrix = mat3.create();//法線 213 mat4.toInverseMat3(mvMatrix, normalMatrix); 214 mat3.transpose(normalMatrix); 215 gl.uniformMatrix3fv(shaderProgram.nMatrixUniform, false, normalMatrix); 216 } 217 218 //角度轉化爲弧度,webgl默認支持弧度 219 function degToRad(degrees) { 220 return degrees * Math.PI / 180; 221 } 222 223 224 225 var xRot = 0; 226 var xSpeed = 3; 227 228 var yRot = 0; 229 var ySpeed = -3; 230 231 var z = -5.0; 232 233 //鍵盤處理,經過這種方式能夠處理同時按下多個按鍵 234 var currentlyPressedKeys = {}; 235 236 function handleKeyDown(event) { 237 currentlyPressedKeys[event.keyCode] = true; 238 } 239 240 241 function handleKeyUp(event) { 242 currentlyPressedKeys[event.keyCode] = false; 243 } 244 245 246 function handleKeys() { 247 if (currentlyPressedKeys[33]) { 248 // Page Up 249 z -= 0.05; 250 } 251 if (currentlyPressedKeys[34]) { 252 // Page Down 253 z += 0.05; 254 } 255 if (currentlyPressedKeys[37]) { 256 // Left cursor key 257 ySpeed -= 1; 258 } 259 if (currentlyPressedKeys[39]) { 260 // Right cursor key 261 ySpeed += 1; 262 } 263 if (currentlyPressedKeys[38]) { 264 // Up cursor key 265 xSpeed -= 1; 266 } 267 if (currentlyPressedKeys[40]) { 268 // Down cursor key 269 xSpeed += 1; 270 } 271 } 272 273 var cubeVertexPositionBuffer; 274 var cubeVertexNormalBuffer; 275 var cubeVertexTextureCoordBuffer; 276 var cubeVertexIndexBuffer; 277 var cubeVertexIndexBuffer2; 278 //初始化緩存 279 function initBuffers() { 280 cubeVertexPositionBuffer = gl.createBuffer(); 281 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer); 282 //一個正方形的頂點數組 283 vertices = [ 284 // Front face 285 -1.0, -1.0, 1.0, 286 1.0, -1.0, 1.0, 287 1.0, 1.0, 1.0, 288 -1.0, 1.0, 1.0, 289 290 // Back face 291 -1.0, -1.0, -1.0, 292 -1.0, 1.0, -1.0, 293 1.0, 1.0, -1.0, 294 1.0, -1.0, -1.0, 295 296 // Top face 297 -1.0, 1.0, -1.0, 298 -1.0, 1.0, 1.0, 299 1.0, 1.0, 1.0, 300 1.0, 1.0, -1.0, 301 302 // Bottom face 303 -1.0, -1.0, -1.0, 304 1.0, -1.0, -1.0, 305 1.0, -1.0, 1.0, 306 -1.0, -1.0, 1.0, 307 308 // Right face 309 1.0, -1.0, -1.0, 310 1.0, 1.0, -1.0, 311 1.0, 1.0, 1.0, 312 1.0, -1.0, 1.0, 313 314 // Left face 315 -1.0, -1.0, -1.0, 316 -1.0, -1.0, 1.0, 317 -1.0, 1.0, 1.0, 318 -1.0, 1.0, -1.0, 319 ]; 320 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 321 cubeVertexPositionBuffer.itemSize = 3; 322 cubeVertexPositionBuffer.numItems = 24; 323 324 //正方形每一個面的法線向量 325 cubeVertexNormalBuffer = gl.createBuffer();//法線向量 326 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer); 327 var vertexNormals = [ 328 // Front face 329 0.0, 0.0, 1.0, 330 0.0, 0.0, 1.0, 331 0.0, 0.0, 1.0, 332 0.0, 0.0, 1.0, 333 334 // Back face 335 0.0, 0.0, -1.0, 336 0.0, 0.0, -1.0, 337 0.0, 0.0, -1.0, 338 0.0, 0.0, -1.0, 339 340 // Top face 341 0.0, 1.0, 0.0, 342 0.0, 1.0, 0.0, 343 0.0, 1.0, 0.0, 344 0.0, 1.0, 0.0, 345 346 // Bottom face 347 0.0, -1.0, 0.0, 348 0.0, -1.0, 0.0, 349 0.0, -1.0, 0.0, 350 0.0, -1.0, 0.0, 351 352 // Right face 353 1.0, 0.0, 0.0, 354 1.0, 0.0, 0.0, 355 1.0, 0.0, 0.0, 356 1.0, 0.0, 0.0, 357 358 // Left face 359 -1.0, 0.0, 0.0, 360 -1.0, 0.0, 0.0, 361 -1.0, 0.0, 0.0, 362 -1.0, 0.0, 0.0 363 ]; 364 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), gl.STATIC_DRAW); 365 cubeVertexNormalBuffer.itemSize = 3; 366 cubeVertexNormalBuffer.numItems = 24; 367 368 //每一個面的紋理座標 369 cubeVertexTextureCoordBuffer = gl.createBuffer(); 370 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer); 371 var textureCoords = [ 372 // Front face 373 0.0, 0.0, 374 1.0, 0.0, 375 1.0, 1.0, 376 0.0, 1.0, 377 378 // Back face 379 1.0, 0.0, 380 1.0, 1.0, 381 0.0, 1.0, 382 0.0, 0.0, 383 384 // Top face 385 0.0, 1.0, 386 0.0, 0.0, 387 1.0, 0.0, 388 1.0, 1.0, 389 390 // Bottom face 391 1.0, 1.0, 392 0.0, 1.0, 393 0.0, 0.0, 394 1.0, 0.0, 395 396 // Right face 397 1.0, 0.0, 398 1.0, 1.0, 399 0.0, 1.0, 400 0.0, 0.0, 401 402 // Left face 403 0.0, 0.0, 404 1.0, 0.0, 405 1.0, 1.0, 406 0.0, 1.0 407 ]; 408 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW); 409 cubeVertexTextureCoordBuffer.itemSize = 2; 410 cubeVertexTextureCoordBuffer.numItems = 24; 411 412 //頂點繪製順序,即存在24個頂點,分別使用其中的哪幾個頂點繪製三角形 413 //由於使用了兩個紋理句柄,這裏將正方形分紅了兩個物體,下降了效率 414 cubeVertexIndexBuffer = gl.createBuffer(); 415 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer); 416 var cubeVertexIndices = [ 417 //0, 1, 2, 0, 2, 3, // Front face 418 //4, 5, 6, 4, 6, 7, // Back face 419 //8, 9, 10, 8, 10, 11, // Top face 420 12, 13, 14, 12, 14, 15, // Bottom face 421 16, 17, 18, 16, 18, 19, // Right face 422 20, 21, 22, 20, 22, 23 // Left face 423 ]; 424 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW); 425 //cubeVertexIndexBuffer.itemSize = 1; 426 //cubeVertexIndexBuffer.numItems = 36; 427 428 cubeVertexIndexBuffer2 = gl.createBuffer(); 429 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer2); 430 var cubeVertexIndices2 = [ 431 0, 1, 2, 0, 2, 3, // Front face 432 4, 5, 6, 4, 6, 7, // Back face 433 8, 9, 10, 8, 10, 11 // Top face 434 //12, 13, 14, 12, 14, 15, // Bottom face 435 //16, 17, 18, 16, 18, 19, // Right face 436 //20, 21, 22, 20, 22, 23 // Left face 437 ]; 438 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices2), gl.STATIC_DRAW); 439 } 440 441 //場景繪製 442 function drawScene() { 443 gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); 444 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 445 446 //光照 447 var lighting = document.getElementById("lighting").checked; 448 gl.uniform1i(shaderProgram.useLightingUniform, lighting); 449 //從html中讀取光照設置 450 if (lighting) { 451 gl.uniform3f( 452 shaderProgram.ambientColorUniform, 453 parseFloat(document.getElementById("ambientR").value), 454 parseFloat(document.getElementById("ambientG").value), 455 parseFloat(document.getElementById("ambientB").value) 456 ); 457 var lightingDirection = [ 458 parseFloat(document.getElementById("lightDirectionX").value), 459 parseFloat(document.getElementById("lightDirectionY").value), 460 parseFloat(document.getElementById("lightDirectionZ").value) 461 ]; 462 var adjustedLD = vec3.create(); 463 vec3.normalize(lightingDirection, adjustedLD); 464 465 vec3.scale(adjustedLD, -1); 466 gl.uniform3fv(shaderProgram.lightingDirectionUniform, adjustedLD); 467 gl.uniform3f( 468 shaderProgram.directionalColorUniform, 469 parseFloat(document.getElementById("directionalR").value), 470 parseFloat(document.getElementById("directionalG").value), 471 parseFloat(document.getElementById("directionalB").value) 472 ); 473 } 474 475 //變換姿態矩陣,創造出物體運動效果 476 mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix); 477 mat4.identity(mvMatrix); 478 mat4.translate(mvMatrix, [0.0, 0.0, z]); 479 mat4.rotate(mvMatrix, degToRad(xRot), [1, 0, 0]); 480 mat4.rotate(mvMatrix, degToRad(yRot), [0, 1, 0]); 481 setMatrixUniforms();//考慮到多個物體的變化,這個矩陣設置加入在繪製以前,若是須要在另外一個位置繪製物體,就再設置一次姿態矩陣 482 483 //頂點數組變量映射 484 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer); 485 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); 486 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer); 487 gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, cubeVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0); 488 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer); 489 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0); 490 491 //紋理與頂點索引 492 gl.activeTexture(gl.TEXTURE0); 493 gl.bindTexture(gl.TEXTURE_2D, crateTexture); 494 gl.uniform1i(shaderProgram.samplerUniform, 0);//傳給着色器 495 496 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer); 497 gl.drawElements(gl.TRIANGLES, 18, gl.UNSIGNED_SHORT, 0);//與上一例程中的「三角形帶」方式不一樣,包含頂點繪製順序時使用drawElements方法繪製 498 499 gl.activeTexture(gl.TEXTURE1); 500 gl.bindTexture(gl.TEXTURE_2D, neheTexture); 501 gl.uniform1i(shaderProgram.samplerUniform, 1);//傳給着色器,替換了上一個samplerUniform變量的值 502 503 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer2); 504 gl.drawElements(gl.TRIANGLES, 18, gl.UNSIGNED_SHORT, 0); 505 } 506 507 508 var lastTime = 0; 509 //根據延遲時間計算動畫效果,使得動畫效果不受幀率影響 510 function animate() { 511 var timeNow = new Date().getTime(); 512 if (lastTime != 0) { 513 var elapsed = timeNow - lastTime; 514 515 xRot += (xSpeed * elapsed) / 1000.0; 516 yRot += (ySpeed * elapsed) / 1000.0; 517 } 518 lastTime = timeNow; 519 } 520 521 522 function tick() { 523 requestAnimFrame(tick);//在瀏覽器認爲能夠時重調循環,這樣能夠隨着系統負載狀況不一樣產生動態幀率 524 handleKeys();//處理鍵盤 525 drawScene();//繪製場景 526 animate();//幀動畫 527 } 528 529 530 function webGLStart() { 531 var canvas = document.getElementById("lesson07-canvas"); 532 initGL(canvas);//上下文 533 initShaders();//着色器 534 initBuffers();//緩存 535 initTexture();//紋理 536 537 gl.clearColor(0.0, 0.0, 0.0, 1.0); 538 gl.enable(gl.DEPTH_TEST);//深度探測打開,webgl認爲被遮擋的三角形將不被渲染,處理半透明物體時需關掉深度探測 539 540 document.onkeydown = handleKeyDown;//聲明鍵盤事件 541 document.onkeyup = handleKeyUp; 542 543 tick();//繪製循環 544 } 545 /*能夠看到,隨着頁面複雜度的提升,webglAPI繪製方法的變量映射和glsl代碼愈來愈多,所以在實際應用中每每使用運行庫對這部分代碼進行封裝。至於頂點數組、法線向量、紋理座標、紋理圖片則使用標準繪製函數或模型文件進行封裝*/ 546 </script> 547 548 549 </head> 550 551 552 <body onload="webGLStart();"> 553 <a href="http://learningwebgl.com/blog/?p=684"><< Back to Lesson 7</a><br> 554 555 <canvas width="500" height="500" id="lesson07-canvas" style="border: currentColor; border-image: none;"></canvas> 556 557 <br> 558 <input id="lighting" type="checkbox" checked=""> Use lighting<br> 559 (Use cursor keys to spin the box and <code>Page Up</code>/<code>Page Down</code> to zoom out/in) 560 561 <br> 562 <h2>Directional light:</h2> 563 564 <table style="padding: 10px; border: 0px currentColor; border-image: none;"> 565 <tbody><tr> 566 <td><b>Direction:</b> 567 <td>X: <input id="lightDirectionX" type="text" value="-0.25"> 568 <td>Y: <input id="lightDirectionY" type="text" value="-0.25"> 569 <td>Z: <input id="lightDirectionZ" type="text" value="-1.0"> 570 </tr> 571 <tr> 572 <td><b>Colour:</b> 573 <td>R: <input id="directionalR" type="text" value="0.8"> 574 <td>G: <input id="directionalG" type="text" value="0.8"> 575 <td>B: <input id="directionalB" type="text" value="0.8"> 576 </tr> 577 </tbody></table> 578 579 <h2>Ambient light:</h2> 580 <table style="padding: 10px; border: 0px currentColor; border-image: none;"> 581 <tbody><tr> 582 <td><b>Colour:</b> 583 <td>R: <input id="ambientR" type="text" value="0.2"> 584 <td>G: <input id="ambientG" type="text" value="0.2"> 585 <td>B: <input id="ambientB" type="text" value="0.2"> 586 </tr> 587 </tbody></table> 588 589 <a href="http://learningwebgl.com/blog/?p=684"><< Back to Lesson 7</a> 590 591 592 593 </body></html>
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <title>使用Three.js實現上述效果</title> 5 <meta charset="UTF-8"> 6 <link rel="stylesheet" href="../css/webglbook.css" /> 7 <script src="Three.js"></script> 8 <script src="webgl-utils.js"></script> 9 <script> 10 11 var renderer = null, 12 scene = null, 13 camera = null, 14 cube = null, 15 animating = false; 16 17 function onLoad() 18 { 19 // Grab our container div 20 //取得做爲容器的div,事實上容器不侷限於div標籤 21 var container = document.getElementById("container"); 22 23 // Create the Three.js renderer, add it to our div 24 //創建Three.js渲染器,並把它加入容器div中 25 renderer = new THREE.WebGLRenderer( { antialias: true } ); 26 renderer.setSize(container.offsetWidth, container.offsetHeight); 27 container.appendChild( renderer.domElement ); 28 29 // Create a new Three.js scene 30 //創建一個Three.js場景 31 scene = new THREE.Scene(); 32 33 // Put in a camera 34 //引入一個相機 35 camera = new THREE.PerspectiveCamera( 45, container.offsetWidth / container.offsetHeight, 1, 4000 ); 36 camera.position.set( 0, 0, 3 ); 37 38 // Create a directional light to show off the object 39 //創建一個方向光來照亮場景中的物體 40 var light = new THREE.DirectionalLight( 0xffffff, 1.5); 41 light.position.set(0, 0, 1); 42 scene.add( light ); 43 44 // Create a shaded, texture-mapped cube and add it to the scene 45 //創建一個具備陰影效果和紋理效果的立方體,並把它加入到場景中 46 // First, create the texture map 47 //第一步,創建紋理圖片(Three.js不要求圖片尺寸是2的整數次方) 48 var mapUrl = "molumen_small_funny_angry_monster.jpg"; 49 var map = THREE.ImageUtils.loadTexture(mapUrl); 50 51 // Now, create a Phong material to show shading; pass in the map 52 //創建一個Phong類型的材質對象來表現光照和陰影;把這個材質傳入紋理圖中 53 var material = new THREE.MeshPhongMaterial({ map: map }); 54 55 // Create the cube geometry 56 //創建一個默認的幾何體 57 var geometry = new THREE.CubeGeometry(1, 1, 1); 58 59 // And put the geometry and material together into a mesh 60 //把「多邊形」和「材質」組合成一個「網格」 61 cube = new THREE.Mesh(geometry, material); 62 63 // Turn it toward the scene, or we won't see the cube shape! 64 //稍微旋轉一下物體,不然咱們看不出立體的效果! 65 cube.rotation.x = Math.PI / 5; 66 cube.rotation.y = Math.PI / 5; 67 68 // Add the cube to our scene 69 //把物體加入到場景中 70 scene.add( cube ); 71 72 // Add a mouse up handler to toggle the animation 73 //添加鼠標監聽 74 addMouseHandler(); 75 76 // Run our render loop 77 //啓動渲染循環 78 run(); 79 } 80 81 function run() 82 { 83 // Render the scene 84 //渲染場景,renderer是canvas的繪製上下文,scene是變量映射接口,camera是姿態矩陣和投影矩陣的結合 85 renderer.render( scene, camera ); 86 87 // Spin the cube for next frame 88 //旋轉物體生成下一幀畫面 89 if (animating) 90 { 91 cube.rotation.y -= 0.01; 92 } 93 94 // Ask for another frame 95 //請求下一幀(循環) 96 requestAnimFrame(run); 97 } 98 99 function addMouseHandler() 100 { 101 var dom = renderer.domElement; 102 103 dom.addEventListener( 'mouseup', onMouseUp, false); 104 } 105 106 function onMouseUp (event) 107 { 108 event.preventDefault(); 109 110 animating = !animating; 111 } 112 113 </script> 114 115 </head> 116 <body onLoad="onLoad();" style=""> 117 <center><h1>Welcome to WebGL!</h1></center> 118 <div id="container" style="width:95%; height:80%; position:absolute;"></div> 119 <div id="prompt" style="width:95%; height:6%; bottom:0; text-align:center; position:absolute;">Click to animate the cube</div> 120 121 </body> 122 </html>