OpenGL 中的深度测试默认是反转的,还是我弄错了?
我已经用 OpenGL 玩了一整周或相当的时间了。 继 2D 之后,我现在正在尝试 3D。我想重现您可以在 http://johnnylee.net/projects/ 上的第三个视频中看到的 3D 场景Wii/。
我很难让一切在纹理和深度上正常工作。
我最近遇到了两个在视觉上具有相同影响的问题:
- 其中一个问题是,使用我在 2D 中找到的技术,纹理在 3D 中不能很好地融合。
- 一种物体出现在顶部上方的底部。 就像这里暴露的问题一样: OpenGL 中的深度缓冲区
我已经解决了这两个问题,但我想知道我是否做对了,尤其是第二点。
对于第一个,我想我已经明白了。 我有一个圆形目标的图像,其中圆盘以外的任何东西都有 alpha 值。它在 OpenGL 中加载得很好。 它后面的一些(由于我的 z 排序问题)其他目标被我用来绘制它的自然方形四边形的透明区域隐藏。
其原因是,对于深度缓冲区,纹理的每个部分都被假定为完全不透明。 使用 glEnable(GL_ALPHA_TEST)
和 glAlphaFunc(GL_GREATER, 0.5f)
测试使纹理的 Alpha 层充当每像素(布尔)不透明度指示器,并且因此使混合变得毫无用处(因为我的图像具有布尔透明度)。
补充问题:顺便问一下,是否有办法为 alpha 测试指定与用于混合的 alpha 层不同的源?
第二,我已经找到了解决我的问题的方法。 在清除颜色和深度缓冲区之前,我已将默认深度设置为 0 glClearDepth(0.0f)
,并且我使用了“更大”深度函数 glDepthFunc(GL_GREATER)
代码>.
在我看来奇怪的是,深度为 1.0,而深度函数默认为“less”GL_LESS
。 我基本上将其反转,以便我的对象不会显示反转...
我在任何地方都没有见过这样的黑客,但另一方面,我也没有看到任何地方的对象以错误的顺序系统地绘制,不管我按什么顺序绘制它们!!
好的,这是一段代码(精简了,我希望不要太多)现在正在按我想要的方式工作:
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(600, 600); // Size of the OpenGL window
glutCreateWindow("OpenGL - 3D Test"); // Creates OpenGL Window
glutDisplayFunc(display);
glutReshapeFunc(reshape);
PngImage* pi = new PngImage(); // custom class that reads well PNG with transparency
pi->read_from_file("target.png");
GLuint texs[1];
glGenTextures(1, texs);
target_texture = texs[0];
glBindTexture(GL_TEXTURE_2D, target_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, pi->getGLInternalFormat(), pi->getWidth(), pi->getHeight(), 0, pi->getGLFormat(), GL_UNSIGNED_BYTE, pi->getTexels());
glutMainLoop(); // never returns!
return 0;
}
void reshape(int w, int h) {
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-1, 1, -1, 1);
gluPerspective(45.0, w/(GLdouble)h, 0.5, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void display(void) {
// The stared *** lines in this function make the (ugly?) fix for my second problem
glClearColor(0, 0, 0, 1.00);
glClearDepth(0); // ***
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_FUNC); // ***
glDepthFunc(GL_GREATER); // ***
draw_scene();
glutSwapBuffers();
glutPostRedisplay();
}
void draw_scene() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(1.5, 0, -3, 0, 0, 1, 0, 1, 0);
glColor4f(1.0, 1.0, 1.0, 1.0);
glEnable(GL_TEXTURE_2D);
// The following 2 lines fix the first problem
glEnable(GL_ALPHA_TEST); // makes highly transparent parts
glAlphaFunc(GL_GREATER, 0.2f); // as not existent/not drawn
glBindTexture(GL_TEXTURE_2D, target_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Drawing a textured target
float x = 0, y = 0, z = 0, size = 0.2;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(x-size, y-size, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(x+size, y-size, z);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(x+size, y+size, z);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(x-size, y+size, z);
glEnd();
// Drawing an textured target behind the other (but drawn after)
float x = 0, y = 0, z = 2, size = 0.2;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(x-size, y-size, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(x+size, y-size, z);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(x+size, y+size, z);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(x-size, y+size, z);
glEnd();
}
I've been playing around with OpenGL for a full week or equivalent.
After 2D I'm now trying 3D. I want to reproduce the 3D scene you can see in the third video on http://johnnylee.net/projects/wii/.
I've had a hard time making everything work properly with textures and depth.
I've had recently 2 problems that have somewhat visually the same impact :
- One with textures that do not blend well in 3D using the techniques I've found for 2D.
- One with objects appearing bottom above top.
Like the problem exposed here: Depth Buffer in OpenGL
I've solved both problem, but I would like to know if I get things right, especially for the second point.
For the first one, I think I've got it.
I have an image of a round target, with alpha for anything outside the disc. It's loaded fine inside OpenGL.
Some (due to my z-ordering problem) other targets behind it suffered from being hidden by the transparent regions of the naturally square quad I used to paint it.
The reason for that was that every part of the texture is assumed to be full opaque with regard for the depth buffer.
Using an glEnable(GL_ALPHA_TEST)
with an test for glAlphaFunc(GL_GREATER, 0.5f)
makes the alpha layer of the texture act as a per pixel (boolean) opacity indicator, and thus makes the blending quite useless (because my image has boolean transparency).
Supplementary question: By the way, is there a mean of specifying a different source for the alpha test than the alpha layer used for blending?
Second, I've found a fix to my problem.
Before clearing the color and depth buffer I've set the default depth to 0 glClearDepth(0.0f)
and and I've make use of "greater" depth function glDepthFunc(GL_GREATER)
.
What looks strange to me is that depth is 1.0 and the depth function is "less" GL_LESS
by default.
I'm basically inverting that so that my objects don't get displayed inverted...
I've seen nowhere such a hack, but in the other hand I've seen nowhere objects getting drawn systematically in the wrong order, regardless of which order I draw them!
OK, here's the bit of code (stripped down, not too much I hope) that is now working as I want:
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(600, 600); // Size of the OpenGL window
glutCreateWindow("OpenGL - 3D Test"); // Creates OpenGL Window
glutDisplayFunc(display);
glutReshapeFunc(reshape);
PngImage* pi = new PngImage(); // custom class that reads well PNG with transparency
pi->read_from_file("target.png");
GLuint texs[1];
glGenTextures(1, texs);
target_texture = texs[0];
glBindTexture(GL_TEXTURE_2D, target_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, pi->getGLInternalFormat(), pi->getWidth(), pi->getHeight(), 0, pi->getGLFormat(), GL_UNSIGNED_BYTE, pi->getTexels());
glutMainLoop(); // never returns!
return 0;
}
void reshape(int w, int h) {
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-1, 1, -1, 1);
gluPerspective(45.0, w/(GLdouble)h, 0.5, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void display(void) {
// The stared *** lines in this function make the (ugly?) fix for my second problem
glClearColor(0, 0, 0, 1.00);
glClearDepth(0); // ***
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_FUNC); // ***
glDepthFunc(GL_GREATER); // ***
draw_scene();
glutSwapBuffers();
glutPostRedisplay();
}
void draw_scene() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(1.5, 0, -3, 0, 0, 1, 0, 1, 0);
glColor4f(1.0, 1.0, 1.0, 1.0);
glEnable(GL_TEXTURE_2D);
// The following 2 lines fix the first problem
glEnable(GL_ALPHA_TEST); // makes highly transparent parts
glAlphaFunc(GL_GREATER, 0.2f); // as not existent/not drawn
glBindTexture(GL_TEXTURE_2D, target_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Drawing a textured target
float x = 0, y = 0, z = 0, size = 0.2;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(x-size, y-size, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(x+size, y-size, z);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(x+size, y+size, z);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(x-size, y+size, z);
glEnd();
// Drawing an textured target behind the other (but drawn after)
float x = 0, y = 0, z = 2, size = 0.2;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(x-size, y-size, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(x+size, y-size, z);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(x+size, y+size, z);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(x-size, y+size, z);
glEnd();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
通常,深度清除值为 1(实际上无穷大),并且深度传递函数为 LESS,因为您想要模拟现实世界,在该世界中您看到的事物位于其前面的事物后面。通过将深度缓冲区清除为 1,您实际上是在说应该绘制所有比最大深度更近的对象。除非您真正了解自己在做什么,否则您通常不会想要更改这些参数。
使用传递给 gluLookAt 的相机参数和对象的位置,z=2 四边形将比 z=0 对象距离相机更远。您想完成什么任务,以致这看起来不正确?
实现顺序正确的 Alpha 混合的标准方法是渲染所有不透明对象,然后从后向前渲染所有透明对象。始终使用常规/默认深度函数。
另请注意,您设置透视矩阵的方式可能会出现一些奇怪的行为。通常您会调用 gluOrtho 或 gluPerspective。但不是两者兼而有之。这会将两个不同的透视矩阵相乘,这可能不是您想要的。
Normally the depth clear value is 1 (effectively infinity) and the depth pass function is LESS because you want to simulate the real world where you see things that are in front of the things behind them. By clearing the depth buffer to 1, you are essentially saying that all objects closer than the maximum depth should be drawn. Changing those parameters is generally not something you would want to do unless you really understand what you are doing.
With the camera parameters you are passing to gluLookAt and the positions of your objects, the z=2 quad will be further from the camera than the z=0 object. What are you trying to accomplish such that this doesn't seem correct?
The standard approach to achieve order-correct alpha blending is to render all opaque objects, then render all transparent objects back to front. The regular/default depth function would always be used.
Also note that you may get some weird behavior from the way you are setting up your perspective matrix. Normally you would call gluOrtho OR gluPerspective. But not both. That will multiply the two different perspective matrices together which is probably not what you want.
是的,如果您使用着色器,您可以自己计算输出片段的 alpha 值。
Yes, if you use a shader you can compute yourself the alpha value of the output fragment.
关于第二个问题:您的 modelViewProjection 矩阵很可能有问题。
我也遇到了同样的问题(并且可以用你的黑客“修复”它),这是由于我使用了一个不知何故奇怪错误的矩阵引起的。我通过实现自己的矩阵生成解决了这个问题。
Regarding the second problem: There is very probably something wrong with your modelViewProjection matrix.
I've had the same problem (and could "fix" it with your hack) wich was caused by me employing a matrix that was somehow weirdly wrong. I solved it be implementing my own matrix generation.
标准 glOrtho 函数中实现的公式将 zNear 映射到 -1,将 zFar 映射到 1,默认情况下将其映射到窗口坐标为 [0,1](可通过 glDepthRange 更改固定管道,不确定是否仍支持该函数)。深度测试确实在这些方面起作用。
解决这个问题的方法是假设 zNear 距离投影平面最远,或者自己生成矩阵,如果你想摆脱遗留管道,这无论如何都需要。
Formulas implemented in standard glOrtho function map zNear to -1 and zFar to 1, which are by default mapped to window coordinates as [0,1] (changeable via glDepthRange for fixed pipeline, not sure if that function is still supported). Depth test works in those terms indeed.
The way around this is just assume that zNear is furthest away from projection plane, or generate matrix yourself,which would need anyway , if you want to get rid of legacy pipeline.