简体   繁体   中英

Out of order Z-buffer in OpenGL

I am working on an OpenGL application in C++. It is a scene in space with large distances and geometries, therefore I am using a logarithmic depth scale. I have included the basics of the code below. Essentially, I cannot seem to get depth testing correct. The below picture should have the cube appearing before the grey sphere. Additionally, you can see that the triangles on the larger sphere in the back are incorrectly depth ordered.

乙=

Here is how I initialize the depth buffer in my application (I am using SDL2 and Glew). Am I missing something?

(Some additional information, the blue sphere is 650000 units in diameter, the cube is 100 units in width, and the grey sphere is 1000 units in diameter)

#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);
}

Here is the vertex shader:

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;
}

And finally, the fragment shader:

#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
}

It appears that your vertex shader is setting logz to a constant value then passing it to the fragment shader, which then assigns it to gl_FragDepth . Therefore all your fragments have roughly the same depth.

I would try removing all the special logarithm math from the vertex shader and instead do it all in the fragment shader. OpenGL performs perspective-correct interpolation, so expects the vertex shader's outgoing Z to be clipped between -W and +W.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM