OpenGL 是按内部或外部查看器的顺序排列的吗?

Are OpenGL ordered as viewed by an internal or external viewer?

我编写了一个显示旋转立方体的 WebGL 程序。从 外侧 看,索引按逆时针顺序排列。然而,当我决定启用剔除面孔时,我理解为“正面”的面孔被剔除,立方体似乎被挖空了。我可以通过告诉 WebGL 剔除“正面”面孔来解决这个问题。

我声明立方体的顶点位置、纹理坐标和索引如下:image or text.

我的问题是:我是否应该在 形状内按逆时针顺序声明点?如果不是,为什么我使用的索引是倒着的?

下面是这个程序(已经是最小可复现的例子)

const cubeVertexPositions = [
  // Front
  -1, 1, -1,
  -1, -1, -1,
  1, -1, -1,
  1, 1, -1,

  // Back
  1, 1, 1,
  1, -1, 1,
  -1, -1, 1,
  -1, 1, 1,

  // Left
  -1, 1, 1,
  -1, -1, 1,
  -1, -1, -1,
  -1, 1, -1,

  // Right
  1, 1, -1,
  1, -1, -1,
  1, -1, 1,
  1, 1, 1,

  // Top
  -1, 1, 1,
  -1, 1, -1,
  1, 1, -1,
  1, 1, 1,

  // Bottom
  -1, -1, -1,
  -1, -1, 1,
  1, -1, 1,
  1, -1, -1
];

const cubeTextureCoordinates = [
  // Front
  0, 1,
  0, 0,
  1, 0,
  1, 1,

  // Back
  0, 1,
  0, 0,
  1, 0,
  1, 1,

  // Left
  0, 1,
  0, 0,
  1, 0,
  1, 1,

  // Right
  0, 1,
  0, 0,
  1, 0,
  1, 1,

  // Bottom
  0, 1,
  0, 0,
  1, 0,
  1, 1,

  // Top
  0, 1,
  0, 0,
  1, 0,
  1, 1
];

const cubeIndices = [
  // Front
  0, 1, 2,
  2, 3, 0,

  // Back
  4, 5, 6,
  6, 7, 4,

  // Left
  8, 9, 10,
  10, 11, 8,

  // Right
  12, 13, 14,
  14, 15, 12,

  // Top
  16, 17, 18,
  18, 19, 16,

  // Bottom
  20, 21, 22,
  22, 23, 20
];

// Vertex shader source code.
const vertexShaderSrc = `#version 300 es
in vec4 a_position;
in vec2 a_texcoord;

out vec2 v_texcoord;

uniform mat4 u_matrix;

void main() {
    gl_Position = u_matrix * a_position;

    v_texcoord = a_texcoord;
}`;

// Fragment shader source code.
const fragmentShaderSrc = `#version 300 es
precision highp float;

in vec2 v_texcoord;

uniform sampler2D u_texture;

out vec4 outColor;

void main() {
    outColor = texture(u_texture, v_texcoord);
}`;

// Variables for controlling the cube.
let cubeTranslation = new Vector(0, 0, 0);
let cubeRotation = new Vector(0, 0, 0);
let cubeAngularVelocity = new Vector(0, 1, 0);
let cubeScale = new Vector(1, 1, 1);
let cubeHue = new Vector(1, 1, 1, 1);
let fov = 60;
let near = 1;
let far = 200;
let lookAt = cubeTranslation;
let cameraTranslaton = new Vector(0, 0, 5);

