在支持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复制内容到剪贴板
  1. // TODO #1 New method to create a texture   
  2. function createCubeTexture(text) {   
  3.     ...   
  4. }  

在这里使用 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 是非常重要的,用来确保写文本时不会前后颠倒。剩下的就很容易理解了:

XML/HTML Code复制内容到剪贴板
  1. // TODO #2 Assign the created texture for display   
  2. cubeTexture = createCubeTexture("Hello World!");  

源码

// File #1: webgl-demo.htm

XML/HTML Code复制内容到剪贴板
  1. <html>  
  2.   <head>  
  3.     <title>WebGL - Hello World!</title>  
  4.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">  
  5.     <script src="sylvester.js" type="text/javascript"></script>  
  6.     <script src="glUtils.js" type="text/javascript"></script>  
  7.     <script src="webgl-demo.js" type="text/javascript"></script>  
  8.         
  9.     <!-- Fragment shader program -->  
  10.     <script id="shader-fs" type="x-shader/x-fragment">  
  11.       varying highp vec2 vTextureCoord;   
  12.       varying highp vec3 vLighting;   
  13.           
  14.       uniform sampler2D uSampler;   
  15.           
  16.       void main(void) {   
  17.         highp vec4 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));   
  18.             
  19.         gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);   
  20.       }   
  21.     </script>  
  22.         
  23.     <!-- Vertex shader program -->  
  24.     <script id="shader-vs" type="x-shader/x-vertex">  
  25.       attribute highp vec3 aVertexNormal;   
  26.       attribute highp vec3 aVertexPosition;   
  27.       attribute highp vec2 aTextureCoord;   
  28.         
  29.       uniform highp mat4 uNormalMatrix;   
  30.       uniform highp mat4 uMVMatrix;   
  31.       uniform highp mat4 uPMatrix;   
  32.           
  33.       varying highp vec2 vTextureCoord;   
  34.       varying highp vec3 vLighting;   
  35.         
  36.       void main(void) {   
  37.         gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);   
  38.         vTextureCoord = aTextureCoord;   
  39.             
  40.         // Apply lighting effect   
  41.             
  42.         highp vec3 ambientLight = vec3(0.6, 0.6, 0.6);   
  43.         highp vec3 directionalLightColor = vec3(0.5, 0.5, 0.75);   
  44.         highp vec3 directionalVector = vec3(0.85, 0.8, 0.75);   
  45.         highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);   
  46.        
  47.         highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);   
  48.         vLighting = ambientLight + (directionalLightColor * directional);   
  49.       }   
  50.     </script>  
  51.   </head>        
  52.       
  53.   <body onload="start()">  
  54.     <canvas id="glcanvas" width="640" height="480">  
  55.       Your browser doesn't appear to support the HTML5 <code><canvas></code> element.   
  56.     </canvas>  
  57.   </body>  
  58. </html>  

// File #02: webgl-demo.js

