Search code examples
javascriptrenderingwebglwebgl2

WebGL render only vertices, but not triangles using gl.drawElements()


I am trying to render an indexed cube (with duplicate vertices to achieve flat shading). I set up a position buffer, an indices buffer, set the shader inputs and draw it using gl.drawElements(gl.TRIANGLES, ...):

However, on the screen, I only see the vertices, but the triangles are not being rendered.

I have put prints with gl.getError() after each gl call, but all return 0 (no error). Here is the live demo (the cube can be rotated by clicking and dragging on the canvas):

function printError(gl, msg)
{
    console.log(msg + " " + gl.getError());
}

let clicking = false;
let lastXPos = -1;
let lastYPos = -1;
let rotationSpeed = 0.5 // (deg/pixel) 0.1 degree rotation on a given axis per pixel
let pitch = 0.0
let maxPitch = 90.0
let yaw = 0.0
let projMatrix = Object();
let modelViewMatrix = Object();
let buffers = Object();
let programInfo = Object();

function deg2Rad(degrees)
{
  return degrees * (Math.PI / 180.0);
}

function main_gl()
{
    const canvas = document.querySelector('#glcanvas');
    const gl = canvas.getContext('webgl2');

    // If we don't have a GL context, give up now
    if (!gl)
    {
        alert('Unable to initialize WebGL. Your browser or machine may not support it.');
        return;
    }

    // Vertex shader program
    const vsSource = `
    attribute vec4 aVertexPosition;

    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;

    void main(void) {
      gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
    }
    `;

    // Fragment shader program
    const fsSource = `

    void main(void) {
      gl_FragColor = vec4(1, 1, 1, 1);
    }
    `;

    // Initialize a shader program; this is where all the lighting
    // for the vertices and so forth is established.
    const shaderProgram = initShaderProgram(gl, vsSource, fsSource);

    // Collect all the info needed to use the shader program.
    // Look up which attributes our shader program is using
    // for aVertexPosition, aVevrtexColor and also
    // look up uniform locations.
    programInfo =
    {
        program: shaderProgram,
        attribLocations:
        {
            vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
            vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
        },
        uniformLocations:
        {
            projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
            modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
        },
    };
    printError(gl, "Attributes and uniform gathered");


    setUpInputCallbacks(canvas, gl);

    // Here's where we call the routine that builds all the
    // objects we'll be drawing.
    buffers = initBuffers(gl);

    setUpScene(gl);

    // Draw the scene
    drawScene(gl);
}

// ================================================================================================

function initBuffers(gl)
{
    const positions = [
        -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,
         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,
    ];
    const positionBuffer = gl.createBuffer();
    printError(gl, "Position buffer created");
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    printError(gl, "Position bufffer binded");
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    printError(gl, "Position buffer filled");

    const indices = [
        0, 1, 2,
        3, 4, 5,
        6, 7, 8,
        9, 10, 11,
        12, 13, 14,
        15, 16, 17,
        18, 19, 20,
        21, 22, 23,
        24, 25, 26,
        27, 28, 29,
        30, 31, 32,
        33, 34, 35,
    ];

    var indexBuffer = gl.createBuffer ();
    printError(gl, "Index buffer created");
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    printError(gl, "Index buffer binded");
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
    printError(gl, "Index buffer filled");

    return {
        indices: indexBuffer,
        position: positionBuffer,
    };
}

// ================================================================================================

function setUpScene(gl)
{
    const fieldOfView = 45 * Math.PI / 180;   // in radians
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.1;
    const zFar = 100.0;
    projMatrix = mat4.create();
    // note: glmatrix.js always has the first argument
    // as the destination to receive the result.
    mat4.perspective(projMatrix,
                     fieldOfView,
                     aspect,
                     zNear,
                     zFar);

    modelViewMatrix = mat4.create();

    const vNumComponents = 3;
    const vType = gl.FLOAT;
    const vNormalize = false;
    const vStride = 0;
    const vOffset = 0;
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
    printError(gl, "Bind position buffer");

    gl.vertexAttribPointer(
        programInfo.attribLocations.vertexPosition,
        vNumComponents,
        vType,
        vNormalize,
        vStride,
        vOffset);
    gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
    printError(gl, "Setted shader position input");
}

