WebGL vertexAttribPointer 指向错误的 VBO

WebGL vertexAttribPointer points to the wrong VBO

我正在尝试创建两个网格,一个正方形和一个三角形。代码在底部。

我首先为第一个网格创建着色器程序"program1"、数组缓冲区"vertexBuffer1" 和元素数组缓冲区"indexBuffer1"。第一个网格是正方形。

然后我对第二个网格做同样的事情。第二个网格是三角形。

当我 运行 代码时出现错误:

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

如果我注释掉这一行,我不会收到错误消息:

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

我认为正在发生的事情是三角形的 vbo ("vertexBuffer2") 以某种方式连接到正方形的着色器程序 ("program1")。这导致

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

尝试从三角形顶点缓冲区绘制正方形时失败。

我不明白为什么

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

导致 "program1" 连接到 "vertexBuffer2",如果这甚至是问题的话。如何让这两个程序("program1" 和 "program2")连接到各自的缓冲区?

<!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>

注意,gl.vertexAttribPointer 指定通用顶点属性数组的状态。

首先创建并绑定 vertexBuffer1indexBuffer1program1。并且您指定:

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);

然后您创建并绑定 vertexBuffer2indexBuffer2program2。并且您指定:

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);

你终于打电话了:

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

此时顶点缓冲区状态仍然指的是 vertexBuffer2,因为这是您为索引为 0 的通用顶点属性指定的最后一个状态。

像这样更改您的代码:

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);

请注意,在绘制网格之前立即设置顶点属性状态就足够了。要管理顶点属性的不同状态,您可以在 WebGL 1.0 中使用 VertexArrayObjects, which are supported in WebGL 2.0 or available via extension


查看代码片段:

// 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>