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
9 <canvas id="can_main" width="500" height="500"
10 style="width:500px;height:500px;float: left">canvas>
11
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
23
24
25
26
27
28
29
30
31
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
7
8 <script src="glMatrix-0.9.5.min.js" type="text/javascript">script>
9
10 <script src="webgl-utils.js" type="text/javascript">script>
11
12
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
61
62
63
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 7a><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 Upcode>/<code>Page Downcode> 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 7a>
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 cubediv>
120
121 body>
122 html>