来点真正的3D

这节我们把三角改成四棱椎,矩形改成正方体。

效果如图4。

图4

图4

之前几节的图形是一个面,而立体的图形是由多个面组成的,我们一个面一个面地给出顶点坐标,画上去就得到了三维图形。

在绘制正方体的时候,我们增加一个索引。原因是:

  1. 相邻的面是有公共点的,如果所有面都各自用坐标表示,就多了许多重复工作,这时我们增加一个索引(index),只需给出所有点的坐标,就可以用索引来重复使用点。
  2. 如果我们用“POLYGON”画面,程序不清楚我们每个面有几条边,就要用for循环去一个面一个面画。如果用“TRIANGLE_STRIP”来画,则这个面画完,可能会和另一个面的顶点组成一个我们并不想画的三角形。如果用“TRIANGLES”来画,一个面四个点,等于两个三角形,两个公共点的坐标我们就需要多提供一次。使用索引+“TRIANGLES”,就方便多了。

为了让变量名更有意义一些,我们先把三角和矩形对应的变量名改成四面体和立方体。比如rTri改为rPyramid,triangleVertexPositionBuffer改成pyramidVertexPositionBuffer等等。

四面体用老方法来画,正方体用点索引的方法(具体方法是随意的,看怎么方便了)。

var cubeVertexIndexBuffer;

增加全局变量,正方体顶点索引的buffer。

function initBuffers()
{
    pyramidVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer);
    var vertices = [
                    // 正面
                     0.0, 1.0, 0.0,
                    -1.0, -1.0, 1.0,
                     1.0, -1.0, 1.0,
                    // 右侧面
                     0.0, 1.0, 0.0,
                     1.0, -1.0, 1.0,
                     1.0, -1.0, -1.0,
                    // 背面
                     0.0, 1.0, 0.0,
                     1.0, -1.0, -1.0,
                    -1.0, -1.0, -1.0,
                    // 左侧面
                     0.0, 1.0, 0.0,
                    -1.0, -1.0, -1.0,
                    -1.0, -1.0, 1.0
                    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),
        gl.STATIC_DRAW);
    pyramidVertexPositionBuffer.itemSize = 3;
    pyramidVertexPositionBuffer.numItems = 12;

    pyramidVertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
    var colors = [
                 // 正面
                 1.0, 0.0, 0.0, 1.0,
                 0.0, 1.0, 0.0, 1.0,
                 0.0, 0.0, 1.0, 1.0,
                 // 右侧面
                 1.0, 0.0, 0.0, 1.0,
                 0.0, 0.0, 1.0, 1.0,
                 0.0, 1.0, 0.0, 1.0,
                 // 背面
                 1.0, 0.0, 0.0, 1.0,
                 0.0, 1.0, 0.0, 1.0,
                 0.0, 0.0, 1.0, 1.0,
                 // 左侧面
                 1.0, 0.0, 0.0, 1.0,
                 0.0, 0.0, 1.0, 1.0,
                 0.0, 1.0, 0.0, 1.0
                ];
     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors),
         gl.STATIC_DRAW);
     pyramidVertexColorBuffer.itemSize = 4;
     pyramidVertexColorBuffer.numItems = 12;

对四面体,给出了三维世界中每个面的顶点坐标,相应的numItems也自然和三角的不一样了。

    cubeVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
    vertices = [
                // 正面
                -1.0, -1.0,  1.0,
                 1.0, -1.0,  1.0,
                 1.0,  1.0,  1.0,
                -1.0,  1.0,  1.0,

                // 背面
                -1.0, -1.0, -1.0,
                -1.0,  1.0, -1.0,
                 1.0,  1.0, -1.0,
                 1.0, -1.0, -1.0,

                // 顶部
                -1.0,  1.0, -1.0,
                -1.0,  1.0,  1.0,
                 1.0,  1.0,  1.0,
                 1.0,  1.0, -1.0,

                // 底部
                -1.0, -1.0, -1.0,
                 1.0, -1.0, -1.0,
                 1.0, -1.0,  1.0,
                -1.0, -1.0,  1.0,

                // 右侧面
                 1.0, -1.0, -1.0,
                 1.0,  1.0, -1.0,
                 1.0,  1.0,  1.0,
                 1.0, -1.0,  1.0,

                // 左侧面
                -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;

    cubeVertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
    colors = [
              [1.0, 0.0, 0.0, 1.0],     // 正面
              [1.0, 1.0, 0.0, 1.0],     // 背面
              [0.0, 1.0, 0.0, 1.0],     // 顶部
              [1.0, 0.5, 0.5, 1.0],     // 底部
              [1.0, 0.0, 1.0, 1.0],     // 右侧面
              [0.0, 0.0, 1.0, 1.0]     // 左侧面
            ];
    var unpackedColors = [];
    for (var i in colors)
    {
        var color = colors[i];
        for (var j=0; j < 4; j++)
        {
            unpackedColors = unpackedColors.concat(color);
        }
    }
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors),
        gl.STATIC_DRAW);
    cubeVertexColorBuffer.itemSize = 4;
    cubeVertexColorBuffer.numItems = 24;

代码还是把中正方体每个面的顶点分别给出了,以便给不同的面设置不同颜色,因为一个点有三个相邻面,我们无法给一个点设置三种颜色,但是可以在同一个位置放三个不同颜色的点。

颜色按顶点的顺序对应给出,这个for循环代码的逻辑,思考一下应该会明白。

    cubeVertexIndexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    var cubeVertexIndices = 
        [
          0, 1, 2,      0, 2, 3,    // 正面
          4, 5, 6,      4, 6, 7,    // 背面
          8, 9, 10,     8, 10, 11,  // 顶部
          12, 13, 14,   12, 14, 15, // 底部
          16, 17, 18,   16, 18, 19, // 右侧面
          20, 21, 22,   20, 22, 23  // 左侧面
        ];
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 
        new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
    cubeVertexIndexBuffer.itemSize = 1;
    cubeVertexIndexBuffer.numItems = 36;
}

索引的设置方法与前面的顶点、颜色类似,只是相应改变了一些参数,如gl.ELEMENT_ARRAY_BUFFER、Uint16Array等。

function drawScene()
{
    //...
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    setMatrixUniforms();
    gl.drawElements(gl.TRIANGLES,
        cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
    ////mvPopMatrix();
}

绘制的时候,四面体依然使用gl.drawArrays()直接顶点数组画。对正方体使用索引来绘制,函数相应改为gl.drawElements(),当然执行绘制函数之前绑定的buffer也要对应IndexBuffer。

这样我们就得到了两个立体的旋转着的图形。

results matching ""

    No results matching ""