本篇内容主要讲解“如何在HTML5的浏览器上运行WebGL程序”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何在HTML5的浏览器上运行WebGL程序”吧! 前提条件和预期结果 目前只有少数的浏览器支持 WebGL ,请看我的另外一篇文章:Can I use WebGL?. 下面的例子是在 Windows 下的 Chrome 16/23 以及 Android 下的 Firefox 17 进行测试。如果你使用的是非兼容浏览器访问则会弹出一个警告。
 图1:包含 Hello world 文本的动画的 WebGL 立方体 在兼容 HTML5 的浏览器上,你将会看到如下图所示的带动画效果的立方体:
 图2: 示例运行的屏幕截图 该代码基于 Lighting in WebGL - How to simulate lighting effects in your WebGL context - 非常感谢这篇教程。在该实例初始运行时,动画的立方体是通过一个静态的 Bitmap 图形对象渲染的。
下面的代码演示如何在程序中动态的渲染文本: XML/HTML Code复制内容到剪贴板 // TODO #1 New method to create a texture function createCubeTexture(text) { ... }
在这里使用 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 是非常重要的,用来确保写文本时不会前后颠倒。剩下的就很容易理解了: XML/HTML Code复制内容到剪贴板 // TODO #2 Assign the created texture for display cubeTexture = createCubeTexture("Hello World!");
源码 // File #1: webgl-demo.htm XML/HTML Code复制内容到剪贴板 <html> <head> <title>WebGL - Hello World!</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="sylvester.js" type="text/javascript"></script> <script src="glUtils.js" type="text/javascript"></script> <script src="webgl-demo.js" type="text/javascript"></script> <script id="shader-fs" type="x-shader/x-fragment"> varying highp vec2 vTextureCoord; varying highp vec3 vLighting; uniform sampler2D uSampler; void main(void) { highp vec4 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a); } </script> <script id="shader-vs" type="x-shader/x-vertex"> attribute highp vec3 aVertexNormal; attribute highp vec3 aVertexPosition; attribute highp vec2 aTextureCoord; uniform highp mat4 uNormalMatrix; uniform highp mat4 uMVMatrix; uniform highp mat4 uPMatrix; varying highp vec2 vTextureCoord; varying highp vec3 vLighting; void main(void) { gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); vTextureCoord = aTextureCoord; // Apply lighting effect highp vec3 ambientLight = vec3(0.6, 0.6, 0.6); highp vec3 directionalLightColor = vec3(0.5, 0.5, 0.75); highp vec3 directionalVector = vec3(0.85, 0.8, 0.75); highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); vLighting = ambientLight + (directionalLightColor * directional); } </script> </head> <body onload="start()"> <canvas id="glcanvas" width="640" height="480"> Your browser doesn't appear to support the HTML5 <code><canvas></code> element. </canvas> </body> </html>
// File #02: webgl-demo.js
XML/HTML Code复制内容到剪贴板 var canvas; var gl; var cubeVerticesBuffer; var cubeVerticesTextureCoordBuffer; var cubeVerticesIndexBuffer; var cubeVerticesIndexBuffer; var cubeRotation = 0.0; var lastCubeUpdateTime = 0; var cubeImage; var cubeTexture; var mvMatrix; var shaderProgram; var vertexPositionAttribute; var vertexNormalAttribute; var textureCoordAttribute; var perspectiveMatrix; // // start // // Called when the canvas is created to get the ball rolling. // function start() { canvas = document.getElementById("glcanvas"); initWebGL(canvas); // Initialize the GL context // Only continue if WebGL is available and working if (gl) { gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque gl.clearDepth(1.0); // Clear everything gl.enable(gl.DEPTH_TEST); // Enable depth testing gl.depthFunc(gl.LEQUAL); // Near things obscure far things // Initialize the shaders; this is where all the lighting for the // vertices and so forth is established. initShaders(); // Here's where we call the routine that builds all the objects // we'll be drawing. initBuffers(); // Next, load and set up the textures we'll be using. // TODO#2 Start cubeTexture = createCubeTexture("Hello World!"); // TODO#2 End // Set up to draw the scene periodically. setInterval(drawScene, 15); } } // // initWebGL // // Initialize WebGL, returning the GL context or null if // WebGL isn't available or could not be initialized. // function initWebGL() { gl = null; try { gl = canvas.getContext("experimental-webgl"); } catch(e) { } // If we don't have a GL context, give up now if (!gl) { alert("Unable to initialize WebGL. Your browser may not support it."); } } // // initBuffers // // Initialize the buffers we'll need. For this demo, we just have // one object -- a simple two-dimensional cube. // function initBuffers() { // Create a buffer for the cube's vertices. cubeVerticesBuffer = gl.createBuffer(); // Select the cubeVerticesBuffer as the one to apply vertex // operations to from here out. gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer); // Now create an array of vertices for the cube. var 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 ]; // Now pass the list of vertices into WebGL to build the shape. We // do this by creating a Float32Array from the JavaScript array, // then use it to fill the current vertex buffer. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Set up the normals for the vertices, so that we can compute lighting. cubeVerticesNormalBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer); var vertexNormals = [ // Front 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // Back 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, // Top 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // Bottom 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // Right 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // Left -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), gl.STATIC_DRAW); // Map the texture onto the cube's faces. cubeVerticesTextureCoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer); var textureCoordinates = [ // Front 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // Back 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // Top 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // Bottom 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // Right 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // Left 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), gl.STATIC_DRAW); // Build the element array buffer; this specifies the indices // into the vertex array for each face's vertices. cubeVerticesIndexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer); // This array defines each face as two triangles, using the // indices into the vertex array to specify each triangle's // position. var cubeVertexIndices = [ 0, 1, 2, 0, 2, 3, // front 4, 5, 6, 4, 6, 7, // back 8, 9, 10, 8, 10, 11, // top 12, 13, 14, 12, 14, 15, // bottom 16, 17, 18, 16, 18, 19, // right 20, 21, 22, 20, 22, 23 // left ] // Now send the element array to GL gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW); } // // initTextures // // Initialize the textures we'll be using, then initiate a load of // the texture images. The handleTextureLoaded() callback will finish // the job; it gets called each time a texture finishes loading. // // TODO#1 Start function createCubeTexture(text) { // create a hidden canvas to draw the texture var canvas = document.createElement('canvas'); canvas.id = "hiddenCanvas"; canvas.width = 512; canvas.height = 512; canvas.style.display = "none"; var body = document.getElementsByTagName("body")[0]; body.appendChild(canvas); // draw texture var cubeImage = document.getElementById('hiddenCanvas'); var ctx = cubeImage.getContext('2d'); ctx.beginPath(); ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.fillStyle = 'white'; ctx.fill(); ctx.fillStyle = 'black'; ctx.font = "65px Arial"; ctx.textAlign = 'center'; ctx.fillText(text, ctx.canvas.width / 2, ctx.canvas.height / 2); ctx.restore(); // create new texture var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); 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.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); handleTextureLoaded(cubeImage, texture) return texture; } // TODO#1 End function handleTextureLoaded(image, texture) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, 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); } // // drawScene // // Draw the scene. // function drawScene() { // Clear the canvas before we start drawing on it. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Establish the perspective with which we want to view the // scene. Our field of view is 45 degrees, with a width/height // ratio of 640:480, and we only want to see objects between 0.1 units // and 100 units away from the camera. perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0); // Set the drawing position to the "identity" point, which is // the center of the scene. loadIdentity(); // Now move the drawing position a bit to where we want to start // drawing the cube. mvTranslate([0.0, 0.0, -6.0]); // Save the current matrix, then rotate before we draw. mvPushMatrix(); mvRotate(cubeRotation, [1, 0, 1]); // Draw the cube by binding the array buffer to the cube's vertices // array, setting attributes, and pushing it to GL. gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer); gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); // Set the texture coordinates attribute for the vertices. gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer); gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0); // Bind the normals buffer to the shader attribute. gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer); gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0); // Specify the texture to map onto the faces. gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, cubeTexture); gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0); // Draw the cube. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer); setMatrixUniforms(); gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0); // Restore the original matrix mvPopMatrix(); // Update the rotation for the next draw, if it's time to do so. var currentTime = (new Date).getTime(); if (lastCubeUpdateTime) { var delta = currentTime - lastCubeUpdateTime; cubeRotation += (30 * delta) / 1000.0; } lastCubeUpdateTime = currentTime; } // // initShaders // // Initialize the shaders, so WebGL knows how to light our scene. // function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs"); // Create the shader program shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); // If creating the shader program failed, alert if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Unable to initialize the shader program."); } gl.useProgram(shaderProgram); vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(vertexPositionAttribute); textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); gl.enableVertexAttribArray(textureCoordAttribute); vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal"); gl.enableVertexAttribArray(vertexNormalAttribute); } // // getShader // // Loads a shader program by scouring the current document, // looking for a script with the specified ID. // function getShader(gl, id) { var shaderScript = document.getElementById(id); // Didn't find an element with the specified ID; abort. if (!shaderScript) { return null; } // Walk through the source element's children, building the // shader source string. var theSource = ""; var currentChild = shaderScript.firstChild; while(currentChild) { if (currentChild.nodeType == 3) { theSource += currentChild.textContent; } currentChildcurrentChild = currentChild.nextSibling; } // Now figure out what type of shader script we have, // based on its MIME type. 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; // Unknown shader type } // Send the source to the shader object gl.shaderSource(shader, theSource); // Compile the shader program gl.compileShader(shader); // See if it compiled successfully if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)); return null; } return shader; } // // Matrix utility functions // function loadIdentity() { mvMatrix = Matrix.I(4); } function multMatrix(m) { mvMatrixmvMatrix = mvMatrix.x(m); } function mvTranslate(v) { multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4()); } function setMatrixUniforms() { var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten())); var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten())); var normalMatrix = mvMatrix.inverse(); normalMatrixnormalMatrix = normalMatrix.transpose(); var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix"); gl.uniformMatrix4fv(nUniform, false, new Float32Array(normalMatrix.flatten())); } var mvMatrixStack = []; function mvPushMatrix(m) { if (m) { mvMatrixStack.push(m.dup()); mmvMatrix = m.dup(); } else { mvMatrixStack.push(mvMatrix.dup()); } } function mvPopMatrix() { if (!mvMatrixStack.length) { throw("Can't pop from an empty matrix stack."); } mvMatrix = mvMatrixStack.pop(); return mvMatrix; } function mvRotate(angle, v) { var inRadians = angle * Math.PI / 180.0; var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4(); multMatrix(m); }
到此,相信大家对“如何在HTML5的浏览器上运行WebGL程序”有了更深的了解,不妨来实际操作一番吧!这里是天达云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
|