WebGL 只为最远的边着色,而不是最近的边

WebGL only colors the furthest side, instead of nearest side

我正在使用 WebGL 绘制一个简单的平截头几何体。我试着给前面上色 (小)和背(大)面红色,侧面白色。

这是它的样子。请注意,较小的表面离眼睛较近,较大的表面离眼睛较远。不要被这个图像所迷惑。看起来着色器选择为离眼睛最远的侧面着色,而忽略前面朝上的面孔。

我应该如何使它正常工作?

以下是我的着色器设置: 深度缓冲区在初始化期间被清除并且从未使用过。

function init(){
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  gl = getWebGLContext(canvas);
  if (!gl) {
    console.log("lib1.js: init() failed to get WebGL rendering context 'gl'\n");
    console.log("from the HTML-5 Canvas object named 'canvas'!\n\n");
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('lib1.js: init() failed to intialize shaders.');
    return;
  }

  bufferSetup(gl);

  // Set the background-clearing color and enable the depth test
  gl.clearColor(0.0, 0.0, 0.0, 1.0);  // black!
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);   // draw the back side of triangles
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}

感谢 Denis 指出这一点!我还尝试打开和关闭 gl.CULL_FACE,并尝试了 gl.BACKgl.FRONT。 None 个正常工作。

  gl.disable(gl.CULL_FACE);
  // gl.cullFace(gl.BACK)

我们知道CVV坐标系,非变换绘图轴 (CVV == 规范视图体积,我们在屏幕上描绘的 +/-1 立方体体积) 具有右手坐标,原点位于 CVV 立方体的中心,x 向右增加,y 向上增加,z 向外增加, 朝向你的眼睛。但是对于我们来说,CVV 中具有更大、更正 Z 深度的片段没有必要被绘制为 'nearer',并且在同一屏幕位置具有更小、更负 Z 的片段被重叠。为什么?

WebGL(以及 OpenGL-ES 和 OpenGL)在 3D 绘图的其他方面是明智的方法有一个历史怪癖:它将 'depth' 定义为 0.0 和 1.0 之间的计算值,它不仅仅是CVV 坐标中的 z 值。

默认情况下,WebGL 将深度定义为:

  • 0.0 对于 'least' 深度,最近的屏幕或相机; (CVV 中的z = +1
  • 1.0 'most' 深度,离屏幕或相机最远; (CVV 中的z = -1

虽然 GPU 会自动计算深度,但正确的结果需要由 3D 相机投影矩阵转换的顶点来模拟相机镜头。有时,如果我们没有定义或使用投影矩阵,GPU 深度计算会减少到:

depth = 0.5 + 0.5*(gl_position.z / gl.position.w);

换句话说,没有这个'camera-projection'矩阵,GPU计算 'depth' backwards -- 顶点 at z = -1 在深度应该为 1 时获得 0。有关 GPU 如何计算深度的更多详细信息,请参阅:

知道这一点,我们可以通过以下任一方式轻松解决此问题:

  1. 我们可以编写始终设置第一个 ModelMatrix 变换的代码 改变 z 的符号,像这样:myMatrix.setScale(1,1,-1); 然后我们计算的所有 z 值都将反转符号。 (当结合复杂的场景图时,这有点混乱......)。
  2. 我们可以保留 z 值不变,而是告诉 WebGL 对深度缓冲区的使用应用不同的规则;这是一个更明智的解决方案。尝试添加这些代码行
gl.enable(gl.DEPTH_TEST); // enabled by default, but let's be SURE.
gl.clearDepth(0.0);       // each time we 'clear' our depth buffer, set all
                          // pixel depths to 0.0  (1.0 is DEFAULT)
gl.depthFunc(gl.GREATER); // gl.LESS is DEFAULT; reverse it!
                          // draw a pixel only if its depth value is GREATER
                          // than the depth buffer's stored value.

感谢我的计算机图形学教授 - John E. Tumblin