OpenGL中的乱序Z缓冲区

Posted

技术标签:

【中文标题】OpenGL中的乱序Z缓冲区【英文标题】:Out of order Z-buffer in OpenGL 【发布时间】:2019-12-11 13:57:56 【问题描述】:

我正在开发 C++ 中的 OpenGL 应用程序。这是一个具有大距离和几何形状的空间场景,因此我使用对数深度刻度。我已经包含了下面代码的基础知识。从本质上讲,我似乎无法正确进行深度测试。下图应该有立方体出现在灰色球体之前。此外,您可以看到背面较大球体上的三角形的深度排序不正确。

这是我在应用程序中初始化深度缓冲区的方法(我使用的是 SDL2 和 Glew)。我错过了什么吗?

(一些附加信息,蓝色球体直径650000单位,立方体宽100单位,灰色球体直径1000单位)

#include <iostream.h>
#include <GL/glew.h>
#include <SDL.h>
#include <SDL_opengl.h>
#include <glm/mat4x4.hpp>

int main() 
  // Set up the window
  SDL_Init(SDL_INIT_VIDEO);
  SDL_GL_SetAttribute (SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
  SDL_GL_SetAttribute (SDL_GL_CONTEXT_MAJOR_VERSION, 3); //OpenGL 3+
  SDL_GL_SetAttribute (SDL_GL_CONTEXT_MINOR_VERSION, 3); //OpenGL 3.3
  SDL_GL_SetAttribute (SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); //OpenGL core profile

  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

  displayWindow = SDL_CreateWindow(GAME_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)screenWidth, (int)screenHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);

  SDL_GLContext context = SDL_GL_CreateContext(displayWindow);
  // SDL_GL_MakeCurrent(displayWindow, context);

  SDL_GL_SetSwapInterval( 1 );

  glewExperimental = GL_TRUE; 
  GLenum glewError = glewInit();
  if( glewError != GLEW_OK ) 
    std::cout << "Error Initializing GLEW" << std::endl;
    exit(1);
  

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  // Enable Depth Buffering
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

这是顶点着色器:

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;

uniform vec3 lightPosition;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix; 
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;

out vec3 Normal_camera;
out vec3 LightDirection_camera;
out vec3 Position_global;
out vec3 EyeDirection_camera;
out float logz;

void main()

  // vec3 lightPosition = vec3(1, 1, 0);
  // Position of the vertex, in worldspace : modelMatrix * position
  Position_global = (modelMatrix * vec4(position, 1)).xyz;

  // Vector that goes from the vertex to the camera, in camera space.
  // In camera space, the camera is at the origin (0,0,0).
  vec3 Position_camera = (viewMatrix * vec4(Position_global, 1)).xyz;
  EyeDirection_camera = (vec4(0, 0, 0, 0) - vec4(Position_camera, 1)).xyz;

  // Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity.
  vec3 LightPosition_camera = (viewMatrix * vec4(lightPosition, 1)).xyz;
  LightDirection_camera = LightPosition_camera + EyeDirection_camera;

  // Normal of the the vertex, in camera space
  Normal_camera = (modelMatrix * vec4(normal, 0)).xyz;
  // Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not

  gl_Position = projectionMatrix * vec4(Position_camera, 1);
  logz = 0.0f;
  float ZNEAR = 0.0001;
    float ZFAR = 1000000.0;
    float FCOEF = 2.0 / log2(ZFAR + 1.0);
  logz = FCOEF;
    gl_Position.z = log2(max(ZNEAR, 1.0 + gl_Position.w)) * FCOEF - 1.0;

最后,片段着色器:

#version 330 core
uniform vec4 color;
uniform float lightPower;
uniform vec3 lightPosition;
uniform vec3 lightColor;

in vec3 Normal_camera;
in vec3 LightDirection_camera;
in vec3 Position_global;
in vec3 EyeDirection_camera;
in float logz;

out vec4 FragColor;

void main() 
  vec3 n = normalize(Normal_camera);
  vec3 l = normalize(lightPosition - Position_global);
  float cosTheta = clamp(dot(n, l), 0.0, 1.0);
  float distance = length( lightPosition - Position_global );

  // Eye vector (towards the camera)
    vec3 E = normalize(EyeDirection_camera);
    // Direction in which the triangle reflects the light
    vec3 R = reflect(-l,n);
    // Cosine of the angle between the Eye vector and the Reflect vector,
    // clamped to 0
    //  - Looking into the reflection -> 1
    //  - Looking elsewhere -> < 1
    float cosAlpha = clamp( dot( E,R ), 0.0, 1.0);
  float specularStrength = 0.3;
  float shininess = 16.0;
  float ambientStrength = 0.3;
  float sqDistance = distance * distance;

  vec3 MaterialAmbientColor = ambientStrength * lightColor;
  //* lightPower / sqDistance;  
  vec3 MaterialDiffuseColor = lightColor * lightPower * cosTheta / sqDistance;
  vec3 MaterialSpecularColor = specularStrength * lightColor * lightPower * pow(cosAlpha, shininess) / sqDistance;

  vec4 c = color * vec4(MaterialAmbientColor + MaterialDiffuseColor + MaterialSpecularColor, 1);
  FragColor = c;
  gl_FragDepth = logz;

  // Later on, I assign glm::perspective(radians(45.0f), SCREEN_HEIGHT/SCREEN_WIDTH, 1.0f, 1000.0f) to the perspective uniform

【问题讨论】:

【参考方案1】:

您的顶点着色器似乎将logz 设置为一个常量值,然后将其传递给片段着色器,然后将其分配给gl_FragDepth。因此,您所有的片段都具有大致相同的深度。

我会尝试从顶点着色器中删除所有特殊的对数数学,而是在片段着色器中完成所有操作。 OpenGL 执行透视正确插值,因此预计顶点着色器的传出 Z 将被剪裁在 -W 和 +W 之间。

【讨论】:

谢谢您,阅读更多有关该主题的信息并根据您的建议调整着色器。跟进:这将如何影响更复杂模型的性能? 任何时候您在片段着色器中更改gl_FragDepth,您都可能导致 GPU 运行缓慢。但是,有很多因素,所以很难回答你的问题。

以上是关于OpenGL中的乱序Z缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

OpenGl和闪烁

OpenGL中的深度深度缓存深度测试及保存成图片

◮OpenGL-深度测试

使用System.out.printf()输出日志重定向到文件后显示混乱问题

防止java中的乱序执行

有没有办法在带有 Java 的 Android 的 OpenGL ES 2.0 中使用顶点缓冲区中的对象?