function drawScene(gl)
{
    printError(gl, "Draw scene begin");

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clearDepth(1.0);                 // Clear everything
    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    printError(gl, "OpenGL configured");

    mat4.identity(modelViewMatrix);

    mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
    mat4.rotateX(modelViewMatrix, modelViewMatrix, deg2Rad(pitch));
    mat4.rotateY(modelViewMatrix, modelViewMatrix, deg2Rad(yaw));


    // Tell WebGL to use our program when drawing
    gl.useProgram(programInfo.program);
    printError(gl, "Bind program");

    // Set the shader uniforms
    gl.uniformMatrix4fv(
      programInfo.uniformLocations.projectionMatrix,
      false,
      projMatrix);
    gl.uniformMatrix4fv(
      programInfo.uniformLocations.modelViewMatrix,
      false,
      modelViewMatrix);
    printError(gl, "Setted uniforms");

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
    printError(gl, "Bind index buffer");

    gl.drawElements(gl.GL_LINES, 36, gl.UNSIGNED_SHORT, 0);
    printError(gl, "Drawing");
}

// ================================================================================================

function initShaderProgram(gl, vsSource, fsSource)
{
    const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
    const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

    const shaderProgram = gl.createProgram();
    printError(gl, "Program created");
    gl.attachShader(shaderProgram, vertexShader);
    printError(gl, "Vertex shader attached");
    gl.attachShader(shaderProgram, fragmentShader);
    printError(gl, "Fragment shader attached");
    gl.linkProgram(shaderProgram);
    printError(gl, "Program linked");

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
    {
        alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
        return null;
    }

    return shaderProgram;
}

// ================================================================================================

