Простейшая 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>