// Initialization instructions.
onload = () => {
  // Find the canvas and WebGL2 context.
  const canvas = document.querySelector('#canvas');
  const gl = canvas.getContext('webgl2');

  // Create a WebGL2 program using the vertex and fragment shader source code above.
  const programInfo = new ProgramInfo(gl, new ShaderInfo(gl, gl.VERTEX_SHADER, vertexShaderSrc), new ShaderInfo(gl, gl.FRAGMENT_SHADER, fragmentShaderSrc));

  // Cube vertex positions buffer.
  const cubeVerticesBufferInfo = new BufferInfo(gl, new Float32Array(cubeVertexPositions));

  // Cube texture coordinates buffer.
  const cubeTexcoordsBufferInfo = new BufferInfo(gl, new Float32Array(cubeTextureCoordinates));

  // Vertex Array Object for the cube.
  const vao = new VAOInfo(gl, programInfo);
  vao.addAttribute(new AttributeInfo('a_position', cubeVerticesBufferInfo));
  vao.addAttribute(new AttributeInfo('a_texcoord', cubeTexcoordsBufferInfo, 2));
  vao.setIndices(new Uint8Array(cubeIndices));

  // A simple texture to show the edges of the cube.
  const outlineTextureInfo = new TextureInfo(gl);
  outlineTextureInfo.setColor(new Uint8Array([255, 0, 255, 255])); // Temporary color while image is loading.
  outlineTextureInfo.loadImage(imageBase64);

  // Render time instructions.
  const render = (time) => {
    requestAnimationFrame(render);

    // Set global state.
    UmbraGL.resizeCanvasToDisplaySize(canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.CULL_FACE);
    // gl.cullFace(gl.FRONT); // Uncomment this line to fix the cube.

    // Animate.
    cubeRotation.operate(cubeAngularVelocity);

    // Draw.
    gl.useProgram(programInfo.program);
    programInfo.uniforms['u_matrix'].setter([...new Matrix()
      .perspective(fov, gl.canvas.clientWidth / gl.canvas.clientHeight, near, far)
      .multiply(new Matrix().lookAt(cameraTranslaton, lookAt).invert())
      .translate(...cubeTranslation)
      .rotate(...cubeRotation)
    ]);
    programInfo.uniforms['u_texture'].setter(outlineTextureInfo.texture);
    vao.draw();
  };
  requestAnimationFrame(render);
};

// Base64 version of the image. This is at the bottom so that it doesn't get in the way of the rest of the code.
const imageBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TS0VaRFpExCFDdbIgKuIoVSyChdJWaNXB5NIvaNKQtLg4Cq4FBz8Wqw4uzro6uAqC4AeIm5uToouU+L+k0CLGg+N+vLv3uHsHCM0KU82eCUDVakYqHhOzuVXR/wofQhjAIIISM/VEejED1/F1Dw9f76I8y/3cnyOo5E0GeETiOaYbNeIN4pnNms55nzjMSpJCfE48btAFiR+5Ljv8xrlos8Azw0YmNU8cJhaLXSx3MSsZKvE0cURRNcoXsg4rnLc4q5U6a9+TvzCQ11bSXKc5gjiWkEASImTUUUYFNURp1UgxkaL9mIt/2PYnySWTqwxGjgVUoUKy/eB/8LtbszA16SQFYoDvxbI+RgH/LtBqWNb3sWW1TgDvM3CldfzVJjD7SXqjo0WOgP5t4OK6o8l7wOUOMPSkS4ZkS16aQqEAvJ/RN+WA0C3Qt+b01t7H6QOQoa6Wb4CDQ2CsSNnrLu/u7e7t3zPt/n4ASt1ylxIM+ngAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQflBRMRGRZyIYKeAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAEaJJREFUeNrt2bERwDAIBMHHZUvF4yIU2CN2S/iIGxIAAGCMStLdhgAAgOvtvR8rAADAHAIAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAAAQACYAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAACAc5Wkuw0BAAD3X/9VPgAAADCIAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAAASACQAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAMA3AbDWsgIAAEwJABMAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAABAAJgAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAAAgAAABAAAAAAAIAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAAAQAAAAgAAAAAAEAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAAAQAAAAgAAAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAIAAAAQAAAAAACAAAAEAAAAIAAAAAABAAAACAAAAAAAQAAAAgAAABAAAAAAAIAAAAEAAAAIAAAAAABAAAACAAAAEAAAAAAAgAAABAAAACAAAAAAAQAAAAgAAAAAAEAAAACAAAAEAAAAIAAAAAABAAAAAAAAPAfLyiBENdcBN3eAAAAAElFTkSuQmCC';
body {
  margin: 0;
}

canvas#canvas {
  width: 100%;
  height: 100%;
}
<script src="https://unpkg.com/umbra-math@1.0.0/index.js"></script>
<script src="https://unpkg.com/umbra-gl@1.0.0/index.js"></script>

<canvas id="canvas"></canvas>

您说“从外面看,索引是逆时针排列的。”

不,他们不是。第一个三角形的索引是 0, 1, 2,顶点是 (-1, 1, -1), (-1, -1, -1), (1, -1, -1).

    0
y    +
^    | \
|    |   \
|    +-----+
    1       2

      ----> x

在右手系统 (Right-hand rule) 中,z 轴指向视图。因此这是背面的三角形。

当你从后面看三角形时,你可以清楚地看到缠绕顺序是顺时针的

        0
       +
     / |
   /   |
 +-----+
2       1