Search code examples
javascriptglslwebgl

I'm trying to create a 3d cube in Webgl but I'm making a diamond?


I am new to Webgl and have been following numerous guides to create a 3d cube that has interpolated colors, the colors are interpolated and the animation is working. However, I need the shape to be a 3D cube and instead it's a 3D pyramid and I for the life of me cannot figure out why.

This is my code

//Defing Vector shader
var VSHADER_SOURCE = 
'precision mediump float;\n '+
'attribute vec3 vertPosition;\n '+
'attribute vec3 vertColor;\n '+
'uniform mat4 mWorld;\n '+
'uniform mat4 mView;\n '+
'uniform mat4 mProj;\n '+
'varying  vec3 vColor;\n '+
'void main()\n '+
'{\n '+
'  vColor = vertColor;\n '+
'  gl_Position = mProj * mView * mWorld * vec4(vertPosition, 1.0);\n '+
'}'
//Defining Fragment Shader
var FSHADER_SOURCE =
'precision mediump float;\n '+
'varying  vec3 vColor;\n '+
'void main()\n '+
'{\n '+
'  gl_FragColor = vec4(vColor, 1.0);\n '+
'}';

//Defining vertices for rectangular cube
var boxVertices = 
[ // X, Y, Z    
// 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,
];
//Defining indices for rectangular cube
var boxIndices =
[
    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
];
//Defining colors for the color buffer
const colors = [
    1.0,1.0,1.0, //white color
    1.0,0.0,0.0, //red color
    0.0,1.0,0.0, //green color
    0.0,0.0,1.0, //blue color
    0.0,0.0,0.0, // black color
    1.0,1.0,1.0, //white color
    1.0,0.0,0.0, //red color
    0.0,1.0,0.0, //green color
    0.0,0.0,1.0, //blue color
    0.0,0.0,0.0, // black color
    1.0,1.0,1.0, //white color
    1.0,0.0,0.0, //red color
    0.0,1.0,0.0, //green color
    0.0,0.0,1.0, //blue color
    0.0,0.0,0.0, // black color
    1.0,1.0,1.0, //white color
    1.0,0.0,0.0, //red color
    0.0,1.0,0.0, //green color
    0.0,0.0,1.0, //blue color
    0.0,0.0,0.0, // black color
    1.0,0.0,0.0, //red color
    0.0,1.0,0.0, //green color
    0.0,0.0,1.0, //blue color
    0.0,0.0,0.0, // black color
];

var angle = 0;

function main() {
    var canvas = document.getElementById('canvas');
    var gl = canvas.getContext('webgl');

    if (!gl) {
        console.log('Your browser does not support WebGL');
    }
    // Initializing shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }
    gl.clearColor(0.5, 0.5, 0.5, 0.9);
    gl.clearDepth(1.0);
    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    var n = initBuffers(gl,boxVertices,boxIndices,colors);

    var matWorldUniformLocation = gl.getUniformLocation(gl.program, 'mWorld');
    checkLocations(matWorldUniformLocation);
    var matViewUniformLocation = gl.getUniformLocation(gl.program, 'mView');
    checkLocations(matViewUniformLocation);
    var matProjUniformLocation = gl.getUniformLocation(gl.program, 'mProj');
    checkLocations(matProjUniformLocation);
    

    var worldMatrix = mat4.create();
    var viewMatrix = mat4.create();
    var projMatrix = mat4.create();
    
    mat4.lookAt(viewMatrix, [3, 3, 7], //x,y,z
         [0, 0, 0], [0, 1, 0]);
    mat4.perspective(projMatrix, glMatrix.toRadian(20), 1/1, 1, 100);

    gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
    gl.uniformMatrix4fv(matViewUniformLocation, gl.FALSE, viewMatrix);
    gl.uniformMatrix4fv(matProjUniformLocation, gl.FALSE, projMatrix);
    

    var xRotationMatrix = mat4.create();
    var yRotationMatrix = mat4.create();
    var identityMatrix = mat4.create(); 
    function loop() {
        angle = performance.now() / 1000 / 6 * 2 * Math.PI;
        mat4.rotate(yRotationMatrix, identityMatrix, angle, [0, 1, 0]);
        mat4.rotate(xRotationMatrix, identityMatrix, angle / 4, [1, 0, 0]);
        mat4.mul(worldMatrix, yRotationMatrix, xRotationMatrix);
        gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
        gl.clearColor(0.5, 0.5, 0.5, 0.8);
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
        gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
    
        requestAnimationFrame(loop);
    };
    requestAnimationFrame(loop);
};

function checkLocations(location){
    if(!location){
        console.log("ERROR: cannot find storage location of " + location);
    }
}

