WebGL:索引的使用会导致模型消失

WebGL: usage of indices causes model to dissapear

本文关键字:模型 消失 索引 WebGL      更新时间:2023-09-26

我目前正在开发一个关于WebGL的网站。我和我的搭档想使用一个小而有趣的WebGL背景。

我们要做的是让一个盒子在画布上旋转。现在,它的原则已经起作用了。我们已经有一个旋转框,但是许多三角形使用错误的顶点来形成它们的三角形,导致下图:

三角形设置不良的旋转立方体。

所以,我们想:让我们使用索引来正确设置它。但是,这会导致框从画布上消失。它似乎不再在任何地方了。

我们的问题是:有人能明白为什么会这样吗?我们感觉我们非常接近,但这个微小的细节确实给我们带来了一些麻烦。

下面提供了我们使用的代码。任何其他反馈也总是受欢迎的。

当前程序

// ROTATING EXAMPLE
// The canvas that the GL environment will be using.
var canvas = null;
// The Graphics Library, aka: WebGL.
var openGL = null;
// The shader used by the graphics library.
var shaderProgram = null;
// Our matrices.
var modelViewMatrix;
var modelNormalMatrix;
var perspectiveMatrix;
var VertexBuffer;
var ColorBuffer;
var NormalBuffer;
var IndicesBuffer;
var vertexPositionAttribute;
var vertexColorAttribute;
var vertexNormalAttribute;
var squareRotation = 0.0;
var lastSquareUpdateTime = null;
// Encapsulation of the initialisation of WebGL.
function initWebGL(openGLCanvas) {
    var gl = null;
    // Attempt to build the context.
    try {
        gl = openGLCanvas.getContext("webgl", { premultipliedAlpha: false })
            || openGLCanvas.getContext("experimental-webgl", { premultipliedAlpha: false });
    }
    // Report back to use when some exception is thrown.
        catch (exception) {
            console.log("An error happened when trying to initialise the openGL environment. See the exception below for details.");
            console.log(exception);
            gl = null;
        }
    return gl;
}
function initialise(){      
    // Keep this variable local. We do not need it globally.
    var openGLCanvas = document.getElementById("openGLCanvas");
    openGL = initWebGL(openGLCanvas);
    if(openGL)
        {
            // The color to use to clear the screen.
            openGL.clearColor(0.9, 0.9, 0.9, 1.0);
            openGL.clearDepth(1.0);
            // Enables the depth testing.
            openGL.enable(openGL.DEPTH_TEST);
            // Closer things will overlap with things further away.
            openGL.depthFunc(openGL.LEQUAL);
            // Clear both the color as the depth buffer.
            openGL.clear(openGL.COLOR_BUFFER_BIT |openGL.DEPTH_BUFFER_BIT);
            openGL.viewport(0, 0, openGLCanvas.width, openGLCanvas.height);
            InitialiseShaders();
            InitialiseBuffers();
            setInterval(drawScene, 15);
            //drawScene();
        }
}
function InitialiseShaders()
{
    var fragmentShader = retrieveShader(openGL, "04fragment", "f", "shader");
    var vertexShader = retrieveShader(openGL, "04vertex", "v", "shader");
    //console.log(fragmentShader);
    //console.log(vertexShader);
    shaderProgram = openGL.createProgram();
    openGL.attachShader(shaderProgram, vertexShader);
    openGL.attachShader(shaderProgram, fragmentShader);
    openGL.linkProgram(shaderProgram);
    if(!openGL.getProgramParameter(shaderProgram, openGL.LINK_STATUS))
        {
            console.log("Something went wrong during the initialisation of the shader program. See below for extra information.");
            console.log(openGL.getProgramParameter(shaderProgram, openGL.LINK_STATUS));
        }
    openGL.useProgram(shaderProgram);
    vertexPositionAttribute = openGL.getAttribLocation(shaderProgram, "position");
    openGL.enableVertexAttribArray(vertexPositionAttribute);
    vertexColorAttribute = openGL.getAttribLocation(shaderProgram, "color");
    openGL.enableVertexAttribArray(vertexColorAttribute);
    vertexNormalAttribute = openGL.getAttribLocation(shaderProgram, "normal");
    openGL.enableVertexAttribArray(vertexNormalAttribute);
}
function retrieveShader(openGL, filename, type, filetype)
{       // Ensure the file type is always given.
    filetype = filetype || "txt";
        // Read out the source file.
    var source = "";    
    $.ajax({
        url: "Shaders/" + filename + "." + filetype,
        async: false,
        success: function(data) { source = data; }
    });
    console.info("Found source for the following filename: " + filename + ", of type: " + type + ".");
    console.info(source);
        // Check what type the shader should be.
        // If the type is unknown we can see the error report within the console.
    var shader;
    if(type == "f")
    { shader = openGL.createShader(openGL.FRAGMENT_SHADER);}
    else if (type == "v")
        { shader = openGL.createShader(openGL.VERTEX_SHADER);}
    else 
        {
            console.log("Unknown shader type. See below for extra information.");
            console.log(identification);
            console.log(shaderScript.type);
            return null;
        }
        // Attempt to compile the shader.
    openGL.shaderSource(shader, source);
    openGL.compileShader(shader);
        // Check whether or not there are any compilation errors.
        // If there are any, we'll be able to see the error report within the console.
    if(!openGL.getShaderParameter(shader, openGL.COMPILE_STATUS))
        {
            console.log("An error occured during the compilation of the shader. See below for extra information.");
            console.log(openGL.getShaderInfoLog(shader));
            return null;
        }
        // All is green. 
        // Lets start this baby up.
    return shader;
}
function InitialiseBuffers()
{
    bufferPosition();
    bufferColor();
    bufferNormal();
    bufferIndices();
}
function bufferIndices(){
    var indices = [
      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
    ];
    IndicesBuffer = openGL.createBuffer();
    openGL.bindBuffer(openGL.ELEMENT_ARRAY_BUFFER, IndicesBuffer);
    openGL.bufferData(openGL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), openGL.STATIC_DRAW);
}
function bufferPosition()
{
    // Vertex data for a 3D box.
    var vertices = [
      // 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
    ];
    // Create a buffer, prep it for filling and then fill it.
    VertexBuffer = openGL.createBuffer();
    openGL.bindBuffer(openGL.ARRAY_BUFFER, VertexBuffer);
    openGL.bufferData(openGL.ARRAY_BUFFER, new Float32Array(vertices), openGL.STATIC_DRAW);
}
function bufferColor()
{
    // Color data for a 3D Box.
    var colorPerFace = [
      [0.8,  0.9,  0.75,  1.0],    // Front face
      [0.8,  0.9,  0.75,  1.0],   // Back face
      [0.8,  0.9,  0.75,  1.0],    // Top face
      [0.8,  0.9,  0.75,  1.0],    // Bottom face
      [0.8,  0.9,  0.75,  1.0],    // Right face
      [0.8,  0.9,  0.75,  1.0],     // Left face
    ];
    var colors = [];
    // For each face.
    for (j=0; j<6; j++) {
      var c = colorPerFace[j];
        // generate a color for every vertex on that face.
      for (var i=0; i<4; i++) {
        colors = colors.concat(c);
      }
    }
    // Create a buffer, prep it and then fill it.
    ColorBuffer = openGL.createBuffer();
    openGL.bindBuffer(openGL.ARRAY_BUFFER, ColorBuffer);
    openGL.bufferData(openGL.ARRAY_BUFFER, new Float32Array(colors), openGL.STATIC_DRAW);
}
function bufferNormal()
{
    var vertexNormals = [
      // Front
       0.0,  0.0,  1.0,
       0.0,  0.0,  1.0,
       0.0,  0.0,  1.0,
       0.0,  0.0,  1.0,
      // Back
       0.0,  0.0, -1.0,
       0.0,  0.0, -1.0,
       0.0,  0.0, -1.0,
       0.0,  0.0, -1.0,
      // Top
       0.0,  1.0,  0.0,
       0.0,  1.0,  0.0,
       0.0,  1.0,  0.0,
       0.0,  1.0,  0.0,
      // Bottom
       0.0, -1.0,  0.0,
       0.0, -1.0,  0.0,
       0.0, -1.0,  0.0,
       0.0, -1.0,  0.0,
      // Right
       1.0,  0.0,  0.0,
       1.0,  0.0,  0.0,
       1.0,  0.0,  0.0,
       1.0,  0.0,  0.0,
      // Left
      -1.0,  0.0,  0.0,
      -1.0,  0.0,  0.0,
      -1.0,  0.0,  0.0,
      -1.0,  0.0,  0.0
    ];
    NormalBuffer = openGL.createBuffer();
    openGL.bindBuffer(openGL.ARRAY_BUFFER, NormalBuffer);
    openGL.bufferData(openGL.ARRAY_BUFFER, new Float32Array(vertexNormals), openGL.STATIC_DRAW);
}
function drawScene()
{           
    openGL.clear(openGL.GL_COLOR_BUFFER_BIT | openGL.DEPTH_BUFFER_BIT);
    updateScene();
    // Clear both the color as the depth buffer.

    openGLBindings();
    openGL.drawElements(openGL.TRIANGLE, 24, openGL.UNSIGNED_SHORT, 0);
    openGL.drawArrays(openGL.TRIANGLE_STRIP, 0, 24);
    console.log("Scene made!");
}
function updateScene()
{
    var currentTime = Date.now();
    if (lastSquareUpdateTime) {
      var delta = currentTime - lastSquareUpdateTime;
      squareRotation += (30 * delta) / 2000.0;
    }
    lastSquareUpdateTime = currentTime;

    // Calculate the matrices
    modelViewMatrix = Matrix.I(4);
    var inRadians = squareRotation * Math.PI / 180;
    modelViewMatrix = modelViewMatrix.x(Matrix.Translation($V([0.0, 0.0, -12.0])).ensure4x4());
    modelViewMatrix = modelViewMatrix.x(Matrix.Rotation(inRadians, $V([1, 1, 0])).ensure4x4());
    modelNormalMatrix = modelViewMatrix.inverse();
    modelNormalMatrix = modelNormalMatrix.transpose();
    perspectiveMatrix = makePerspective(45, 520 / 520, 0.1, 100.0);

    // Send the matrices to the GPU.
    var mvUniform = openGL.getUniformLocation(shaderProgram, "matrixModelView");
    openGL.uniformMatrix4fv(mvUniform, 
                            false, 
                            new Float32Array(modelViewMatrix.flatten()));
    var pUniform = openGL.getUniformLocation(shaderProgram, "matrixPerspective");
    openGL.uniformMatrix4fv(pUniform, 
                            false, 
                            new Float32Array(perspectiveMatrix.flatten()));
    var nUniform = openGL.getUniformLocation(shaderProgram, "matrixNormal");
    openGL.uniformMatrix4fv(nUniform, 
                            false, 
                            new Float32Array(modelNormalMatrix.flatten()));
}
function openGLBindings()
{   
    // Send the data to the GPU.
    openGL.bindBuffer(openGL.ARRAY_BUFFER, VertexBuffer);
    openGL.vertexAttribPointer(vertexPositionAttribute,     // Where to bind it to
                               3,                           // The number of data per set.
                               openGL.FLOAT,                // The type of data
                               false,                       // Normalisation (or not).
                               0,                           // Stride
                               0);                          // Offset
    openGL.bindBuffer(openGL.ARRAY_BUFFER, ColorBuffer);
    openGL.vertexAttribPointer(vertexColorAttribute,        // Where to bind it to
                               4,                           // The number of data per set.
                               openGL.FLOAT,                    // The type of data.
                               false,                       // Normalisation (or not).
                               0,                           // Stride
                               0);                          // Offset
    openGL.bindBuffer(openGL.ARRAY_BUFFER, NormalBuffer);
    openGL.vertexAttribPointer(vertexNormalAttribute,       // Where to bind it to
                               3,                           // The number of data per set.
                               openGL.FLOAT,                // The type of data.
                               false,                       // Normalisation (or not).
                               0,                           // Stride
                               0);                          // Offset
    openGL.bindBuffer(openGL.ELEMENT_ARRAY_BUFFER, IndicesBuffer);
}

