Простейшая 3D программа
Материал из Department of Theoretical and Applied Mechanics
Виртуальная лаборатория > Простейшая 3D программа
Здесь расположена простейшая 3D программа, написанная на чистом WebGL.
Чтобы работало управление, нужно щелкнуть окно программы.
Скачать WebGL_Example_v4.zip
Текст программы на языке JavaScript (разработчик Цветков Денис):
Файл "WebGL_Example_v4.js"
1 // Куб на чистом WebGL
2 // Версия 4.0 от 24.08.2014
3
4 function Main_WebGL_example() {
5 var gl;
6
7 function initGL(canvas) {
8 try {
9 gl = canvas.getContext("experimental-webgl");
10 gl.viewportWidth = canvas.width;
11 gl.viewportHeight = canvas.height;
12 } catch (e) {
13 }
14 if (!gl) {
15 alert("Could not initialise WebGL, sorry :-(");
16 }
17 }
18
19 function getShader(gl, id) {
20 var shaderScript = document.getElementById(id);
21 if (!shaderScript) {
22 return null;
23 }
24
25 var str = "";
26 var k = shaderScript.firstChild;
27 while (k) {
28 if (k.nodeType == 3) {
29 str += k.textContent;
30 }
31 k = k.nextSibling;
32 }
33
34 var shader;
35 if (shaderScript.type == "x-shader/x-fragment") {
36 shader = gl.createShader(gl.FRAGMENT_SHADER);
37 } else if (shaderScript.type == "x-shader/x-vertex") {
38 shader = gl.createShader(gl.VERTEX_SHADER);
39 } else {
40 return null;
41 }
42
43 gl.shaderSource(shader, str);
44 gl.compileShader(shader);
45
46 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
47 alert(gl.getShaderInfoLog(shader));
48 return null;
49 }
50
51 return shader;
52 }
53
54
55 var shaderProgram;
56
57 function initShaders() {
58 var fragmentShader = getShader(gl, "shader-fs");
59 var vertexShader = getShader(gl, "shader-vs");
60
61 shaderProgram = gl.createProgram();
62 gl.attachShader(shaderProgram, vertexShader);
63 gl.attachShader(shaderProgram, fragmentShader);
64 gl.linkProgram(shaderProgram);
65
66 if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
67 alert("Could not initialise shaders");
68 }
69
70 gl.useProgram(shaderProgram);
71
72 shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
73 gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
74
75 shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
76 gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
77
78 shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
79 shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
80 shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
81 }
82
83
84 function handleLoadedTexture(textures) {
85 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
86
87 gl.bindTexture(gl.TEXTURE_2D, textures[0]);
88 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[0].image);
89 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
90 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
91
92 gl.bindTexture(gl.TEXTURE_2D, textures[1]);
93 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[1].image);
94 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
95 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
96
97 gl.bindTexture(gl.TEXTURE_2D, textures[2]);
98 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[2].image);
99 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
100 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
101 gl.generateMipmap(gl.TEXTURE_2D);
102
103 gl.bindTexture(gl.TEXTURE_2D, null);
104 }
105
106
107 var crateTextures = Array();
108
109 function initTexture() {
110 var crateImage = new Image();
111
112 for (var i=0; i < 3; i++) {
113 var texture = gl.createTexture();
114 texture.image = crateImage;
115 crateTextures.push(texture);
116 }
117
118 crateImage.onload = function () {
119 handleLoadedTexture(crateTextures)
120 }
121 crateImage.src = "teormeh.png";
122 }
123
124
125 var mvMatrix = mat4.create();
126 var mvMatrixStack = [];
127 var pMatrix = mat4.create();
128
129 //function mvPushMatrix() {
130 // var copy = mat4.create();
131 // mat4.set(mvMatrix, copy);
132 // mvMatrixStack.push(copy);
133 //}
134 //
135 //function mvPopMatrix() {
136 // if (mvMatrixStack.length == 0) {
137 // throw "Invalid popMatrix!";
138 // }
139 // mvMatrix = mvMatrixStack.pop();
140 //}
141
142
143 function setMatrixUniforms() {
144 gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
145 gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
146 }
147
148
149 function degToRad(degrees) {
150 return degrees * Math.PI / 180;
151 }
152
153
154 var xRot = 0;
155 var xSpeed = 0;
156
157 var yRot = 0;
158 var ySpeed = 0;
159
160 var z = -4.0;
161
162 var filter = 1;
163
164
165 var currentlyPressedKeys = {};
166
167 function handleKeyDown(event) {
168 currentlyPressedKeys[event.keyCode] = true;
169
170 if (String.fromCharCode(event.keyCode) == "F") {
171 filter += 1;
172 if (filter == 3) {
173 filter = 0;
174 }
175 }
176 if (event.keyCode == "36") { // TODO удалить
177 mat4.identity(rotate_buf);
178 }
179 }
180
181
182 function handleKeyUp(event) {
183 currentlyPressedKeys[event.keyCode] = false;
184 }
185
186
187 function handleKeys() {
188 if (currentlyPressedKeys[33]) {z += 0.05;} // Page Up
189 if (currentlyPressedKeys[34]) {z -= 0.05;} // Page Down
190 if (currentlyPressedKeys[37]) {ySpeed -= 2;} // Влево
191 if (currentlyPressedKeys[39]) {ySpeed += 2;} // Вправо
192 if (currentlyPressedKeys[38]) {xSpeed -= 2;} // Вверх
193 if (currentlyPressedKeys[40]) {xSpeed += 2;} // Вниз
194 }
195
196
197 var cubeVertexPositionBuffer;
198 var cubeVertexTextureCoordBuffer;
199 var cubeVertexIndexBuffer;
200 function initBuffers() {
201 cubeVertexPositionBuffer = gl.createBuffer();
202 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
203 vertices = [
204 // Front face
205 -1.0, -1.0, 1.0,
206 1.0, -1.0, 1.0,
207 1.0, 1.0, 1.0,
208 -1.0, 1.0, 1.0,
209
210 // Back face
211 -1.0, -1.0, -1.0,
212 -1.0, 1.0, -1.0,
213 1.0, 1.0, -1.0,
214 1.0, -1.0, -1.0,
215
216 // Top face
217 -1.0, 1.0, -1.0,
218 -1.0, 1.0, 1.0,
219 1.0, 1.0, 1.0,
220 1.0, 1.0, -1.0,
221
222 // Bottom face
223 -1.0, -1.0, -1.0,
224 1.0, -1.0, -1.0,
225 1.0, -1.0, 1.0,
226 -1.0, -1.0, 1.0,
227
228 // Right face
229 1.0, -1.0, -1.0,
230 1.0, 1.0, -1.0,
231 1.0, 1.0, 1.0,
232 1.0, -1.0, 1.0,
233
234 // Left face
235 -1.0, -1.0, -1.0,
236 -1.0, -1.0, 1.0,
237 -1.0, 1.0, 1.0,
238 -1.0, 1.0, -1.0,
239 ];
240 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
241 cubeVertexPositionBuffer.itemSize = 3;
242 cubeVertexPositionBuffer.numItems = 24;
243
244 cubeVertexTextureCoordBuffer = gl.createBuffer();
245 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
246 var textureCoords = [
247 // Front face
248 0.0, 0.0,
249 1.0, 0.0,
250 1.0, 1.0,
251 0.0, 1.0,
252
253 // Back face
254 1.0, 0.0,
255 1.0, 1.0,
256 0.0, 1.0,
257 0.0, 0.0,
258
259 // Top face
260 0.0, 1.0,
261 0.0, 0.0,
262 1.0, 0.0,
263 1.0, 1.0,
264
265 // Bottom face
266 1.0, 1.0,
267 0.0, 1.0,
268 0.0, 0.0,
269 1.0, 0.0,
270
271 // Right face
272 1.0, 0.0,
273 1.0, 1.0,
274 0.0, 1.0,
275 0.0, 0.0,
276
277 // Left face
278 0.0, 0.0,
279 1.0, 0.0,
280 1.0, 1.0,
281 0.0, 1.0,
282 ];
283 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
284 cubeVertexTextureCoordBuffer.itemSize = 2;
285 cubeVertexTextureCoordBuffer.numItems = 24;
286
287 cubeVertexIndexBuffer = gl.createBuffer();
288 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
289 var cubeVertexIndices = [
290 0, 1, 2, 0, 2, 3, // Front face
291 4, 5, 6, 4, 6, 7, // Back face
292 8, 9, 10, 8, 10, 11, // Top face
293 12, 13, 14, 12, 14, 15, // Bottom face
294 16, 17, 18, 16, 18, 19, // Right face
295 20, 21, 22, 20, 22, 23 // Left face
296 ]
297 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
298 cubeVertexIndexBuffer.itemSize = 1;
299 cubeVertexIndexBuffer.numItems = 36;
300 }
301
302 var rotate_buf = mat4.create();
303 function drawScene() {
304 gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
305 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
306
307 mat4.perspective(pMatrix, 45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
308
309 mat4.identity(mvMatrix);
310
311
312 mat4.translate(mvMatrix, mvMatrix, [0, 0, z]);
313
314 var new_rotate = mat4.create();
315
316 mat4.rotateX(new_rotate, new_rotate, degToRad(xRot));
317 mat4.rotateY(new_rotate, new_rotate, degToRad(yRot));
318
319
320 mat4.multiply(rotate_buf, new_rotate, rotate_buf);
321 mat4.multiply(mvMatrix, mvMatrix, rotate_buf);
322
323
324 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
325 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
326
327 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
328 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
329
330
331 gl.activeTexture(gl.TEXTURE0);
332 gl.bindTexture(gl.TEXTURE_2D, crateTextures[filter]);
333 gl.uniform1i(shaderProgram.samplerUniform, 0);
334
335 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
336 setMatrixUniforms();
337 gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
338 }
339
340 function animate() {
341 xRot = xSpeed;
342 yRot = ySpeed;
343 xSpeed = ySpeed = 0;
344 }
345
346 function tick() {
347 requestAnimFrame(tick);
348 handleKeys();
349 drawScene();
350 animate();
351 }
352
353 function webGLStart() {
354 var canvas = document.getElementById("WebGL_canvas");
355 initGL(canvas);
356 initShaders();
357 initBuffers();
358 initTexture();
359
360 gl.clearColor(0.95, 0.95, 1.0, 1.0);
361 gl.enable(gl.DEPTH_TEST);
362
363 document.onkeydown = handleKeyDown;
364 document.onkeyup = handleKeyUp;
365
366 tick();
367 }
368
369 // Запуск программы
370 webGLStart();
371 }
Файл "WebGL_Example_v4.html"
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8" />
5 <title>WebGL Example</title>
6
7 <!--<script type="text/javascript" src="glMatrix-0.9.5.min.js"></script>-->
8 <script type="text/javascript" src="gl-matrix.js"></script>
9 <script type="text/javascript" src="webgl-utils.js"></script>
10 <script id="shader-fs" type="x-shader/x-fragment">
11 precision mediump float;
12
13 varying vec2 vTextureCoord;
14
15 uniform sampler2D uSampler;
16
17 void main(void) {
18 gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
19 }
20 </script>
21 <script id="shader-vs" type="x-shader/x-vertex">
22 attribute vec3 aVertexPosition;
23 attribute vec2 aTextureCoord;
24
25 uniform mat4 uMVMatrix;
26 uniform mat4 uPMatrix;
27
28 varying vec2 vTextureCoord;
29
30
31 void main(void) {
32 gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
33 vTextureCoord = aTextureCoord;
34 }
35 </script>
36 <script src="WebGL_Example_v4.js"></script>
37 </head>
38
39 <body onload="Main_WebGL_example();">
40 <canvas id="WebGL_canvas" style="border: none;" width="500" height="500"></canvas>
41
42 <ul>
43 <li>Стрелочки - поворот куба
44 <li><code>[Home]</code> - вернуть куб в исходное положение
45 <li><code>[Page Up]</code>/<code>[Page Down]</code> - приближение/отдаление
46 </ul>
47 </body>
48
49 </html>