function initBuffers(gl,boxVertices,boxIndices,colors){

    const boxVertexBufferObject = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, boxVertexBufferObject);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(boxVertices), gl.STATIC_DRAW);

    const boxIndexBufferObject = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boxIndexBufferObject);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(boxIndices), gl.STATIC_DRAW);
 

    const colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors),gl.STATIC_DRAW);

    var positionAttribLocation = gl.getAttribLocation(gl.program, 'vertPosition');
    var colorAttribLocation = gl.getAttribLocation(gl.program, 'vertColor');
    gl.vertexAttribPointer(positionAttribLocation, // Attribute location
        3, // Number of elements per attribute
        gl.FLOAT, // Type of elements
        gl.FALSE,
        0, // Size of an individual vertex
        0 // Offset from the beginning of a single vertex to this attribute
    );
    gl.vertexAttribPointer(
        colorAttribLocation, // Attribute location
        3, // Number of elements per attribute
        gl.FLOAT, // Type of elements
        gl.FALSE,
        0,
        0  
    );

    gl.enableVertexAttribArray(positionAttribLocation);
    gl.enableVertexAttribArray(colorAttribLocation);

    // Tell OpenGL state machine which program should be active.
    gl.useProgram(gl.program);
    var n = boxIndices.length;
    return n;
}

