Web3D編程入門總結——WebGL與Three.js基礎介紹

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">&lt;&lt; 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">&lt;&lt; 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>
相關文章
相關標籤/搜索