function loadShader(gl, type, source)
{
    const shader = gl.createShader(type);
    printError(gl, "Shader created");
    gl.shaderSource(shader, source);
    printError(gl, "Shader source setted");
    gl.compileShader(shader);
    printError(gl, "Shader compiled");

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
    {
        alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

  return shader;
}

function setUpInputCallbacks(canvas, gl)
{
    canvas.onmousedown = function(event)
    {
        clicking = event.button === 0;
    }
    canvas.onmouseup = function(event)
    {
        clicking = !event.button === 0;
        lastXPos = -1;
        lastYPos = -1;
    }
    canvas.onmousemove = function(event)
    {
        if(clicking)
        {
            if(lastXPos === -1 || lastYPos === -1)
            {
                lastXPos = event.clientX;
                lastYPos = event.clientY;
            }
            else
            {
                xDiff = lastXPos - event.clientX;
                yDiff = lastYPos - event.clientY;

                lastXPos = event.clientX;
                lastYPos = event.clientY;

                rotatePitch = yDiff * rotationSpeed;
                rotateYaw = xDiff * rotationSpeed;

                pitch += rotatePitch;
                pitchSign = pitch / Math.abs(pitch);
                if(isNaN(pitchSign))
                    pitchSign = 1.0;
                pitch = Math.min(Math.abs(pitch), maxPitch);
                pitch *= pitchSign;

                yaw += rotateYaw;

                drawScene(gl);
            }
        }
    }
    canvas.onmouseout = function(event)
    {
        lastXPos = -1;
        lastYPos = -1;
    }
}
canvas {
    border: 2px solid black;
    background-color: black;
}
<html>
<head>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
      integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
      crossorigin="anonymous" defer>
</script>
</head>
<body onload="main_gl()">
<canvas id="glcanvas" width="640" height="480"></canvas>
</body>

Any idea what could be wrong?


Solution

  • GL_LINES and GL_TRIANGLES are not valid WebGL enumerator constants. However, LINES and TRIANGLES are valide:

    gl.drawElements(gl.GL_LINES, 36, gl.UNSIGNED_SHORT, 0);

    gl.drawElements(gl.LINES, 36, gl.UNSIGNED_SHORT, 0);
    

    gl.drawElements(gl.GL_TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);

    gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
    

    function printError(gl, msg)
    {
        console.log(msg + " " + gl.getError());
    }
    
    let clicking = false;
    let lastXPos = -1;
    let lastYPos = -1;
    let rotationSpeed = 0.5 // (deg/pixel) 0.1 degree rotation on a given axis per pixel
    let pitch = 0.0
    let maxPitch = 90.0
    let yaw = 0.0
    let projMatrix = Object();
    let modelViewMatrix = Object();
    let buffers = Object();
    let programInfo = Object();
    
    function deg2Rad(degrees)
    {
      return degrees * (Math.PI / 180.0);
    }
    
    function main_gl()
    {
        const canvas = document.querySelector('#glcanvas');
        const gl = canvas.getContext('webgl2');
    
        // If we don't have a GL context, give up now
        if (!gl)
        {
            alert('Unable to initialize WebGL. Your browser or machine may not support it.');
            return;
        }
    
        // Vertex shader program
        const vsSource = `
        attribute vec4 aVertexPosition;
    
        uniform mat4 uModelViewMatrix;
        uniform mat4 uProjectionMatrix;
    
        void main(void) {
          gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
        }
        `;
    
        // Fragment shader program
        const fsSource = `
    
        void main(void) {
          gl_FragColor = vec4(1, 1, 1, 1);
        }
        `;
    
        // Initialize a shader program; this is where all the lighting
        // for the vertices and so forth is established.
        const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
    
        // Collect all the info needed to use the shader program.
        // Look up which attributes our shader program is using
        // for aVertexPosition, aVevrtexColor and also
        // look up uniform locations.
        programInfo =
        {
            program: shaderProgram,
            attribLocations:
            {
                vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
                vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
            },
            uniformLocations:
            {
                projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
                modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
            },
        };
        printError(gl, "Attributes and uniform gathered");
    
    
        setUpInputCallbacks(canvas, gl);
    
        // Here's where we call the routine that builds all the
        // objects we'll be drawing.
        buffers = initBuffers(gl);
    
        setUpScene(gl);
    
        // Draw the scene
        drawScene(gl);
    }
    
    // ================================================================================================
    
    function initBuffers(gl)
    {
        const positions = [
            -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,
             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,
        ];
        const positionBuffer = gl.createBuffer();
        printError(gl, "Position buffer created");
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        printError(gl, "Position bufffer binded");
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
        printError(gl, "Position buffer filled");
    
        const indices = [
            0, 1, 2,
            3, 4, 5,
            6, 7, 8,
            9, 10, 11,
            12, 13, 14,
            15, 16, 17,
            18, 19, 20,
            21, 22, 23,
            24, 25, 26,
            27, 28, 29,
            30, 31, 32,
            33, 34, 35,
        ];
    
        var indexBuffer = gl.createBuffer ();
        printError(gl, "Index buffer created");
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        printError(gl, "Index buffer binded");
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
        printError(gl, "Index buffer filled");
    
        return {
            indices: indexBuffer,
            position: positionBuffer,
        };
    }
    
    // ================================================================================================
    
    function setUpScene(gl)
    {
        const fieldOfView = 45 * Math.PI / 180;   // in radians
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const zNear = 0.1;
        const zFar = 100.0;
        projMatrix = mat4.create();
        // note: glmatrix.js always has the first argument
        // as the destination to receive the result.
        mat4.perspective(projMatrix,
                         fieldOfView,
                         aspect,
                         zNear,
                         zFar);
    
        modelViewMatrix = mat4.create();
    
        const vNumComponents = 3;
        const vType = gl.FLOAT;
        const vNormalize = false;
        const vStride = 0;
        const vOffset = 0;
        gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
        printError(gl, "Bind position buffer");
    
        gl.vertexAttribPointer(
            programInfo.attribLocations.vertexPosition,
            vNumComponents,
            vType,
            vNormalize,
            vStride,
            vOffset);
        gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
        printError(gl, "Setted shader position input");
    }
    
    function drawScene(gl)
    {
        printError(gl, "Draw scene begin");
    
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.clearDepth(1.0);                 // Clear everything
        gl.enable(gl.DEPTH_TEST);
        gl.depthFunc(gl.LEQUAL);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        printError(gl, "OpenGL configured");
    
        mat4.identity(modelViewMatrix);
    
        mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
        mat4.rotateX(modelViewMatrix, modelViewMatrix, deg2Rad(pitch));
        mat4.rotateY(modelViewMatrix, modelViewMatrix, deg2Rad(yaw));
    
    
        // Tell WebGL to use our program when drawing
        gl.useProgram(programInfo.program);
        printError(gl, "Bind program");
    
        // Set the shader uniforms
        gl.uniformMatrix4fv(
          programInfo.uniformLocations.projectionMatrix,
          false,
          projMatrix);
        gl.uniformMatrix4fv(
          programInfo.uniformLocations.modelViewMatrix,
          false,
          modelViewMatrix);
        printError(gl, "Setted uniforms");
    
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
        printError(gl, "Bind index buffer");
    
        gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
        printError(gl, "Drawing");
    }
    
    // ================================================================================================
    
    function initShaderProgram(gl, vsSource, fsSource)
    {
        const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
        const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
    
        const shaderProgram = gl.createProgram();
        printError(gl, "Program created");
        gl.attachShader(shaderProgram, vertexShader);
        printError(gl, "Vertex shader attached");
        gl.attachShader(shaderProgram, fragmentShader);
        printError(gl, "Fragment shader attached");
        gl.linkProgram(shaderProgram);
        printError(gl, "Program linked");
    
        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
        {
            alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
            return null;
        }
    
        return shaderProgram;
    }
    
    // ================================================================================================
    
    function loadShader(gl, type, source)
    {
        const shader = gl.createShader(type);
        printError(gl, "Shader created");
        gl.shaderSource(shader, source);
        printError(gl, "Shader source setted");
        gl.compileShader(shader);
        printError(gl, "Shader compiled");
    
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
        {
            alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
            gl.deleteShader(shader);
            return null;
        }
    
      return shader;
    }
    
    function setUpInputCallbacks(canvas, gl)
    {
        canvas.onmousedown = function(event)
        {
            clicking = event.button === 0;
        }
        canvas.onmouseup = function(event)
        {
            clicking = !event.button === 0;
            lastXPos = -1;
            lastYPos = -1;
        }
        canvas.onmousemove = function(event)
        {
            if(clicking)
            {
                if(lastXPos === -1 || lastYPos === -1)
                {
                    lastXPos = event.clientX;
                    lastYPos = event.clientY;
                }
                else
                {
                    xDiff = lastXPos - event.clientX;
                    yDiff = lastYPos - event.clientY;
    
                    lastXPos = event.clientX;
                    lastYPos = event.clientY;
    
                    rotatePitch = yDiff * rotationSpeed;
                    rotateYaw = xDiff * rotationSpeed;
    
                    pitch += rotatePitch;
                    pitchSign = pitch / Math.abs(pitch);
                    if(isNaN(pitchSign))
                        pitchSign = 1.0;
                    pitch = Math.min(Math.abs(pitch), maxPitch);
                    pitch *= pitchSign;
    
                    yaw += rotateYaw;
    
                    drawScene(gl);
                }
            }
        }
        canvas.onmouseout = function(event)
        {
            lastXPos = -1;
            lastYPos = -1;
        }
    }
    canvas {
        border: 2px solid black;
        background-color: black;
    }
    <html>
    <head>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
          integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
          crossorigin="anonymous" defer>
    </script>
    </head>
    <body onload="main_gl()">
    <canvas id="glcanvas" width="640" height="480"></canvas>
    </body>