You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现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)nearfar反转了,这会导致深度值的计算逻辑完全错误,深度测试无法正确判断前后关系。


修改后的完整代码

#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

火山引擎 最新活动