渲染中的 OpenGL 闪烁问题

OpenGL flickering issue in rendering

我有一个 OpenGL 渲染问题,我认为这是由于 Z-Buffer 的问题。
我有一个代码来渲染一组点,它们的大小取决于与相机的距离。因此,更大的点意味着更接近相机。此外,在以下快照中,颜色反映了片段的 z 缓冲区。



怎么看摄像头附近有个大点
然而,一些帧之后,相同的点被渲染在更远的点后面。



这些是我在渲染点之前调用的函数:

  glClearDepth(1.0f);

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  glEnable(GL_DEPTH_TEST);
  
  glDepthFunc(GL_LEQUAL);

这是顶点着色器:

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec4 color;

// uniform variable
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform float pointSize;

out vec4 fragColor;

void main() {
  
  gl_Position = projection * view * model * vec4(position, 1.0f);
  
  vec3 posEye = vec3(view * model * vec4(position, 1.0f));
  
  float Z = length(posEye);
  
  gl_PointSize = pointSize / Z;
      
  fragColor = color;

}

这是片段着色器

#version 330 core

in vec4 fragColor;

out vec4 outColor;

void main() {
    
  vec2 cxy = 2.0 * gl_PointCoord - 1.0;

  float r = dot(cxy, cxy);

  if(r > 1.0) discard;

  // calculate lighting
  vec3 pos = vec3(cxy.x,cxy.y,sqrt(1.0-r));
  vec3 lightDir = vec3(0.577, 0.577, 0.577);
  float diffuse = max(0.0, dot(lightDir, pos));

  float alpha = 1.0;
  float delta = fwidth(r);
  alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r);

  outColor = fragColor * alpha * diffuse;

}

更新

看起来问题出在近平面和远平面的定义上。
我不明白哪些是我应该使用的最佳值。

这是我用来创建投影矩阵的函数

glm::perspective(glm::radians(fov), width/(float)height, zNear, zFar);

其中 winth=1600 height=1200 fov=45

当事情不起作用时,zNear 设置为零,zFar 设置为距点云重心的最远点的距离加倍,即在我的例子中为 1.844

如果我将近裁剪平面从 0 移动到 0.1,闪烁似乎得到了解决。然而,我之前看到的远处的物体消失了。所以我也将远平面更改为 10,一切似乎都有效。不幸的是,我不明白为什么我以前使用的值不好。

正如已经更新的那样,当在投影矩阵中选择错误的近距和远距窗格时,这个问题称为 Z 冲突。如果它们离您的对象太远,则只剩下非常离散的 z 值。然后绘图由着色器中的调用顺序和处理顺序确定,因为z没有区别。如果有人暗示需要先做什么,就那样画。一旦设置了渲染调用,就没有正确的方法来确定哪个处理器首先获取对象,并且您会看到闪烁。

所以请更新您建立投影矩阵的方式。最佳实践:查看需要渲染的对象并确定一个大致的边界框,将其设为边界球,center-radius 是近窗格中的一个点,center+radius 是远窗格中的一个点。完成!

更新

调查

glm::perspective(glm::radians(fov), width/(float)height, zNear, zFar);

给出了 z 的唯一影响:(归一化之前):

Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);

除了zFar!=zNear之外的意思,也应该避免将zNear设置为零。这意味着 z 没有差异,所以它必须 Flicker。然后我会假设你没有对你的投影矩阵应用一些变换,最好不要。如果您的所有对象都位于坐标中心周围的 space 中,这意味着也位于投影的中心,请将它们移动到投影前面作为最后一步。因此,不要在 projection/view 矩阵上应用一些转换,而是在您的对象矩阵上应用一些转换,以避免出现这种格式错误的投影 space。例如。设置为接近 1,并在场景中移动该数量的所有对象。