WebGL Drawing a Cube

←Back

Related Topics: Vertex Buffer Object, GLSL Shader

Overview

v0-v1-v2 v2-v3-v0 v0-v3-v7 v7-v4-v0 v0-v4-v5 v5-v1-v0 base cube

A cube has 6 faces, and each face consists of 2 triangle primitives with 4 vertices, for example, the front face has v0-v1-v2 and v2-v3-v0 triangles. The right-side face has v0-v3-v7 and v7-v4-v0 triangles. The top face is composed of v0-v4-v5 and v5-v1-v0 triangles, and so on. Each triangle must be constructed with counter-clockwise winding.

Therefore, a cube requires total 24 vertices and 12 triangle primitives. Therefore, you need to allocate a Float32Array with 72 elements to define a cube; 24 vertices x 3 coordinates per vertex. If you also specify normals, texture coordinates and colors to the corresponding vertices, you need additional arrays for each vertex attributes, or to increase the size of a single array.

If you want to draw the cube using gl.drawElements(), you need an additional Uint16Array or Uint8Array of 36 indices; 12 triangles x 3.


// define a cube with 24 vertices
let vertices = new Float32Array([
    1, 1, 1, -1, 1, 1, -1,-1, 1,  1,-1, 1,    // v0-v1-v2-v3
    1, 1, 1,  1,-1, 1,  1,-1,-1,  1, 1,-1,    // v0-v3-v7-v4
    1, 1, 1,  1, 1,-1, -1, 1,-1, -1, 1, 1,    // v0-v4-v5-v1
   -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1,    // v1-v5-v6-v2
   -1,-1,-1,  1,-1,-1,  1,-1, 1, -1,-1, 1,    // v6-v7-v3-v2
   -1, 1,-1,  1, 1,-1,  1,-1,-1, -1,-1,-1     // v5-v4-v7-v6
]);

// index array 36 indices (2 triangles per face)
let indices = new Uint16Array([
    0,  1,  2,    2,  3,  0,                  // v0-v1-v2, v2-v3-v0
    4,  5,  6,    6,  7,  4,                  // v0-v3-v7, v7-v4-v0
    8,  9, 10,   10, 11,  8,                  // v0-v4-v5, v5-v1-v0
   12, 13, 14,   14, 15, 12,                  // v1-v5-v6, v6-v2-v1
   16, 17, 18,   18, 19, 16,                  // v6-v7-v3, v3-v2-v6
   20, 21, 22,   22, 23, 20                   // v5-v4-v7, v7-v6-v5
]);

Shared Vertex

normals at shared vertex
Different normals at a shared vertex

A cube has only 8 unique vertex positions, however other vertex attributes such as normals, colors, texture coordinates may be different at the same vertex position.

For example, the front, right and top faces must have different normal vectors; n0, n1, n2 at the shared vertex position, v0. Since the shared vertex position must be repeated 3 times, the total number of vertices should be 24; 6 faces x 4 vertices per face.

Example: Rotating Cube

This example explains how to define a VBO for a cube, and to draw a rotating cube applying vertex colors and a texture map. Please see the complete code from the following link.


