1
votes

I am trying to create two meshes, a square and a triangle. The code is at the bottom.

I first create a shader program "program1", array buffer "vertexBuffer1" and an element array buffer "indexBuffer1" for the first mesh. The first mesh is a square.

Then I do the same thing for the second mesh. The second mesh is a triangle.

When I run the code I get the error:

[.Offscreen-For-WebGL-000002B76A973870]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 0

I don't get the error if I comment out this line:

//gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0); // <- problem!

I think what is happening is that somehow the triangle's vbo ("vertexBuffer2") gets connected to the square's shader program ("program1"). This causes

gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_SHORT, 0);

to fail as it is trying to draw a square from the triangles vertex buffer.

I don't understand why

gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0); // <- problem!

causes "program1" to connect to "vertexBuffer2", if that is even the problem. How do I make theese two programs ("program1" and "program2") connect to each their buffers?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <canvas id="canvas" width="500" height="450" style="border:1px solid black"></canvas>
    <script>
        // setup gl
        var canvas = document.getElementById("canvas");
        var gl = canvas.getContext("webgl");
        gl.enable(gl.DEPTH_TEST);
        gl.depthFunc(gl.LEQUAL);
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        gl.clearDepth(1.0);

        gl.viewport(0.0, 0.0, canvas.width, canvas.height);

        // setup mesh 1
        // // vertex shader
        var vertexShader1 = gl.createShader(gl.VERTEX_SHADER);
        var vertexShaderCode = `
            // in
            attribute vec3 position;

            void main(void) {
                gl_Position = vec4(position, 1.);
            }
        `;
        gl.shaderSource(vertexShader1, vertexShaderCode);
        gl.compileShader(vertexShader1);
        // // fragment shader
        var fragmentShader1 = gl.createShader(gl.FRAGMENT_SHADER);
        var fragmentShaderCode = `
            precision mediump float;
            // in
            void main(void) {
                gl_FragColor = vec4(1., 0., 0., 1.);
            }
        `;
        gl.shaderSource(fragmentShader1, fragmentShaderCode);
        gl.compileShader(fragmentShader1);
        // // program1
        var program1 = gl.createProgram();
        gl.attachShader(program1, vertexShader1);
        gl.attachShader(program1, fragmentShader1);
        gl.linkProgram(program1);
        gl.useProgram(program1);

        // // create buffer 1
        var vertices1 = [ // suqare
            -0.5, -0.5, 0,
            0.5, -0.5, 0,
            0.5, 0.5, 0,
            -0.5, 0.5, 0,
        ];
        var vertexBuffer1 = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices1), gl.STATIC_DRAW);
        var loc = gl.getAttribLocation(program1, "position");
        gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(loc);

        var indices1 = [
            0,1,2,
            0,2,3,
        ];
        var indexBuffer1 = gl.createBuffer ();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer1);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices1), gl.STATIC_DRAW);

        // setup mesh 2
        // // vertex shader
        var vertexShader2 = gl.createShader(gl.VERTEX_SHADER);

        gl.shaderSource(vertexShader2, vertexShaderCode); // uses same vertexShaderCode as above
        gl.compileShader(vertexShader2);
        // // fragment shader
        var fragmentShader2 = gl.createShader(gl.FRAGMENT_SHADER);

        gl.shaderSource(fragmentShader2, fragmentShaderCode);
        gl.compileShader(fragmentShader2);
        // // program2
        var program2 = gl.createProgram();
        gl.attachShader(program2, vertexShader2);
        gl.attachShader(program2, fragmentShader2);
        gl.linkProgram(program2);
        gl.useProgram(program2);

        // // create buffer 2
        var vertices2 = [ // triangle one less vertex than in buffer 1
            -0.5, -0.5, 0,
            0.5, -0.5, 0,
            0.5, 0.5, 0,
        ];
        var vertexBuffer2 = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer2);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices2), gl.STATIC_DRAW);
        var loc = gl.getAttribLocation(program2, "position");
        // On the line below it seems like program1 instead of program2 gets connected to the vertexBuffer2
        gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0); // <- problem!
        gl.enableVertexAttribArray(loc);

        var indices2 = [
            0,1,2,
        ];
        var indexBuffer2 = gl.createBuffer ();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer2);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices2), gl.STATIC_DRAW);

        // render
        // // clear
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // // render mesh 1 (the square)
        gl.useProgram(program1);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer1);
        gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_SHORT, 0); // <- this line fail
    </script>
</body>
</html>
1

1 Answers

1
votes

Note, gl.vertexAttribPointer specifies the state for a generic vertex attribute array.

