我一直在研究用于教育追求的引擎的开端,并且遇到了一个我认为我理解的 OpenGL 概念,但是我无法解释我一直在观察的行为。问题在于深度缓冲区。另外,请理解我已经解决了这个问题,并且在我的帖子末尾我将解释是什么解决了这个问题,但是我不明白为什么通过我的解决方案解决了这个问题。首先我初始化 GLUT & GLEW:
//Initialize openGL
glutInit(&argc, argv);
//Set display mode and window attributes
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
//Size and position attributes can be found in constants.h
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_XPOS, WINDOW_YPOS);
//Create window
glutCreateWindow("Gallagher");
// Initialize GLEW
glewExperimental = true;
glewInit();
//Initialize Graphics program
Initialize();
然后我初始化我的程序(为了可读性和缺乏相关性而忽略了段):
//Remove cursor
glutSetCursor(GLUT_CURSOR_NONE);
//Enable depth buffering
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
glDepthRange(0.0f, 1.0f)
//Set back color
glClearColor(0.0,0.0,0.0,1.0);
//Set scale and dimensions of orthographic viewport
//These values can be found in constants.h
//Program uses a right handed coordinate system.
glOrtho(X_LEFT, X_RIGHT, Y_DOWN, Y_UP, Z_NEAR, Z_FAR);
除此之外的任何事情都只包括初始化各种引擎组件,加载 .obj 文件,初始化 ModularGameObject 类的实例,将网格附加到它们,没有涉及任何相关的 glut/glew。但是,在我继续之前,指定以下值可能很重要:
X_LEFT = -1000;
X_RIGHT = 1000;
Y_DOWN = -1000;
Y_UP = 1000;
Z_NEAR = -0.1;
Z_FAR = -1000;
这导致我的视口遵循右手坐标系。似乎与问题有关的最后一段代码是我的顶点着色器:
#version 330 core
//Position of vertices in attribute 0
layout(location = 0) in vec4 _vertexPosition;
//Vertex Normals in attribute 1
layout(location = 1) in vec4 _vertexNormal;
//Model transformations
//Uniform location of model transformation matrix
uniform mat4 _modelTransformation;
//Uniform location of camera transformations
//Camera transformation matrix
uniform mat4 _cameraTransformation;
//Camera perspective matrix
uniform mat4 _cameraPerspective;
//Uniform location of inverse screen dimensions
//This is used because GLSL normalizes viewport from -1 to 1
//So any vector representing a position in screen space must be multiplied by this vector before display
uniform vec4 _inverseScreenDimensions;
//Output variables
//Indicates whether a vertex is valid or not, non valid vertices will not be drawn.
flat out int _valid; // 0 = valid vertex
//Normal to be sent to fragment shader
smooth out vec4 _normal;
void main()
{
//Initiate transformation pipeline
//Transform to world space
vec4 vertexInWorldSpace = vec4(_modelTransformation *_vertexPosition);
//Transform to camera space
vec4 vertexInCameraSpace = vec4(_cameraTransformation * vertexInWorldSpace);
//Project to screen space
vec4 vertexInScreenSpace = vec4(_cameraPerspective * vertexInCameraSpace);
//Transform to device coordinates and store
vec4 vertexInDeviceSpace = vec4(_inverseScreenDimensions * vertexInScreenSpace);
//Store transformed vertex
gl_Position = vertexInScreenSpace;
}
此代码会导致正确完成所有转换和正常计算(不包括在内),但是我的模型的每一面都在不断地争取超越所有其他人。唯一一次我没有问题是站在第一个正在绘制的模型里面,然后什么都没有闪烁,我可以看到 Suzanne 的头部内部,就像我应该能够看到的那样。
在尝试了我能想到的任何事情几周后,我终于找到了一个解决方案,该解决方案仅涉及更改/添加两行代码。首先,我将这一行添加到我的顶点着色器的主函数的末尾:
gl_Position.z = 0.0001+vertexInScreenSpace.z;
这行代码的添加导致 z-fighting 的每一点都消失了,除了现在深度缓冲区完全向后,更远的顶点被可靠地绘制在前面的顶点之上。这是我的第一个问题,为什么这行代码会导致更可靠的行为?
现在我有了可靠的行为并且不再进行深度战斗,这是一个颠倒绘制顺序的问题,因此我将对 glDepthRange 的调用更改为以下内容:
glDepthRange(1.0f, 0.0f);
我假设 glDepthRange(0.0f, 1.0f) 会导致靠近我的 Z_NEAR (-0.1) 的对象更接近 0,而靠近我的 Z_FAR(-1000) 的对象更接近 1。然后,让我的设置为 GL_LESS 的深度测试非常有意义,事实上,无论我的 Z_NEAR 和 Z_FAR 是什么,都应该是这种情况,因为 glDepthRange 映射值的方式,如果我没记错的话。
不过我一定是误会了,因为这条线的变化意味着离我更近的对象将在深度缓冲区中存储一个更接近 1 的值,而更远的对象将具有 0 的值,从而呈现向后的绘制顺序——但它确实像一个魅力。
如果有人能指出我的假设为什么是错误的方向,以及我可能没有考虑到我对 glsl 和深度缓冲的理解的方向。在我完全理解它的基础的功能之前,我宁愿不继续我的引擎的进步。
编辑: 我的_cameraPerspective矩阵的内容如下:透视矩阵图
AspectX 0 0 0
0 AspectY 0 0
0 0 1 0
0 0 1/focalLength 0
其中 AspectX 为 16,AspectY 为 9。焦距默认为 70,但是添加了控件以在运行时更改此设置。
derhass 指出,这并不能解释着色器如何考虑传递给 glOrtho() 的任何信息。由于不使用标准管道和矩阵堆栈,视口尺寸与 _inverseScreenDimensions 一起考虑。这是一个包含 [1/X_RIGHT, 1/Y_UP, 1/Z_Far, 1] 的 vec4。或者因为缺少变量名,[1/1000, 1/1000, -1/1000, 1]。
在我的顶点着色器中将屏幕坐标向量乘以它会导致 X 值介于 -1 和 1 之间,Y 值介于 -1 和 1 之间,Z 值介于 0 和 1 之间(如果对象在它的相机前面一个负 z 坐标开始),和一个 1 的 W。
如果我没记错的话,这将是达到“设备坐标”的最后一步,然后是绘制网格。
请记住最初的问题:我知道这不是简化的,我知道我没有为此使用 GLM 或所有最常用的库,但是我的问题不是“嘿,伙计们解决这个问题!” 我的问题是:为什么我所做的更改会解决这个问题?