mozilla
您的搜索结果

    使用 WebGL 创建 2D 内容

    这篇翻译不完整。请帮忙从英语翻译这篇文章

     

    在成功创建WEBGL上下文后,你能开始渲染画图了。从最简单的事开始,我们们能够画出简单的2D没有纹理的对象。从这里现在开始编码画一个正方形。

    渲染场景

    在开始前,我们需要明确最重要的一点,那就是虽然我们的例子只是画一个二维物体,但我们是在三维空间中画的。所以,我们依然需要创建着色器,以通过它来渲染我们的简单场景并画出我们的物体。往下,我们将展示正方形怎样一步步地画出来。

    初始化着色器

    着色器规定使用 OpenGL ES Shading Language. 为了更方便地处理和更新内容,事实上我们可以将着色器代码写在HTML文档上,而不是把所有代码都写在 JavaScript。下面我们看一下 initShaders() 函数是怎么实现的:

    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("Unable to initialize the shader program.");
      }
      
      gl.useProgram(shaderProgram);
      
      vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
      gl.enableVertexAttribArray(vertexPositionAttribute);
    }
    
    

    程序中加载了两个着色器。首先,片段着色器是从 ID 为 "shader-fs" 的 script 元素中的内容加载的。然后,顶点着色器是由ID为"shader-vs"的 script 元素中加载的。我们将在下一节看到 getShader() 函数的细节。这段代码实际上实现了从DOM到着色器代码加载的过程。

    接着我们调用 WebGL 对象的 createProgram() 函数创建着色器,将它与两个着色器关联起来,并链接到着色器程序。完成以上步骤后,gl 对象的 LINK_STATUS 参数是用来检查着色器程序是否成功连接。如果成功的话,我们激活新渲染器程序。

    从DOM中加载着色器

    getShader() 代码中利用特定的节点名来加载着色器程序,函数返回一个编译好的渲染器程序,但是,如果它加载失败或编译失败的话,将返回 null 。

    function getShader(gl, id) {
      var shaderScript, theSource, currentChild, shader;
      
      shaderScript = document.getElementById(id);
      
      if (!shaderScript) {
        return null;
      }
      
      theSource = "";
      currentChild = shaderScript.firstChild;
      
      while(currentChild) {
        if (currentChild.nodeType == currentChild.TEXT_NODE) {
          theSource += currentChild.textContent;
        }
        
        currentChild = currentChild.nextSibling;
      }
    

    每当找到指定ID的元素后,其文本内容将被读取保存到变量 theSource 。

      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 {
         // Unknown shader type
         return null;
      }

    当读取到渲染器源码,我们根据着色器对象的属性 MIME 来判断它是顶点着色器(MIME type "x-shader/x-vertex"),还是片段着色(MIME type "x-shader-x-fragment"),接着创建与取回的源码相符类型的渲染器。

      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;
    }
    

    最后,源码将传到着色器上并编译。如果在编译途中发生错误,我们会显示一个警告并返回 null,否则,就返回一个新的编译好的着色器。问题是

    着色器

    接着,我们需要将着色器程序加入到 HTML 文档。但是,着色器的语法和具体怎样工作已经超出本篇文章讨论的范围。

    片段着色器

    每一个像素在 WebGL 多边形中都叫一个片段。这个片段着色器的工作就是建立每个像素的色彩。在这个案例中,我们会简单地给每个像素填上白色。

    gl_FragColor 是一个 GL 内置的特殊变量,用于片段的色彩填充。如下所示,设定它的值就是设定每个像素的颜色。

    <script id="shader-fs" type="x-shader/x-fragment">
      void main(void) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
      }
    </script>
    

    顶点着色器

    顶点着色器定义了每个顶点的位置和形状。

    <script id="shader-vs" type="x-shader/x-vertex">
      attribute vec3 aVertexPosition;
    
      uniform mat4 uMVMatrix;
      uniform mat4 uPMatrix;
      
      void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
      }
    </script>
    

    创建对象

    在画正方形前,我们需要创建一个缓冲器来存储它的顶点。我们需要调用 initBuffers()。当往下我们了解到更多WebGL 的高级概念,这段代码会将有更多参数,且变得更加复杂。

    var horizAspect = 480.0/640.0;
    
    function initBuffers() {
      squareVerticesBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
      
      var vertices = [
        1.0,  1.0,  0.0,
        -1.0, 1.0,  0.0,
        1.0,  -1.0, 0.0,
        -1.0, -1.0, 0.0
      ];
      
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    }
    

    在上述例子中,这段代码简单给出了绘画的性质。开始,它调用 gl 的成员函数 createBuffer() 得到了缓冲对象并存储在顶点缓冲器。然后调用 bindBuffer() 函数绑定上下文。

    当上一步完成,我们创建一个Javascript 数组去记录每一个正方体的顶点坐标。然后将其转化为 WebGL 浮点型类型的数组,并将其传到 gl 对象的 bufferData() 方法去建立对象的顶点。

    绘制场景

    当着色器创建好后,我们可以开始绘画了。因为我们这个例子不会产生动画,所以 drawScene() 方法非常简单。它仅用了几个工具函数,因而可以很简洁。

    function drawScene() {
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      
      perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0);
      
      loadIdentity();
      mvTranslate([-0.0, 0.0, -6.0]);
      
      gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
      gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
      setMatrixUniforms();
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    }
    

    第一步,用背景色擦除上下文,接着建立摄像头视图。设置45度的视图角度,并且尺寸设为 640/480的宽高比(画布尺寸)。 我们可以指定对象在摄像头距离在0.1和100单位长度的范围内渲染。

    接着通过加载特定的位置并,并且与摄像头偏移6个单位的距离,设定好正方形的定位。然后,我们绑定正方形的顶点缓冲到上下文,并配置好,再通过调用 drawArrays() 方法来画出对象。 

    如果你的浏览器支持WebGL的话,可以点击这里看看DEMO

    矩阵通用计算

    矩阵计算是一个很复杂的运算。 没人会想去自己写完所有代码来处理这些运算。幸运的是,这里有 Sylvester, 一个用于方便 JavaScript处理向量和矩阵运算的库。

    glUtils.js 文件在这个例子中是用在Web上排列大量的 WebGL 例子。没有人完全清楚它的来源,但是它简化了Sylvester 的使用,甚至保留其扩展性如增加指定类型的矩阵,同时也能输出成 HTML 来显示。

    另外,这个例子给出了几个有用的代码,它结合这些库来解决特定的任务。准确来说它们已经超出了这个例子讨论的范围,但是这里有一些非常好的有关于矩阵运算的在线引用。可以参考看See also 栏目找到相关资料。

    function loadIdentity() {
      mvMatrix = Matrix.I(4);
    }
    
    function multMatrix(m) {
      mvMatrix = 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()));
    }
    

    相关资料

    文档标签和贡献者

    此页面的贡献者有: ziyunfei, Zhining, Phaeton, yukai
    最后编辑者: yukai,