Solution

  • When you call vertexAttribPointer it reads data from the currently bound gl.ARRAY_BUFFER.

    In your code you should bind your boxVertexBufferObject buffer and then call the corresponding gl.vertexAttribPointer(positionAttribLocation....

    Then you should bind your colorBuffer and then call the corresponding gl.vertexAttribPointer(colorAttribLocation....

    The error in your code was that you bound the boxVertexBufferObject buffer and then bound the colorBuffer before ever calling gl.vertexAttribPointer, effectively overwriting the data you needed to load. The result was that the colorBuffer was loaded into both the vertPosition and vertColor attributes and that's why your faces were loading incorrectly. The faces were using the colorBuffer values for vertices instead of the boxIndices values.

    <!DOCTYPE html>
    <html>
    
    <body>
        <canvas id="canvas"></canvas>
    </body>
    <script type="module">
        import { glMatrix, mat4 } from 'https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/+esm'
        window.mat4 = mat4;
        console.log(mat4)
        //Defing Vector shader
        var VSHADER_SOURCE =
            'precision mediump float;\n ' +
            'attribute vec3 vertPosition;\n ' +
            'attribute vec3 vertColor;\n ' +
            'uniform mat4 mWorld;\n ' +
            'uniform mat4 mView;\n ' +
            'uniform mat4 mProj;\n ' +
            'varying  vec3 vColor;\n ' +
            'void main()\n ' +
            '{\n ' +
            '  vColor = vertColor;\n ' +
            '  gl_Position = mProj * mView * mWorld * vec4(vertPosition, 1.0);\n ' +
            '}'
        //Defining Fragment Shader
        var FSHADER_SOURCE =
            'precision mediump float;\n ' +
            'varying  vec3 vColor;\n ' +
            'void main()\n ' +
            '{\n ' +
            '  gl_FragColor = vec4(vColor, 1.0);\n ' +
            '}';
    
        //Defining vertices for rectangular cube
        var boxVertices =
            [ // X, Y, Z    
                // 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,
            ];
        //Defining indices for rectangular cube
        var boxIndices =
            [
                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
            ];
    
        //Defining indices for rectangular cube
        // var boxIndices =
        //     [
        //         0,
        //         1,
        //         2,
        //         // 7,
        //         // 8,
        //         // 9,
        //         4,
        //         5,
        //         6
        //     ];
    
        //Defining colors for the color buffer
        const colors = [
            1.0, 1.0, 1.0, //white color
            1.0, 0.0, 0.0, //red color
            0.0, 1.0, 0.0, //green color
            0.0, 0.0, 1.0, //blue color
            0.0, 0.0, 0.0, // black color
            1.0, 1.0, 1.0, //white color
            1.0, 0.0, 0.0, //red color
            0.0, 1.0, 0.0, //green color
            0.0, 0.0, 1.0, //blue color
            0.0, 0.0, 0.0, // black color
            1.0, 1.0, 1.0, //white color
            1.0, 0.0, 0.0, //red color
            0.0, 1.0, 0.0, //green color
            0.0, 0.0, 1.0, //blue color
            0.0, 0.0, 0.0, // black color
            1.0, 1.0, 1.0, //white color
            1.0, 0.0, 0.0, //red color
            0.0, 1.0, 0.0, //green color
            0.0, 0.0, 1.0, //blue color
            0.0, 0.0, 0.0, // black color
            1.0, 0.0, 0.0, //red color
            0.0, 1.0, 0.0, //green color
            0.0, 0.0, 1.0, //blue color
            0.0, 0.0, 0.0, // black color
        ];
    
        var angle = 0;
    
        function initShaders(gl, vertexSource, fragmentSource) {
            const vertexShader = gl.createShader(gl.VERTEX_SHADER);
            gl.shaderSource(vertexShader, vertexSource);
            gl.compileShader(vertexShader);
            const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
            gl.shaderSource(fragmentShader, fragmentSource);
            gl.compileShader(fragmentShader);
            const program = gl.createProgram();
            gl.attachShader(program, vertexShader);
            gl.attachShader(program, fragmentShader);
            gl.linkProgram(program);
            gl.validateProgram(program);
            if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
                console.error('Link failed: ' + gl.getProgramInfoLog(program));
                console.error('vs info-log: ' + gl.getShaderInfoLog(vertexShader));
                console.error('fs info-log: ' + gl.getShaderInfoLog(fragmentShader));
            }
            gl.useProgram(program);
            gl.program = program;
            return program;
        }
    
        function main() {
            var canvas = document.getElementById('canvas');
            var gl = canvas.getContext('webgl');
    
            if (!gl) {
                console.log('Your browser does not support WebGL');
            }
            // Initializing shaders
            if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
                console.log('Failed to intialize shaders.');
                return;
            }
            gl.clearColor(0.5, 0.5, 0.5, 0.9);
            gl.clearDepth(1.0);
            gl.enable(gl.DEPTH_TEST);
            gl.depthFunc(gl.LEQUAL);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
            var n = initBuffers(gl, boxVertices, boxIndices, colors);
    
            var matWorldUniformLocation = gl.getUniformLocation(gl.program, 'mWorld');
            checkLocations(matWorldUniformLocation);
            var matViewUniformLocation = gl.getUniformLocation(gl.program, 'mView');
            checkLocations(matViewUniformLocation);
            var matProjUniformLocation = gl.getUniformLocation(gl.program, 'mProj');
            checkLocations(matProjUniformLocation);
    
    
            var worldMatrix = mat4.create();
            var viewMatrix = mat4.create();
            var projMatrix = mat4.create();
    
            mat4.lookAt(viewMatrix, [3, 3, 7], //x,y,z
                [0, 0, 0], [0, 1, 0]);
            mat4.perspective(projMatrix, glMatrix.toRadian(20), 1 / 1, 1, 100);
    
            gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
            gl.uniformMatrix4fv(matViewUniformLocation, gl.FALSE, viewMatrix);
            gl.uniformMatrix4fv(matProjUniformLocation, gl.FALSE, projMatrix);
    
    
            var xRotationMatrix = mat4.create();
            var yRotationMatrix = mat4.create();
            var identityMatrix = mat4.create();
            function loop() {
                angle = performance.now() / 1000 / 6 * 2 * Math.PI;
                mat4.rotate(yRotationMatrix, identityMatrix, angle, [0, 1, 0]);
                mat4.rotate(xRotationMatrix, identityMatrix, angle / 4, [1, 0, 0]);
                mat4.mul(worldMatrix, yRotationMatrix, xRotationMatrix);
                gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
                gl.clearColor(0.5, 0.5, 0.5, 0.8);
                gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
                gl.drawElements(gl.TRIANGLES, boxIndices.length, gl.UNSIGNED_SHORT, 0);
    
                requestAnimationFrame(loop);
            };
            requestAnimationFrame(loop);
        };
    
        function checkLocations(location) {
            if (!location) {
                console.log("ERROR: cannot find storage location of " + location);
            }
        }
    
        function initBuffers(gl, boxVertices, boxIndices, colors) {
            gl.useProgram(gl.program);
    
            var positionAttribLocation = gl.getAttribLocation(gl.program, 'vertPosition');
            var colorAttribLocation = gl.getAttribLocation(gl.program, 'vertColor');
    
            gl.enableVertexAttribArray(positionAttribLocation);
            gl.enableVertexAttribArray(colorAttribLocation);
    
            const boxVertexBufferObject = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, boxVertexBufferObject);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(boxVertices), gl.STATIC_DRAW);
    
            gl.vertexAttribPointer(positionAttribLocation, // Attribute location
                3, // Number of elements per attribute
                gl.FLOAT, // Type of elements
                false,
                0, // Size of an individual vertex
                0 // Offset from the beginning of a single vertex to this attribute
            );
    
    
            const colorBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
            gl.vertexAttribPointer(
                colorAttribLocation, // Attribute location
                3, // Number of elements per attribute
                gl.FLOAT, // Type of elements
                false,
                0,
                0
            );
            const boxIndexBufferObject = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boxIndexBufferObject);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(boxIndices), gl.STATIC_DRAW);
    
            // Tell OpenGL state machine which program should be active.
            var n = boxIndices.length;
            return n;
        }
        main();
    </script>
    
    </html>