XML/HTML Code复制内容到剪贴板
  1. var canvas;   
  2. var gl;   
  3.     
  4. var cubeVerticesBuffer;   
  5. var cubeVerticesTextureCoordBuffer;   
  6. var cubeVerticesIndexBuffer;   
  7. var cubeVerticesIndexBuffer;   
  8. var cubeRotation = 0.0;   
  9. var lastCubeUpdateTime = 0;   
  10.     
  11. var cubeImage;   
  12. var cubeTexture;   
  13.     
  14. var mvMatrix;   
  15. var shaderProgram;   
  16. var vertexPositionAttribute;   
  17. var vertexNormalAttribute;   
  18. var textureCoordAttribute;   
  19. var perspectiveMatrix;   
  20.     
  21. //   
  22. // start   
  23. //   
  24. // Called when the canvas is created to get the ball rolling.   
  25. //   
  26. function start() {   
  27.   canvas = document.getElementById("glcanvas");   
  28.     
  29.   initWebGL(canvas);      // Initialize the GL context   
  30.       
  31.   // Only continue if WebGL is available and working   
  32.       
  33.   if (gl) {   
  34.     gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque   
  35.     gl.clearDepth(1.0);                 // Clear everything   
  36.     gl.enable(gl.DEPTH_TEST);           // Enable depth testing   
  37.     gl.depthFunc(gl.LEQUAL);            // Near things obscure far things   
  38.         
  39.     // Initialize the shaders; this is where all the lighting for the   
  40.     // vertices and so forth is established.   
  41.         
  42.     initShaders();   
  43.         
  44.     // Here's where we call the routine that builds all the objects   
  45.     // we'll be drawing.   
  46.         
  47.     initBuffers();   
  48.         
  49.     // Next, load and set up the textures we'll be using.   
  50.         
  51.     // TODO#2 Start   
  52.     cubeTexture = createCubeTexture("Hello World!");   
  53.     // TODO#2 End   
  54.         
  55.     // Set up to draw the scene periodically.   
  56.         
  57.     setInterval(drawScene, 15);   
  58.   }   
  59. }   
  60.     
  61. //   
  62. // initWebGL   
  63. //   
  64. // Initialize WebGL, returning the GL context or null if   
  65. // WebGL isn't available or could not be initialized.   
  66. //   
  67. function initWebGL() {   
  68.   gl = null;   
  69.       
  70.   try {   
  71.     gl = canvas.getContext("experimental-webgl");   
  72.   }   
  73.   catch(e) {   
  74.   }   
  75.       
  76.   // If we don't have a GL context, give up now   
  77.       
  78.   if (!gl) {   
  79.     alert("Unable to initialize WebGL. Your browser may not support it.");   
  80.   }   
  81. }   
  82.     
  83. //   
  84. // initBuffers   
  85. //   
  86. // Initialize the buffers we'll need. For this demo, we just have   
  87. // one object -- a simple two-dimensional cube.   
  88. //   
  89. function initBuffers() {   
  90.       
  91.   // Create a buffer for the cube's vertices.   
  92.       
  93.   cubeVerticesBuffer = gl.createBuffer();   
  94.       
  95.   // Select the cubeVerticesBuffer as the one to apply vertex   
  96.   // operations to from here out.   
  97.       
  98.   gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);   
  99.       
  100.   // Now create an array of vertices for the cube.   
  101.       
  102.   var vertices = [   
  103.     // Front face   
  104.     -1.0, -1.0,  1.0,   
  105.      1.0, -1.0,  1.0,   
  106.      1.0,  1.0,  1.0,   
  107.     -1.0,  1.0,  1.0,   
  108.         
  109.     // Back face   
  110.     -1.0, -1.0, -1.0,   
  111.     -1.0,  1.0, -1.0,   
  112.      1.0,  1.0, -1.0,   
  113.      1.0, -1.0, -1.0,   
  114.         
  115.     // Top face   
  116.     -1.0,  1.0, -1.0,   
  117.     -1.0,  1.0,  1.0,   
  118.      1.0,  1.0,  1.0,   
  119.      1.0,  1.0, -1.0,   
  120.         
  121.     // Bottom face   
  122.     -1.0, -1.0, -1.0,   
  123.      1.0, -1.0, -1.0,   
  124.      1.0, -1.0,  1.0,   
  125.     -1.0, -1.0,  1.0,   
  126.         
  127.     // Right face   
  128.      1.0, -1.0, -1.0,   
  129.      1.0,  1.0, -1.0,   
  130.      1.0,  1.0,  1.0,   
  131.      1.0, -1.0,  1.0,   
  132.         
  133.     // Left face   
  134.     -1.0, -1.0, -1.0,   
  135.     -1.0, -1.0,  1.0,   
  136.     -1.0,  1.0,  1.0,   
  137.     -1.0,  1.0, -1.0   
  138.   ];   
  139.       
  140.   // Now pass the list of vertices into WebGL to build the shape. We   
  141.   // do this by creating a Float32Array from the JavaScript array,   
  142.   // then use it to fill the current vertex buffer.   
  143.       
  144.   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);   
  145.     
  146.   // Set up the normals for the vertices, so that we can compute lighting.   
  147.       
  148.   cubeVerticesNormalBuffer = gl.createBuffer();   
  149.   gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);   
  150.       
  151.   var vertexNormals = [   
  152.     // Front   
  153.      0.0,  0.0,  1.0,   
  154.      0.0,  0.0,  1.0,   
  155.      0.0,  0.0,  1.0,   
  156.      0.0,  0.0,  1.0,   
  157.         
  158.     // Back   
  159.      0.0,  0.0, -1.0,   
  160.      0.0,  0.0, -1.0,   
  161.      0.0,  0.0, -1.0,   
  162.      0.0,  0.0, -1.0,   
  163.         
  164.     // Top   
  165.      0.0,  1.0,  0.0,   
  166.      0.0,  1.0,  0.0,   
  167.      0.0,  1.0,  0.0,   
  168.      0.0,  1.0,  0.0,   
  169.         
  170.     // Bottom   
  171.      0.0, -1.0,  0.0,   
  172.      0.0, -1.0,  0.0,   
  173.      0.0, -1.0,  0.0,   
  174.      0.0, -1.0,  0.0,   
  175.         
  176.     // Right   
  177.      1.0,  0.0,  0.0,   
  178.      1.0,  0.0,  0.0,   
  179.      1.0,  0.0,  0.0,   
  180.      1.0,  0.0,  0.0,   
  181.         
  182.     // Left   
  183.     -1.0,  0.0,  0.0,   
  184.     -1.0,  0.0,  0.0,   
  185.     -1.0,  0.0,  0.0,   
  186.     -1.0,  0.0,  0.0   
  187.   ];   
  188.       
  189.   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals),   
  190.                 gl.STATIC_DRAW);   
  191.       
  192.   // Map the texture onto the cube's faces.   
  193.       
  194.   cubeVerticesTextureCoordBuffer = gl.createBuffer();   
  195.   gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);   
  196.       
  197.   var textureCoordinates = [   
  198.     // Front   
  199.     0.0,  0.0,   
  200.     1.0,  0.0,   
  201.     1.0,  1.0,   
  202.     0.0,  1.0,   
  203.     // Back   
  204.     0.0,  0.0,   
  205.     1.0,  0.0,   
  206.     1.0,  1.0,   
  207.     0.0,  1.0,   
  208.     // Top   
  209.     0.0,  0.0,   
  210.     1.0,  0.0,   
  211.     1.0,  1.0,   
  212.     0.0,  1.0,   
  213.     // Bottom   
  214.     0.0,  0.0,   
  215.     1.0,  0.0,   
  216.     1.0,  1.0,   
  217.     0.0,  1.0,   
  218.     // Right   
  219.     0.0,  0.0,   
  220.     1.0,  0.0,   
  221.     1.0,  1.0,   
  222.     0.0,  1.0,   
  223.     // Left   
  224.     0.0,  0.0,   
  225.     1.0,  0.0,   
  226.     1.0,  1.0,   
  227.     0.0,  1.0   
  228.   ];   
  229.     
  230.   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),   
  231.                 gl.STATIC_DRAW);   
  232.     
  233.   // Build the element array buffer; this specifies the indices   
  234.   // into the vertex array for each face's vertices.   
  235.       
  236.   cubeVerticesIndexBuffer = gl.createBuffer();   
  237.   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);   
  238.       
  239.   // This array defines each face as two triangles, using the   
  240.   // indices into the vertex array to specify each triangle's   
  241.   // position.   
  242.       
  243.   var cubeVertexIndices = [   
  244.     0,  1,  2,      0,  2,  3,    // front   
  245.     4,  5,  6,      4,  6,  7,    // back   
  246.     8,  9,  10,     8,  10, 11,   // top   
  247.     12, 13, 14,     12, 14, 15,   // bottom   
  248.     16, 17, 18,     16, 18, 19,   // right   
  249.     20, 21, 22,     20, 22, 23    // left   
  250.   ]   
  251.       
  252.   // Now send the element array to GL   
  253.       
  254.   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,   
  255.       new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);   
  256. }   
  257.     
  258. //   
  259. // initTextures   
  260. //   
  261. // Initialize the textures we'll be using, then initiate a load of   
  262. // the texture images. The handleTextureLoaded() callback will finish   
  263. // the job; it gets called each time a texture finishes loading.   
  264. //   
  265. // TODO#1 Start   
  266. function createCubeTexture(text) {   
  267.                     
  268.     // create a hidden canvas to draw the texture   
  269.     var canvas = document.createElement('canvas');   
  270.     canvas.id     = "hiddenCanvas";   
  271.     canvas.width  = 512;   
  272.     canvas.height = 512;   
  273.     canvas.style.display   = "none";   
  274.     var body = document.getElementsByTagName("body")[0];   
  275.     body.appendChild(canvas);          
  276.     
  277.     // draw texture   
  278.     var cubeImage = document.getElementById('hiddenCanvas');   
  279.     var ctx = cubeImage.getContext('2d');   
  280.     ctx.beginPath();   
  281.     ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);              
  282.     ctx.fillStyle = 'white';   
  283.     ctx.fill();   
  284.     ctx.fillStyle = 'black';   
  285.     ctx.font = "65px Arial";   
  286.     ctx.textAlign = 'center';              
  287.     ctx.fillText(text, ctx.canvas.width / 2, ctx.canvas.height / 2);   
  288.     ctx.restore();          
  289.     
  290.     // create new texture   
  291.     var texture = gl.createTexture();   
  292.     gl.bindTexture(gl.TEXTURE_2D, texture);   
  293.     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);   
  294.     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);   
  295.     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);   
  296.     handleTextureLoaded(cubeImage, texture)   
  297.         
  298.     return texture;   
  299. }   
  300. // TODO#1 End   
  301.      
  302. function handleTextureLoaded(image, texture) {   
  303.   gl.bindTexture(gl.TEXTURE_2D, texture);   
  304.   gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);   
  305.   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);   
  306.   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);   
  307.   gl.generateMipmap(gl.TEXTURE_2D);   
  308.   gl.bindTexture(gl.TEXTURE_2D, null);   
  309. }   
  310.     
  311. //   
  312. // drawScene   
  313. //   
  314. // Draw the scene.   
  315. //   
  316. function drawScene() {   
  317.   // Clear the canvas before we start drawing on it.   
  318.     
  319.   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);   
  320.       
  321.   // Establish the perspective with which we want to view the   
  322.   // scene. Our field of view is 45 degrees, with a width/height   
  323.   // ratio of 640:480, and we only want to see objects between 0.1 units   
  324.   // and 100 units away from the camera.   
  325.       
  326.   perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0);   
  327.       
  328.   // Set the drawing position to the "identity" point, which is   
  329.   // the center of the scene.   
  330.       
  331.   loadIdentity();   
  332.       
  333.   // Now move the drawing position a bit to where we want to start   
  334.   // drawing the cube.   
  335.       
  336.   mvTranslate([0.0, 0.0, -6.0]);   
  337.       
  338.   // Save the current matrix, then rotate before we draw.   
  339.       
  340.   mvPushMatrix();   
  341.   mvRotate(cubeRotation, [1, 0, 1]);   
  342.       
  343.   // Draw the cube by binding the array buffer to the cube's vertices   
  344.   // array, setting attributes, and pushing it to GL.   
  345.       
  346.   gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);   
  347.   gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);   
  348.       
  349.   // Set the texture coordinates attribute for the vertices.   
  350.       
  351.   gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);   
  352.   gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);   
  353.       
  354.   // Bind the normals buffer to the shader attribute.   
  355.       
  356.   gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);   
  357.   gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);   
  358.       
  359.   // Specify the texture to map onto the faces.   
  360.       
  361.   gl.activeTexture(gl.TEXTURE0);   
  362.   gl.bindTexture(gl.TEXTURE_2D, cubeTexture);   
  363.   gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);   
  364.       
  365.   // Draw the cube.   
  366.       
  367.   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);   
  368.   setMatrixUniforms();   
  369.   gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);   
  370.       
  371.   // Restore the original matrix   
  372.       
  373.   mvPopMatrix();   
  374.       
  375.   // Update the rotation for the next draw, if it's time to do so.   
  376.       
  377.   var currentTime = (new Date).getTime();   
  378.   if (lastCubeUpdateTime) {   
  379.     var delta = currentTime - lastCubeUpdateTime;   
  380.         
  381.     cubeRotation += (30 * delta) / 1000.0;   
  382.   }   
  383.       
  384.   lastCubeUpdateTime = currentTime;   
  385. }   
  386.     
  387. //   
  388. // initShaders   
  389. //   
  390. // Initialize the shaders, so WebGL knows how to light our scene.   
  391. //   
  392. function initShaders() {   
  393.   var fragmentShader = getShader(gl, "shader-fs");   
  394.   var vertexShader = getShader(gl, "shader-vs");   
  395.       
  396.   // Create the shader program   
  397.       
  398.   shaderProgram = gl.createProgram();   
  399.   gl.attachShader(shaderProgram, vertexShader);   
  400.   gl.attachShader(shaderProgram, fragmentShader);   
  401.   gl.linkProgram(shaderProgram);   
  402.       
  403.   // If creating the shader program failed, alert   
  404.       
  405.   if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {   
  406.     alert("Unable to initialize the shader program.");   
  407.   }   
  408.       
  409.   gl.useProgram(shaderProgram);   
  410.       
  411.   vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");   
  412.   gl.enableVertexAttribArray(vertexPositionAttribute);   
  413.       
  414.   textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");   
  415.   gl.enableVertexAttribArray(textureCoordAttribute);   
  416.       
  417.   vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");   
  418.   gl.enableVertexAttribArray(vertexNormalAttribute);   
  419. }   
  420.     
  421. //   
  422. // getShader   
  423. //   
  424. // Loads a shader program by scouring the current document,   
  425. // looking for a script with the specified ID.   
  426. //   
  427. function getShader(gl, id) {   
  428.   var shaderScript = document.getElementById(id);   
  429.       
  430.   // Didn't find an element with the specified ID; abort.   
  431.       
  432.   if (!shaderScript) {   
  433.     return null;   
  434.   }   
  435.       
  436.   // Walk through the source element's children, building the   
  437.   // shader source string.   
  438.       
  439.   var theSource = "";   
  440.   var currentChild = shaderScript.firstChild;   
  441.       
  442.   while(currentChild) {   
  443.     if (currentChild.nodeType == 3) {   
  444.       theSource += currentChild.textContent;   
  445.     }   
  446.         
  447.     currentChildcurrentChild = currentChild.nextSibling;   
  448.   }   
  449.       
  450.   // Now figure out what type of shader script we have,   
  451.   // based on its MIME type.   
  452.       
  453.   var shader;   
  454.       
  455.   if (shaderScript.type == "x-shader/x-fragment") {   
  456.     shader = gl.createShader(gl.FRAGMENT_SHADER);   
  457.   } else if (shaderScript.type == "x-shader/x-vertex") {   
  458.     shader = gl.createShader(gl.VERTEX_SHADER);   
  459.   } else {   
  460.     return null;  // Unknown shader type   
  461.   }   
  462.       
  463.   // Send the source to the shader object   
  464.       
  465.   gl.shaderSou
    « 
    » 

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3