WebGL 使用 gl.drawElements() 只渲染顶点,不渲染三角形

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

我正在尝试渲染一个索引立方体(使用重复顶点来实现平面着色)。我设置了一个位置缓冲区,一个索引缓冲区,设置着色器输入并使用 gl.drawElements(gl.TRIANGLES, ...):

绘制它

但是,在屏幕上,我只看到顶点,但没有渲染三角形。

我在每次 gl 调用后都打印了 gl.getError(),但所有 return 0(没有错误)。这是现场演示(可以通过单击并拖动 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>

知道哪里出了问题吗?

GL_LINESGL_TRIANGLES 不是有效的 WebGL 枚举器常量。但是,LINESTRIANGLES 有效:

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>