// init arraysfor a cube
// See the full JS code from the download link
function initCube(gl)
{
    // cube ///////////////////////////////////////////////////////////////////
    //    v5------v4  Using GL_TRIANGLES per side
    //   /|      /|   
    //  v1------v0|   
    //  | |     | |   
    //  | |v6---|-|v7 
    //  |/      |/    
    //  v2------v3    

    // vertex position array
    let vertices = new Float32Array([
        1, 1, 1, -1, 1, 1, -1,-1, 1,  1,-1, 1,    // v0-v1-v2-v3
        1, 1, 1,  1,-1, 1,  1,-1,-1,  1, 1,-1,    // v0-v3-v7-v4
        1, 1, 1,  1, 1,-1, -1, 1,-1, -1, 1, 1,    // v0-v4-v5-v1
       -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1,    // v1-v5-v6-v2
       -1,-1,-1,  1,-1,-1,  1,-1, 1, -1,-1, 1,    // v6-v7-v3-v2
       -1, 1,-1,  1, 1,-1,  1,-1,-1, -1,-1,-1     // v5-v4-v7-v6
    ]);

    // normal array
    let normals = new Float32Array([
        0, 0, 1,  0, 0, 1,  0, 0, 1,  0, 0, 1,     // v0-v1-v2-v3
        1, 0, 0,  1, 0, 0,  1, 0, 0,  1, 0, 0,     // v0-v3-v7-v4
        0, 1, 0,  0, 1, 0,  0, 1, 0,  0, 1, 0,     // v0-v4-v5-v1
       -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,     // v1-v5-v6-v2
        0,-1, 0,  0,-1, 0,  0,-1, 0,  0,-1, 0,     // v6-v7-v3-v2
        0, 0,-1,  0, 0,-1,  0, 0,-1,  0, 0,-1      // v5-v4-v7-v6
    ]);

    // color array (r,g,b,a)
    let colors = new Float32Array([
        1,1,1,1,  1,1,0,1,  1,0,0,1,  1,0,1,1,     // v0-v1-v2-v3
        1,1,1,1,  1,0,1,1,  0,0,1,1,  0,1,1,1,     // v0-v3-v7-v4
        1,1,1,1,  0,1,1,1,  0,1,0,1,  1,1,0,1,     // v0-v4-v5-v1
        1,1,0,1,  0,1,0,1,  0,0,0,1,  1,0,0,1,     // v1-v5-v6-v2
        0,0,0,1,  0,0,1,1,  1,0,1,1,  1,0,0,1,     // v6-v7-v3-v2
        0,1,0,1,  0,1,1,1,  0,0,1,1,  0,0,0,1      // v5-v4-v7-v6
    ]);

    // tex-coord array (s,t)
    let texCoords = new Float32Array([
        1,0,  0,0,  0,1,  1,1,                     // v0-v1-v2-v3
        0,0,  0,1,  1,1,  1,0,                     // v0-v3-v7-v4
        1,1,  1,0,  0,0,  0,1,                     // v0-v4-v5-v1
        1,0,  0,0,  0,1,  1,1,                     // v1-v5-v6-v2
        0,1,  1,1,  1,0,  0,0,                     // v6-v7-v3-v2
        1,0,  0,0,  0,1,  1,1                      // v5-v4-v7-v6
    ]);

    // index array (2 triangles per side)
    let indices = new Uint16Array([
        0,  1,  2,    2,  3,  0,                   // v0-v1-v2, v2-v3-v0
        4,  5,  6,    6,  7,  4,                   // v0-v3-v7, v7-v4-v0
        8,  9, 10,   10, 11,  8,                   // v0-v4-v5, v5-v1-v0
       12, 13, 14,   14, 15, 12,                   // v1-v5-v6, v6-v2-v1
       16, 17, 18,   18, 19, 16,                   // v6-v7-v3, v3-v2-v6
       20, 21, 22,   22, 23, 20                    // v5-v4-v7, v7-v6-v5
    ]);

    // create  vertex buffer
    gl.vbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vbo);

    let dataSize = vertices.byteLength + normals.byteLength +
                   colors.byteLength + texCoords.byteLength;
    gl.bufferData(gl.ARRAY_BUFFER, dataSize, gl.STATIC_DRAW);

    gl.vbo.vOffset = 0;
    gl.bufferSubData(gl.ARRAY_BUFFER, gl.vbo.vOffset, vertices);

    gl.vbo.nOffset = vertices.byteLength;
    gl.bufferSubData(gl.ARRAY_BUFFER, gl.vbo.nOffset, normals);

    gl.vbo.cOffset = vertices.byteLength + normals.byteLength;
    gl.bufferSubData(gl.ARRAY_BUFFER, gl.vbo.cOffset, colors);

    gl.vbo.tOffset = vertices.byteLength + normals.byteLength +
                     colors.byteLength;
    gl.bufferSubData(gl.ARRAY_BUFFER, gl.vbo.tOffset, texCoords);

    // create index buffer
    gl.ibo = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.ibo);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
    gl.ibo.indexCount = indices.length;
}
...

// draw the cube
// bind texture
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, gl.tex0);

// draw triangle
gl.bindBuffer(gl.ARRAY_BUFFER, gl.vbo);
gl.vertexAttribPointer(program.attribute.position, 3, gl.FLOAT, false, 0, gl.vbo.vOffset);
gl.vertexAttribPointer(program.attribute.normal, 3, gl.FLOAT, false, 0, gl.vbo.nOffset);
gl.vertexAttribPointer(program.attribute.color, 4, gl.FLOAT, false, 0, gl.vbo.cOffset);
gl.vertexAttribPointer(program.attribute.texCoord0, 2, gl.FLOAT, false, 0, gl.vbo.tOffset);

// draw using indices
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.ibo);
gl.drawElements(gl.TRIANGLES, gl.ibo.indexCount, gl.UNSIGNED_SHORT, 0);


decoration Fullscreen Demo: test_cube.html
decoration GitHub Repo: test_cube.html

←Back
 
Hide Comments
comments powered by Disqus