片段着色器

varying lowp vec4 fColor;
void main(void) {
    gl_FragColor = fColor;
}

顶点着色器

attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 matrixModelView;
uniform mat4 matrixPerspective;
uniform mat4 matrixNormal;
varying lowp vec4 fColor;
void main(void) {
    gl_Position = matrixPerspective * matrixModelView * vec4(position, 1.0);
    highp vec3 directionalVector = vec3(1.0, 0.85, 0.6);
    highp vec4 transformedNormal = matrixNormal * vec4(normal, 1.0);
    highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
    fColor = color * directional;
}

快速浏览代码后,我找到了以下行

openGL.drawElements(openGL.TRIANGLE, 24, openGL.UNSIGNED_SHORT, 0);

相当可疑。您总共有 6(四边形)* 2(三角形/四边形)* 3(顶点/三角形)= 36 个索引,而不是 24 个。此外,您的缓冲区索引包含 36 个整数。

但是,紧接着的行

openGL.drawArrays(openGL.TRIANGLE_STRIP, 0, 24);

显示您正在将四边形绘制为三角形条。绘制三角形时需要保持一致,即将它们全部渲染为 TRIANGLE 或 TRIANGLE_STRIP。如果要绘制为TRIANGLE_STRIP,则定义

 var vertices = [
  // 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,
........
];

不是定义四边形三角形行程的正确顺序。正确的方法应该是

 var vertices = [
  // 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,
...
]

同样的事情也适用于其他面孔的定义。