同一平面内另一个多边形内的多边形
我的 OpenGL 代码中有一个奇怪的行为。我想在地上画一块地毯。
在下面的代码中,如果 GROUND_SIZE 大于 2071 并且 CARPET_HEIGHT 小于 0.0003,则不会绘制较小的多边形。绘制顺序不会改变生成的图像。
#include <GL/glut.h>
const int GROUND_SIZE = 3000;
const bool GROUND_FIRST = true;
const float CARPET_HEIGHT = 0.0003;
void carpet(){
glColor3f(1.0,0.0,0.0);
glBegin(GL_QUADS);
glNormal3f(0,1,0);
glVertex3f(-1.0, -1.0, CARPET_HEIGHT);
glVertex3f( 1.0, -1.0, CARPET_HEIGHT);
glVertex3f( 1.0, 1.0, CARPET_HEIGHT);
glVertex3f(-1.0, 1.0, CARPET_HEIGHT);
glEnd();
}
void ground(){
glColor3f(0.0,0.7,0.0);
glBegin(GL_QUADS);
glNormal3f(0,1,0);
glVertex3f(-GROUND_SIZE, -GROUND_SIZE, 0);
glVertex3f( GROUND_SIZE, -GROUND_SIZE, 0);
glVertex3f( GROUND_SIZE, GROUND_SIZE, 0);
glVertex3f(-GROUND_SIZE, GROUND_SIZE, 0);
glEnd();
}
void draw(){
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (GROUND_FIRST) {
ground();
carpet();
}
else {
carpet();
ground();
}
glutSwapBuffers();
}
int main(){
int argc = 1; char* argv[] = { (char*)"" };
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowPosition(100, 50);
glutInitWindowSize(640, 640);
glutCreateWindow("Window");
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
glutDisplayFunc(draw);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1, 0.1, 1000.0);
gluLookAt(0.0, -5.0, 1.0, 0.0f, 0.0f, 1.0f, 0,0,1);
glutMainLoop();
return 0;
}
我想了解 OpenGL 用来决定是否绘制同一平面上另一个多边形内部的多边形的规则。
谢谢
There is a strange behaviour in my OpenGL code. I want to draw a carpet on the ground.
In the code below, if GROUND_SIZE is larger than 2071 and CARPET_HEIGHT is smaller than 0.0003, the smaller polygon will not be drawn. The drawing order does not change the resulting image.
#include <GL/glut.h>
const int GROUND_SIZE = 3000;
const bool GROUND_FIRST = true;
const float CARPET_HEIGHT = 0.0003;
void carpet(){
glColor3f(1.0,0.0,0.0);
glBegin(GL_QUADS);
glNormal3f(0,1,0);
glVertex3f(-1.0, -1.0, CARPET_HEIGHT);
glVertex3f( 1.0, -1.0, CARPET_HEIGHT);
glVertex3f( 1.0, 1.0, CARPET_HEIGHT);
glVertex3f(-1.0, 1.0, CARPET_HEIGHT);
glEnd();
}
void ground(){
glColor3f(0.0,0.7,0.0);
glBegin(GL_QUADS);
glNormal3f(0,1,0);
glVertex3f(-GROUND_SIZE, -GROUND_SIZE, 0);
glVertex3f( GROUND_SIZE, -GROUND_SIZE, 0);
glVertex3f( GROUND_SIZE, GROUND_SIZE, 0);
glVertex3f(-GROUND_SIZE, GROUND_SIZE, 0);
glEnd();
}
void draw(){
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (GROUND_FIRST) {
ground();
carpet();
}
else {
carpet();
ground();
}
glutSwapBuffers();
}
int main(){
int argc = 1; char* argv[] = { (char*)"" };
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowPosition(100, 50);
glutInitWindowSize(640, 640);
glutCreateWindow("Window");
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
glutDisplayFunc(draw);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1, 0.1, 1000.0);
gluLookAt(0.0, -5.0, 1.0, 0.0f, 0.0f, 1.0f, 0,0,1);
glutMainLoop();
return 0;
}
I want to understand the rules that OpenGL uses to decide if it will draw a polygon that is inside another polygon in the same plane.
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,有一些代码错误。
切勿将 gluLookAt 矩阵放入 GL_PROJECTION 矩阵中。这可能会破坏您的照明。 gluLookAt 的矩阵应该是您放入 GL_MODELVIEW 中的第一个矩阵。
将 GL_MODELVIEW 矩阵设为默认矩阵也是一个好主意。也就是说,任何切换到 GL_PROJECTION(或其中一个纹理矩阵)的代码都负责在处理完另一个矩阵后立即切换回 GL_MODELVIEW。
最后,调用 glClearDepth 和 glDepthFunc 始终是一种好的形式。默认值(1.0 和 GL_LESS)正是您想要的,但明确这些事情总是好的。这样,您就不必搜索规范来确保默认值就是您认为的那样。
现在,进入主要问题。
其规则只是深度缓冲区的规则。这既简单又复杂。
首先,两个平面的斜率。虽然它们在世界空间中肯定具有相同的斜率,但它们在投影后空间中不一定具有相同的斜率。这是由于从世界空间转换到后投影空间的各种计算中的浮点错误造成的。
其次,如果它们之间的 Z 差异太小,那么可能发生的情况是它们将获得相同的 Z 值。或者,由于浮点误差,地毯甚至可能具有较小的 Z 值。
我想如果你对视角进行动画处理,你会看到地毯平面突然出现和消失。
您可以做的一件简单的事情就是始终在地平面之后绘制地毯,并使用 GL_LEQUAL 作为 glDepthFunc。由于浮点错误,这可能无法 100% 正常工作。
下一步是使用多边形偏移。其作用是使基元的 Z 值偏置某个固定值。对于本次讨论来说,其工作原理的细节太过技术性(如果您好奇,请查看 OpenGL 规范),但基本思想是您的
carpet
函数应如下所示:First, a few code mistakes.
Never put the gluLookAt matrix in the GL_PROJECTION matrix. This can play havok with your lighting. gluLookAt's matrix should be the first matrix you put in GL_MODELVIEW.
It's also a good idea to have the GL_MODELVIEW matrix be the default matrix. That is, any code that switches to GL_PROJECTION (or one of the texture matrices) is responsible for switching back to GL_MODELVIEW immediately after it's done with the other matrix.
Lastly, it is always good form to call glClearDepth and glDepthFunc. The default values (1.0 and GL_LESS) are exactly what you want, but it's always good to be explicit about these things. That way, you don't have to go searching through the specification to make sure the default values are what you think they are.
Now, on to the main issue.
The rules for this are just the rules for depth buffers. Which is simple and complex at the same time.
First, there is the slope of the two planes. While they certainly have the same slope in world space, they do not necessarily have the same slope in post-projection space. This is due to floating-point errors in the various computations that transform from world space to post-projection space.
Secondly, if the Z-difference between them is too small, then what can happen is that they will get the same Z value applied to them. Or the carpet may even have a smaller Z value, due to floating-point errors.
I imagine if you animated the viewing angle, you would see the carpet plane pop in and out of view.
One simple thing you can do is always draw the carpet after the ground plane and use GL_LEQUAL for the glDepthFunc. This may not work 100% of the time, again due to floating-point errors.
The next step is to use polygon offsetting. What this does is bias the Z value of a primitive by some fixed value. The details of how it work is too technical for this discussion (check the OpenGL specification if you're curious), but the basic idea is that your
carpet
function should look something like this: