TeaPot 用webgl画茶壶(3) 环境纹理和skybox
生活随笔
收集整理的這篇文章主要介紹了
TeaPot 用webgl画茶壶(3) 环境纹理和skybox
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
1 <html>
2 <head>
3 <title>TeaPolt</title>
4 </head>
5
6 <body οnlοad="main()">
7 <canvas id="viewPort" width="600" height="600">
8 This browser do not support webgl.
9 </canvas>
10 <script src="./examples/lib/cuon-matrix.js"></script>
11 <script src="./TeaPotData.js"></script>
12 <script src="./cuon-utils.js"></script>
13 <script>
14 /*
15 用webgl實現(xiàn)環(huán)境映射和skybox
16 我翻看了很多書籍,網(wǎng)頁,大多喜歡講理論,理論很簡單,很少有代碼的。要我復(fù)訴一遍理論,我卻講不好。
17 http://ogldev.atspace.co.uk/www/tutorial25/tutorial25.html
18 http://antongerdelan.net/opengl/cubemaps.html
19 這兩個講的就很清楚了。
20
21 跟據(jù)教程實現(xiàn)了代碼,但是也遇到了問題,還沒解決
22
23 我一點總結(jié)
24 1.環(huán)境紋理其實使用紋理著色茶壺,所以有一個紋理就可以了,根據(jù)反射的向量來做紋理坐標(biāo),不需要畫一個cube
25 2.skybox要畫一個cube,這個cube用紋理著色,我站在cube center處,看到啥畫啥。我看的方向跟我看茶壺的一致,但是不能直接用茶壺的viewMatrix,因為這個里面有移動。而這個cube是跟著我移動的,所以相對的要取消移動變換。
26 3.那個教程里的cube圖是往里面折疊的(圖是貼在外面的,雖然在里面也可以看到),所以位置跟webgl用的坐標(biāo)是一致的。
27 */
28 function main()
29 {
30 //alert("bb");
31 //get webgl context
32 var viewPort = document.getElementById("viewPort");
33 var gl = viewPort.getContext("webgl") || viewPort.getContext("experimental-webgl");
34
35 var ENV_VERTEX_SHADER =//畫茶壺
36 "attribute vec4 a_Position;\n" +
37 "attribute vec3 a_VNomal;\n" +
38 "varying vec4 v_Position;\n" +
39 "varying vec3 v_VNomal;\n" +
40 "uniform mat4 u_ModelMatrix;\n" +
41 "uniform mat4 u_ViewMatrix;\n" +
42 "uniform mat4 u_ProjMatrix;\n" +
43 "void main()\n" +
44 "{\n" +
45 " gl_Position = u_ProjMatrix*u_ViewMatrix*u_ModelMatrix*a_Position;\n" +
46 " v_Position = a_Position;\n" +
47 " v_VNomal = a_VNomal;\n" +
48 "}\n"; //光線向量l不是線性插值的,必須在FragmentShader里算,所以每一個Fragment要帶它所對應(yīng)的vertex在空間里的位置(位置是可以插值的)。用著個位置和光源位置來算l。
49
50 var ENV_FRAGMENT_SHADER =//畫茶壺
51 "#ifdef GL_ES\n" +
52 "precision mediump float;\n" +
53 "#endif\n" +
54 "uniform vec3 u_EyePosition;\n" +
55 "uniform vec3 u_pointLightPosition;\n" +
56 "uniform samplerCube u_EnvTexMap;\n" +
57 "uniform mat4 u_VNmodelMatrix;\n" +
58 "varying vec4 v_Position;\n" +
59 "varying vec3 v_VNomal;\n" +
60 "void main()\n" +
61 "{\n" +
62 " vec4 pointLight = vec4(1.0, 1.0, 1.0, 1.0);\n" +
63 " vec4 envlight = vec4(0.1, 0.1, 0.1, 1.0);\n" +
64 " float p = 200.0;\n" +
65 " vec3 l = normalize(vec3(u_pointLightPosition.x - v_Position.x, u_pointLightPosition.y - v_Position.y, u_pointLightPosition.z - v_Position.z));\n" +
66 " vec3 e = normalize(vec3(u_EyePosition.x - v_Position.x, u_EyePosition.y - v_Position.y, u_EyePosition.z - v_Position.z));\n" +
67 " vec3 n = (u_VNmodelMatrix*vec4(v_VNomal, 1.0)).xyz;\n" +
68 " n = normalize(n);\n" +
69 " float nl = dot(n, l);\n" +
70 " if(nl<0.0) nl=0.0;\n" +
71 " vec3 h = normalize(e+l);\n" +
72 " float hn = dot(h, n);\n" +
73 " vec4 phongColor = envlight+pointLight*nl*0.5+pointLight*pow(hn,p)*0.5;\n" +
74 " gl_FragColor = textureCube(u_EnvTexMap, -1.0*reflect(e, n))+phongColor;\n" +//要注意,在實現(xiàn)環(huán)境映射時,-1.0* 是因為我的向量方向取得是跟 Fundamentals of Computer Graphics 一書里是一致的,這根webgl的選擇正好相反。就是,e我是等于u_EyePosition-v_Position,而webgl認(rèn)為是v_Position-u_EyePosition
75 " gl_FragColor.w = 1.0;\n" +
76 "}\n";
77
78 var SKY_VERTEX_SHADER =//畫skybox
79 "attribute vec4 a_Position;\n" +
80 "varying vec3 v_SkyCoord;\n" +
81 "uniform mat4 u_ModelMatrix;\n" +
82 "uniform mat4 u_ViewMatrix;\n" +
83 "uniform mat4 u_ProjMatrix;\n" +
84 "void main()\n" +
85 "{\n" +
86 " vec4 p = u_ProjMatrix*u_ViewMatrix*u_ModelMatrix*a_Position;\n" +
87 " gl_Position = p.xyww;\n" +//gl_Position.z被賦值為gl_Position.w的值,webgl在同質(zhì)時標(biāo)準(zhǔn)坐標(biāo)的z(深度值)都變成1.0,在深度測試時它總是輸?shù)?#xff0c;所以不會擋住茶壺
88 //http://antongerdelan.net/opengl/cubemaps.html 卻說,這是一個壞方法,因為這用到了深度值的極限,1.0。我的理解是,實踐里,我們可能遇到,0.9999999,你的那個顯卡硬件設(shè)備里,這個值可能跟1.0的bit值是一樣的,那這時候覆蓋不覆蓋,不穩(wěn)定。
89 //作者提出的方法是,每次畫場景時總是第一個畫skybox,這時候,disable(gl.DEPTH_TEST),這個意思是,不使用深度buffer, 不往它里面寫任何值。
90 //然后enable(gl.DEPTH_TEST),畫茶壺
91 " v_SkyCoord = a_Position.xyz;\n" +
92 "}\n";
93
94 var SKY_FRAGMENT_SHADER =//畫skybox
95 "#ifdef GL_ES\n" +
96 "precision mediump float;\n" +
97 "#endif\n" +
98 "uniform samplerCube u_SkyTexMap;\n" +
99 "varying vec3 v_SkyCoord;\n" +
100 "void main()\n" +
101 "{\n" +
102 " gl_FragColor = +textureCube(u_SkyTexMap, v_SkyCoord);\n" +//這里v_SkyCoord*一個非零數(shù),畫出的圖片一樣,我認(rèn)為textureCube函數(shù)會對v_SkyCoord標(biāo)準(zhǔn)化。
103 "}\n";
104
105 if(!initShaders(gl, ENV_VERTEX_SHADER, ENV_FRAGMENT_SHADER))
106 return;
107 var programEnv = gl.program;//這個program用來畫環(huán)境映射
108
109 if(!initShaders(gl, SKY_VERTEX_SHADER, SKY_FRAGMENT_SHADER))
110 return;
111 var programSky = gl.program;//用來畫skybox
112
113 gl.enable(gl.DEPTH_TEST);//開啟深度測試,WebGL Programming Guide一書竟然沒提及
114 gl.depthFunc(gl.LEQUAL);//指定深度測試的方法,mdn depthFunc有所有的選擇,這個是小于等于已有的值就覆蓋,這是最常見的用法,還有等于的,大于的等等。
115 gl.clearColor(0.0, 0.0, 0.0, 1.0);
116 gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT);
117
118
119 /*
120 void gl.depthFunc(func);
121 Parameters
122
123 func
124 A GLenum specifying the depth comparison function, which sets the conditions under which the pixel will be drawn. The default value is gl.LESS. Possible values are
125 gl.NEVER (never pass)
126 gl.LESS (pass if the incoming value is less than the depth buffer value)
127 gl.EQUAL (pass if the incoming value equals the the depth buffer value)
128 gl.LEQUAL (pass if the incoming value is less than or equal to the depth buffer value)
129 gl.GREATER (pass if the incoming value is greater than the depth buffer value)
130 gl.NOTEQUAL (pass if the incoming value is not equal to the depth buffer value)
131 gl.GEQUAL (pass if the incoming value is greater than or equal to the depth buffer value)
132 gl.ALWAYS (always pass)
133 */
134
135 var urls, targets, imgs;
136 var modelMatrix, viewMatrix, projMatrix, VNmodelMatrix;
137 var eyePosition;
138 eyePosition = new Float32Array([30, 10, 30]);
139 var pointLightPosition = new Float32Array([0.0, 50.0, 50.0]);
140
141 modelMatrix = new Matrix4();//模型矩陣
142 viewMatrix = new Matrix4();//視覺矩陣
143 projMatrix = new Matrix4();//投影矩陣
144 VNmodelMatrix = new Matrix4();//modelMatrix的逆,然后轉(zhuǎn)置。茶壺根據(jù)modelMatrix轉(zhuǎn)變時,VNmodelMatrix轉(zhuǎn)變茶壺vertex normal
145 viewMatrix.setLookAt(eyePosition[0], eyePosition[1], eyePosition[2], 0.0, 0.0, 0.0, 0, 1, 0);
146 projMatrix.setPerspective(30,viewPort.width/viewPort.height,1,100);//30是視角
147 modelMatrix.setRotate(180, 0, 1, 0);
148 VNmodelMatrix.setInverseOf(modelMatrix);
149 VNmodelMatrix.transpose();//這MVP用來畫茶壺
150
151 resource();//這個函數(shù)加載所有資源
152 function resource()
153 {
154 urls = [
155 './teaPotEnvMap/posx.jpg',//Env
156 './teaPotEnvMap/negx.jpg',
157 './teaPotEnvMap/posy.jpg',
158 './teaPotEnvMap/negy.jpg',
159 './teaPotEnvMap/posz.jpg',
160 './teaPotEnvMap/negz.jpg',
161 './teaPotEnvMap/posx.jpg',//Sky
162 './teaPotEnvMap/negx.jpg',
163 './teaPotEnvMap/posy.jpg',
164 './teaPotEnvMap/negy.jpg',
165 './teaPotEnvMap/posz.jpg',
166 './teaPotEnvMap/negz.jpg',
167 ];
168 targets = [
169 gl.TEXTURE_CUBE_MAP_POSITIVE_X,
170 gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
171 gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
172 gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
173 gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
174 gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
175 ];
176
177 var nimgs=0;
178 imgs = new Array(12);
179 for(var i=0;i<12;i++)
180 {//一個img new出來后,定義onload,只是給img對象定義一個函數(shù)屬性,這時不執(zhí)行。
181 //定義src,瀏覽器根據(jù)它加載一張圖片,加載后執(zhí)行onload。
182 imgs[i] = new Image();
183 imgs[i].onload = function()
184 {
185 nimgs++;
186 if(nimgs==12)//圖片全部加載好,兩個紋理,雖然這里是一樣的
187 {
188 envMapping();//畫茶壺
189 skyboxMapping();//畫skybox
190 }
191 }
192 imgs[i].src = urls[i];
193 }
194 }
195
196
197 function envMapping()
198 {
199 gl.useProgram(programEnv);
200
201 var a_Position, u_ModelMatrix, u_ViewMatrix, u_ProjMatrix, a_VNomal, u_EyePosition, u_pointLightPosition, u_EnvTexMap, u_VNmodelMatrix;
202 a_Position = gl.getAttribLocation(programEnv, "a_Position");
203 a_VNomal = gl.getAttribLocation(programEnv, "a_VNomal");
204 u_ModelMatrix = gl.getUniformLocation(programEnv, "u_ModelMatrix");
205 u_ViewMatrix = gl.getUniformLocation(programEnv, "u_ViewMatrix");
206 u_ProjMatrix = gl.getUniformLocation(programEnv, "u_ProjMatrix");
207 u_EyePosition = gl.getUniformLocation(programEnv, "u_EyePosition");
208 u_pointLightPosition = gl.getUniformLocation(programEnv, "u_pointLightPosition");
209 u_EnvTexMap = gl.getUniformLocation(programEnv, "u_EnvTexMap");
210 u_VNmodelMatrix = gl.getUniformLocation(programEnv, "u_VNmodelMatrix");
211 if(a_Position < 0 || a_VNomal < 0 || !u_ModelMatrix || !u_ViewMatrix || !u_ProjMatrix || !u_EyePosition || !u_pointLightPosition || !u_EnvTexMap || !u_VNmodelMatrix)
212 {
213 alert("Failed to get store location from progrom");
214 return;
215 }
216
217 {//在GPU創(chuàng)建緩沖存茶壺的vertex position
218 var teaPotvPropertiesData = gl.createBuffer();
219 gl.bindBuffer(gl.ARRAY_BUFFER, teaPotvPropertiesData); //alert("bb"+teaPotData);
220 gl.bufferData(gl.ARRAY_BUFFER, teaPotData.vertexPositions, gl.STATIC_DRAW);
221 var VFSIZE = teaPotData.vertexPositions.BYTES_PER_ELEMENTS;
222 gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, VFSIZE * 3, VFSIZE * 0 );
223 gl.enableVertexAttribArray(a_Position);
224
225 var teaPotvnPropertiesData = gl.createBuffer();
226 gl.bindBuffer(gl.ARRAY_BUFFER, teaPotvnPropertiesData);
227 gl.bufferData(gl.ARRAY_BUFFER, teaPotData.vertexNormals, gl.STATIC_DRAW);
228 var VNFSIZE = teaPotData.vertexNormals.BYTES_PER_ELEMENT;
229 gl.vertexAttribPointer(a_VNomal, 3, gl.FLOAT, false, VNFSIZE * 3, VNFSIZE * 0);
230 gl.enableVertexAttribArray(a_VNomal);
231
232 //The following code snippet creates a vertex buffer and binds the indices to it
233 teaPotPropertiesIndex = gl.createBuffer();
234 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, teaPotPropertiesIndex);
235 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, teaPotData.indices, gl.STATIC_DRAW);
236 var IINDEX = teaPotData.indices.length;
237 var IFSIZE = teaPotData.indices.BYTES_PER_ELEMENT;//new Uint16Array(indices)
238
239
240 gl.uniform3fv(u_EyePosition, eyePosition);
241 gl.uniform3fv(u_pointLightPosition, pointLightPosition);}
242
243 gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
244 gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
245 gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
246 gl.uniformMatrix4fv(u_VNmodelMatrix, false, VNmodelMatrix.elements);
247 var texture = gl.createTexture();//cub map也是放在紋理緩沖里
248 gl.activeTexture(gl.TEXTURE0);//選擇第一單元,第一號紋理單元變成被選則狀態(tài)
249 gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);//綁定紋理類型,同時綁定狀態(tài)為被選擇的紋理單元,紋理緩沖跟紋理單元關(guān)聯(lián)是因為,shader里的紋理變量的值是紋理單元的標(biāo)號
250 gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
251 for(var j=0;j<6;j++)
252 {
253 gl.texImage2D(targets[j], 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, imgs[j]);//為六個面綁定二維的圖片
254 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
255 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
256 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
257 }
258 gl.uniform1i(u_EnvTexMap, 0);//告訴u_EnvTexMap這個變量,在取紋理值時到0號紋理單元去取。紋理緩沖比如貨船,紋理單元比如港口。這個函數(shù)告訴紋理變量去那個港口下貨,至于下什么由bindTexture決定
259 //alert("IINDEX is "+IINDEX+" IFSIZE is "+IFSIZE);
260 gl.drawElements(gl.TRIANGLES, IINDEX, gl.UNSIGNED_SHORT, IFSIZE * 0);
261 }
262
263 function skyboxMapping()
264 {gl.useProgram(programSky);
265 var a_Position = gl.getAttribLocation(programSky, "a_Position");
266 var u_ModelMatrix = gl.getUniformLocation(programSky, "u_ModelMatrix");
267 var u_ViewMatrix = gl.getUniformLocation(programSky, "u_ViewMatrix");
268 var u_ProjMatrix = gl.getUniformLocation(programSky, "u_ProjMatrix");
269 var u_SkyTexMap = gl.getUniformLocation(programSky, "u_SkyTexMap");
270 if(a_Position<0 || !u_ModelMatrix || !u_ViewMatrix || !u_ProjMatrix || !u_SkyTexMap)
271 {
272 alert("Failed to get store location from progrom");
273 return;
274 }
275 modelMatrix.setIdentity();//e總是在center
276 //viewMatrix.setLookAt(0.0, 0.0, 0.0, 1.0, 1.0, -1.0, 0, 1, 0);
277 //撤銷setLookAt最后一步,因為e總在skybox的center
278 viewMatrix.translate(eyePosition[0], eyePosition[1], eyePosition[2]);
279 projMatrix.setPerspective(120,viewPort.width/viewPort.height,1,100);
280 gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
281 gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
282 gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
283 //理論上正方形的邊長是不影響skybox。因為只要確定了視角,你看到的范圍就那么大。
284 //(1,1,1,1)是邊長為二的cube的v0,(10, 10, 10, 1)是邊長為二十的cube的v0。它們在屏幕空間上是同一個點,而在作為紋理坐標(biāo)(1,1,1),(10, 10, 10)又是一樣的。
285 //實踐里,邊長二十的cube對大概30到150,160的視角的確是這樣,但是對于170,179就會出現(xiàn)奇怪的圖片,而邊長比較小,比如二,當(dāng)視角大于90時,正面的紋理正確顯式其它不顯示。
286 //我是真不知道為什么,希望有人知道能告知。
287 // Create a cube
288 // v6----- v5
289 // /| /|
290 // v1------v0|
291 // | | | |
292 // | |v7---|-|v4
293 // |/ |/
294 // v2------v3
295 /*var vertexSkybox = new Float32Array([
296 // Vertex coordinates and color
297 1.0, 1.0, 1.0, // v0
298 -1.0, 1.0, 1.0, // v1
299 -1.0, -1.0, 1.0, // v2
300 1.0, -1.0, 1.0, // v3
301 1.0, -1.0, -1.0, // v4
302 1.0, 1.0, -1.0, // v5
303 -1.0, 1.0, -1.0, // v6
304 -1.0, -1.0, -1.0, // v7
305 ]);*/
306 var vertexSkybox = new Float32Array([
307 // Vertex coordinates and color
308 10.0, 10.0, 10.0, // v0
309 -10.0, 10.0, 10.0, // v1
310 -10.0, -10.0, 10.0, // v2
311 10.0, -10.0, 10.0, // v3
312 10.0, -10.0, -10.0, // v4
313 10.0, 10.0, -10.0, // v5
314 -10.0, 10.0, -10.0, // v6
315 -10.0, -10.0, -10.0, // v7
316 ]);
317 /*var vertexSkybox = new Float32Array([
318 // Vertex coordinates and color
319 50.0, 50.0, 50.0, // v0
320 -50.0, 50.0, 50.0, // v1
321 -50.0, -50.0, 50.0, // v2
322 50.0, -50.0, 50.0, // v3
323 50.0, -50.0, -50.0, // v4
324 50.0, 50.0, -50.0, // v5
325 -50.0, 50.0, -50.0, // v6
326 -50.0, -50.0, -50.0, // v7
327 ]);*/
328
329 // Indices of the vertices
330 var skyboxIndex = new Uint16Array([
331 0, 1, 2, 0, 2, 3, // front
332 0, 3, 4, 0, 4, 5, // right
333 0, 5, 6, 0, 6, 1, // up
334 1, 6, 7, 1, 7, 2, // left
335 7, 4, 3, 7, 3, 2, // down
336 4, 7, 6, 4, 6, 5 // back
337 ]);
338 var vertexSkyBuffer = gl.createBuffer();
339 if(!vertexSkyBuffer)
340 {
341 alert("Failed to create the buffer object vertexSkyBuffer");
342 return;
343 }
344 gl.bindBuffer(gl.ARRAY_BUFFER, vertexSkyBuffer);
345 gl.bufferData(gl.ARRAY_BUFFER, vertexSkybox, gl.STATIC_DRAW);
346 var skybox_FSIZE = vertexSkybox.BYTES_PER_ELEMENT;
347 gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, skybox_FSIZE * 3, skybox_FSIZE * 0);
348 gl.enableVertexAttribArray(a_Position);var indexSkyboxBuffer = gl.createBuffer();
349 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexSkyboxBuffer);
350 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, skyboxIndex, gl.STATIC_DRAW);
351 skybox_IINDEX = skyboxIndex.length;
352 skybox_IFSIZE = skyboxIndex.BYTES_PER_ELEMENT;
353
354 var texture = gl.createTexture();
355 gl.activeTexture(gl.TEXTURE1);
356 gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
357 //gl.generateMipmap(gl.TEXTURE_CUBE_MAP); 不需要,因為cube離開center距離不變
358 for(var j=0;j<6;j++)
359 {//alert(imgs[j]);
360 gl.texImage2D(targets[j], 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, imgs[j+6]);
361 //gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
362 // Set the texture parameters
363 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
364 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.GL_TEXTURE_MAG_FILTER, gl.LINEAR);
365 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
366 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
367 }
368 gl.uniform1i(u_SkyTexMap, 1);
369 //skybox
370 //gl.clearColor(0.0, 0.0, 0.0, 1.0);
371 //gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT);
372 //alert("IINDEX is "+IINDEX+" IFSIZE is "+IFSIZE);
373 gl.drawElements(gl.TRIANGLES, skybox_IINDEX, gl.UNSIGNED_SHORT, skybox_IFSIZE * 0);
374 }
375 }
376
377 </script>
378
379 </body>
380 </html>
?
轉(zhuǎn)載于:https://www.cnblogs.com/javascript3d/p/7212288.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的TeaPot 用webgl画茶壶(3) 环境纹理和skybox的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ChartCtrl源码剖析之——CCha
- 下一篇: 高通WLAN芯片点灯方法-QCA9563