如何实现OpenGL立方体面不透明?深度测试启用后透视问题排查
问题分析与修复方案
你遇到的“看穿立方体”问题,本质是深度测试没有真正生效——虽然你调用了glEnable(GL_DEPTH_TEST),但代码里有几个关键细节没处理对,导致深度缓冲无法正常工作:
1. 没有创建深度缓冲区
在glutInitDisplayMode中,你只指定了GLUT_DOUBLE | GLUT_RGB,但缺少GLUT_DEPTH标志。这意味着GLUT不会为窗口创建深度缓冲,深度测试自然没有数据可以参考,就会出现面的绘制顺序错误(看起来像透明穿透)。
2. 每帧没有清除深度缓冲
在myDrawing函数里,你只清除了颜色缓冲GL_COLOR_BUFFER_BIT,但没有清除深度缓冲GL_DEPTH_BUFFER_BIT。每帧的深度数据会残留,导致后续帧的深度判断混乱,出现错误的遮挡关系。
3. 正交投影的近远平面参数写反了
glOrtho的参数格式是glOrtho(left, right, bottom, top, near, far),其中near是靠近观察者的平面(应该是正数),far是远离观察者的平面(应该比near大的正数)。你写的glOrtho(-2, 2, -2, 2, 2, -2)把near和far反转了,这会导致深度值的计算逻辑完全错误,深度测试无法正确判断前后关系。
修改后的完整代码
#include <math.h> #include <vector> #include <Windows.h> #include <gl\glut.h> using namespace std; void myInit() { glClearColor(0, 0, 0, 1); glOrtho(-2, 2, -2, 2, 0.1, 10.0); // 修正近远平面参数,符合正常观察逻辑 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // 初始化模型视图矩阵,避免残留变换影响绘制 } float Cube[][3] = { {-1, -1, -1}, {1, -1, -1}, {1, 1, -1}, {-1, 1, -1}, {-1, -1, 1}, {1, -1, 1}, {1, 1, 1}, {-1, 1, 1} }; float Colors[][3] = { {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 0}, {0, 1, 1}, {1, 0, 1}, {1, 1, 1} }; int axis = 0, theta[3] = {0, 0, 0}; void draw_face(int a, int b, int c, int d) { glBegin(GL_QUADS); glColor3fv(Colors[a]); glVertex3fv(Cube[a]); glColor3fv(Colors[b]); glVertex3fv(Cube[b]); glColor3fv(Colors[c]); glVertex3fv(Cube[c]); glColor3fv(Colors[d]); glVertex3fv(Cube[d]); glEnd(); } void draw_cube() { draw_face(0, 3, 2, 1); draw_face(2, 3, 7, 6); draw_face(0, 4, 7, 3); draw_face(1, 2, 6, 5); draw_face(4, 5, 6, 7); draw_face(0, 1, 5, 4); } void spin_cube() { theta[axis] += 2; if (theta[axis] > 360) theta[axis] = -360; glutPostRedisplay(); } void idle_func() { Sleep(10); spin_cube(); } void mouse_func(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0; else if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1; else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2; } void myDrawing() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 同时清除颜色和深度缓冲 glPushMatrix(); glRotatef(theta[0], 1, 0, 0); glRotatef(theta[1], 0, 1, 0); glRotatef(theta[2], 0, 0, 1); draw_cube(); glPopMatrix(); glutSwapBuffers(); // 双缓冲模式下用这个即可,glFlush可以省略 } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // 添加GLUT_DEPTH创建深度缓冲 glutInitWindowSize(500, 500); glutInitWindowPosition(0, 0); glutCreateWindow("sample"); glEnable(GL_DEPTH_TEST); // 移到窗口创建后,确保深度缓冲已存在再启用测试 glutDisplayFunc(myDrawing); glutIdleFunc(idle_func); glutMouseFunc(mouse_func); myInit(); glutMainLoop(); }
额外优化说明
- 把
glEnable(GL_DEPTH_TEST)移到glutCreateWindow之后,确保窗口的深度缓冲已经创建,再启用深度测试,逻辑更稳妥。 - 在
myInit里添加glLoadIdentity(),初始化模型视图矩阵,避免未定义的初始变换影响立方体的位置和旋转。 - 双缓冲模式下,
glutSwapBuffers()已经包含了缓冲刷新的逻辑,glFlush可以直接去掉。
内容的提问来源于stack exchange,提问作者acr1997