First you create and bind vertexBuffer1, indexBuffer1 and program1. And you specify:

gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
....
var loc = gl.getAttribLocation(program1, "position"); // loc == 0
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(loc);

Second you create and bind vertexBuffer2, indexBuffer2 and program2. And you specify:

gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer2);
....
var loc = gl.getAttribLocation(program2, "position"); // loc == 0
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(loc);

Finally you call:

gl.useProgram(program1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer1);
gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_SHORT, 0);

At this moment the vertex buffer state still refers to vertexBuffer2, because this is the last state which you specified for the generic vertex attribute with index 0.

Change yor code like this:

gl.linkProgram(program1);
var loc1 = gl.getAttribLocation(program1, "position");

....

gl.useProgram(program1);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer1);
gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_SHORT, 0);

Note, it is sufficient to set the vertex attribute sate immediately before you draw a mesh. For managing different states of vertex attributes you may use VertexArrayObjects, which are supported in WebGL 2.0 or available via extension in WebGL 1.0.


See the code snippet:

// setup gl
        var canvas = document.getElementById("canvas");
        var gl = canvas.getContext("webgl");
        gl.enable(gl.DEPTH_TEST);
        gl.depthFunc(gl.LEQUAL);
        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        gl.clearDepth(1.0);

        gl.viewport(0.0, 0.0, canvas.width, canvas.height);

        // setup mesh 1
        // // vertex shader
        var vertexShader1 = gl.createShader(gl.VERTEX_SHADER);
        var vertexShaderCode = `
            // in
            attribute vec3 position;

            void main(void) {
                gl_Position = vec4(position, 1.);
            }
        `;
        gl.shaderSource(vertexShader1, vertexShaderCode);
        gl.compileShader(vertexShader1);
        // // fragment shader
        var fragmentShader1 = gl.createShader(gl.FRAGMENT_SHADER);
        var fragmentShaderCode = `
            precision mediump float;
            // in
            void main(void) {
                gl_FragColor = vec4(1., 0., 0., 1.);
            }
        `;
        gl.shaderSource(fragmentShader1, fragmentShaderCode);
        gl.compileShader(fragmentShader1);
        // // program1
        var program1 = gl.createProgram();
        gl.attachShader(program1, vertexShader1);
        gl.attachShader(program1, fragmentShader1);
        gl.linkProgram(program1);
        var loc1 = gl.getAttribLocation(program1, "position");
        
        // // create buffer 1
        var vertices1 = [ // suqare
            -0.5, -0.5, 0,
            0.5, -0.5, 0,
            0.5, 0.5, 0,
            -0.5, 0.5, 0,
        ];
        var vertexBuffer1 = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices1), gl.STATIC_DRAW);
                    
        var indices1 = [
            0,1,2,
            0,2,3,
        ];
        var indexBuffer1 = gl.createBuffer ();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer1);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices1), gl.STATIC_DRAW);

        // setup mesh 2
        // // vertex shader
        var vertexShader2 = gl.createShader(gl.VERTEX_SHADER);

        gl.shaderSource(vertexShader2, vertexShaderCode); // uses same vertexShaderCode as above
        gl.compileShader(vertexShader2);
        // // fragment shader
        var fragmentShader2 = gl.createShader(gl.FRAGMENT_SHADER);

        gl.shaderSource(fragmentShader2, fragmentShaderCode);
        gl.compileShader(fragmentShader2);
        // // program2
        var program2 = gl.createProgram();
        gl.attachShader(program2, vertexShader2);
        gl.attachShader(program2, fragmentShader2);
        gl.linkProgram(program2);
        var loc2 = gl.getAttribLocation(program2, "position");
        
        // // create buffer 2
        var vertices2 = [ // triangle one less vertex than in buffer 1
            -0.5, -0.5, 0,
            0.5, -0.5, 0,
            0.5, 0.5, 0,
        ];
        var vertexBuffer2 = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer2);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices2), gl.STATIC_DRAW);

        var indices2 = [
            0,1,2,
        ];
        var indexBuffer2 = gl.createBuffer ();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer2);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices2), gl.STATIC_DRAW);

        // render
        // // clear
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // // render mesh 1 (the square)
        gl.useProgram(program1);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
        gl.vertexAttribPointer(loc1, 3, gl.FLOAT, false, 0, 0); // <- problem!
        gl.enableVertexAttribArray(loc1);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer1);
        gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_SHORT, 0); // <- this line fail
<canvas id="canvas" width="500" height="450" style="border:1px solid black"></canvas>