Простейшая 3D программа
Здесь расположена простейшая 3D программа, написанная на чистом WebGL.
Чтобы работало управление, нужно щелкнуть окно программы.
Скачать WebGL_Example_v4.zip
Текст программы на языке JavaScript (разработчик Цветков Денис): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default"> Файл "WebGL_Example_v4.js" <syntaxhighlight lang="javascript" line start="1" enclose="div"> // Куб на чистом WebGL // Версия 4.0 от 24.08.2014
function Main_WebGL_example() {
var gl;
function initGL(canvas) { try { gl = canvas.getContext("experimental-webgl"); gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; } catch (e) { } if (!gl) { alert("Could not initialise WebGL, sorry :-("); } }
function getShader(gl, id) { var shaderScript = document.getElementById(id); if (!shaderScript) { return null; }
var str = ""; var k = shaderScript.firstChild; while (k) { if (k.nodeType == 3) { str += k.textContent; } k = k.nextSibling; }
var shader; if (shaderScript.type == "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (shaderScript.type == "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; }
gl.shaderSource(shader, str); gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader)); return null; }
return shader; }
var shaderProgram;
function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Could not initialise shaders"); }
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); }
function handleLoadedTexture(textures) { gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.bindTexture(gl.TEXTURE_2D, textures[0]); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[0].image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, textures[1]); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[1].image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.bindTexture(gl.TEXTURE_2D, textures[2]); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[2].image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(gl.TEXTURE_2D, null); }
var crateTextures = Array();
function initTexture() { var crateImage = new Image();
for (var i=0; i < 3; i++) { var texture = gl.createTexture(); texture.image = crateImage; crateTextures.push(texture); }
crateImage.onload = function () { handleLoadedTexture(crateTextures) } crateImage.src = "teormeh.png"; }
var mvMatrix = mat4.create(); var mvMatrixStack = []; var pMatrix = mat4.create();
//function mvPushMatrix() { // var copy = mat4.create(); // mat4.set(mvMatrix, copy); // mvMatrixStack.push(copy); //} // //function mvPopMatrix() { // if (mvMatrixStack.length == 0) { // throw "Invalid popMatrix!"; // } // mvMatrix = mvMatrixStack.pop(); //}
function setMatrixUniforms() { gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix); gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix); }
function degToRad(degrees) { return degrees * Math.PI / 180; }
var xRot = 0; var xSpeed = 0;
var yRot = 0; var ySpeed = 0;
var z = -4.0;
var filter = 1;
var currentlyPressedKeys = {};
function handleKeyDown(event) { currentlyPressedKeys[event.keyCode] = true;
if (String.fromCharCode(event.keyCode) == "F") { filter += 1; if (filter == 3) { filter = 0; } } if (event.keyCode == "36") { // TODO удалить mat4.identity(rotate_buf); } }
function handleKeyUp(event) { currentlyPressedKeys[event.keyCode] = false; }
function handleKeys() { if (currentlyPressedKeys[33]) {z += 0.05;} // Page Up if (currentlyPressedKeys[34]) {z -= 0.05;} // Page Down if (currentlyPressedKeys[37]) {ySpeed -= 2;} // Влево if (currentlyPressedKeys[39]) {ySpeed += 2;} // Вправо if (currentlyPressedKeys[38]) {xSpeed -= 2;} // Вверх if (currentlyPressedKeys[40]) {xSpeed += 2;} // Вниз }
var cubeVertexPositionBuffer; var cubeVertexTextureCoordBuffer; var cubeVertexIndexBuffer; function initBuffers() { cubeVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer); vertices = [ // Front face -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
// Back face -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,
// Top face -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
// Bottom face -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
// Right face 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
// Left face -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); cubeVertexPositionBuffer.itemSize = 3; cubeVertexPositionBuffer.numItems = 24;
cubeVertexTextureCoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer); var textureCoords = [ // Front face 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
// Back face 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0,
// Top face 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0,
// Bottom face 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
// Right face 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0,
// Left face 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW); cubeVertexTextureCoordBuffer.itemSize = 2; cubeVertexTextureCoordBuffer.numItems = 24;
cubeVertexIndexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer); var cubeVertexIndices = [ 0, 1, 2, 0, 2, 3, // Front face 4, 5, 6, 4, 6, 7, // Back face 8, 9, 10, 8, 10, 11, // Top face 12, 13, 14, 12, 14, 15, // Bottom face 16, 17, 18, 16, 18, 19, // Right face 20, 21, 22, 20, 22, 23 // Left face ] gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW); cubeVertexIndexBuffer.itemSize = 1; cubeVertexIndexBuffer.numItems = 36; }
var rotate_buf = mat4.create(); function drawScene() { gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(pMatrix, 45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, mvMatrix, [0, 0, z]);
var new_rotate = mat4.create();
mat4.rotateX(new_rotate, new_rotate, degToRad(xRot)); mat4.rotateY(new_rotate, new_rotate, degToRad(yRot));
mat4.multiply(rotate_buf, new_rotate, rotate_buf); mat4.multiply(mvMatrix, mvMatrix, rotate_buf);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer); gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, crateTextures[filter]); gl.uniform1i(shaderProgram.samplerUniform, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer); setMatrixUniforms(); gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); }
function animate() { xRot = xSpeed; yRot = ySpeed; xSpeed = ySpeed = 0; }
function tick() { requestAnimFrame(tick); handleKeys(); drawScene(); animate(); }
function webGLStart() { var canvas = document.getElementById("WebGL_canvas"); initGL(canvas); initShaders(); initBuffers(); initTexture();
gl.clearColor(0.95, 0.95, 1.0, 1.0); gl.enable(gl.DEPTH_TEST);
document.onkeydown = handleKeyDown; document.onkeyup = handleKeyUp;
tick(); }
// Запуск программы webGLStart();
} </syntaxhighligh> Файл "WebGL_Example_v4.html" <syntaxhighlight lang="html" line start="1" enclose="div"> <!DOCTYPE html> <html> <head>
<meta charset="UTF-8" /> <title>WebGL Example</title>
<script type="text/javascript" src="gl-matrix.js"></script> <script type="text/javascript" src="webgl-utils.js"></script> <script id="shader-fs" type="x-shader/x-fragment"> precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) { gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); } </script> <script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aVertexPosition; attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix; uniform mat4 uPMatrix;
varying vec2 vTextureCoord;
void main(void) { gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); vTextureCoord = aTextureCoord; } </script> <script src="WebGL_Example_v4.js"></script>
</head>
<body onload="Main_WebGL_example();"> <canvas id="WebGL_canvas" style="border: none;" width="500" height="500"></canvas>
- Стрелочки - поворот куба
[Home]
- вернуть куб в исходное положение[Page Up]
/[Page Down]
- приближение/отдаление
</body>
</html> </syntaxhighligh> </toggledisplay>