在3D OpenGL世界中创建2D接口的问题

6 投票
3 回答
1072 浏览
提问于 2025-04-16 07:40

我正在做一个项目,需要在一个3D世界的“上面”创建一个2D界面。我在其他论坛上看到可以用“GluOrtho2D()”来实现这个功能,完成后再切换回GluPerspective()。问题是,我写的测试代码只显示了3D世界,而没有显示2D的平面。不过,当我关闭3D渲染的代码时,平面又能正常显示在应该的位置。

我把代码简化成了只有OpenGL的部分,下面是我写的代码。这个代码是用Python和Pyglet库写的。


场景初始化的代码:

glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60.0, float(width)/height, .1, 10000.)
glMatrixMode(GL_MODELVIEW)
glClearDepth(1.0)
glShadeModel(GL_FLAT)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL) 
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)

帧渲染的代码。调用builder.buildMap()创建3D场景,而glBegin-glEnd这对代码则绘制了一个2D平面:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
stage.set3DMode(window.width, window.height)
builder.buildMap()
stage.set2DMode(window.width, window.height)
glBegin (GL_QUADS)
glVertex2i(0, 0)
glVertex2i(0, 200)
glVertex2i(200, 200)
glVertex2i(200, 0) 
glEnd()

stage.set3DMode函数:

glDisable(GL_DEPTH_TEST) 
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0, width, 0, height)
glMatrixMode(GL_MODELVIEW)

还有stage.set3DMode函数:

glEnable(GL_DEPTH_TEST) 
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60.0, float(width)/float(height), .1, 10000.)
glMatrixMode(GL_MODELVIEW)

我真的希望有人能指出我的错误!非常感谢大家的帮助 :)

3 个回答

1

我使用以下矩阵来进行二维渲染:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, h, 0, 0, 1);
glViewport(0, 0, w, h);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375f, 0.375f, 0);
2

很多人对如何使用glViewport等函数理解得不太对。他们总是把这些放在重塑回调函数里,这其实是错误的。

你应该在渲染函数里进行完整的设置,最好是在你需要这些设置之前。所以你的代码应该是(伪代码):

render_scene():
    // first clear the whole window
    glViewport(0, 0, window.width, window.height)
    glClearDepth(1.0)
    glClearColor(1., 1., 1., 1.);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)

    glViewport(3Dstuff.x, 3Dstuff.y, 3Dstuff.w, 3Dstuff.h)
    // maybe also set scissor to clip
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(60.0, float(width)/height, .1, 10000.)
    glMatrixMode(GL_MODELVIEW)
    setup3DstuffModelview()
    glDepthFunc(...)
    glEnable(GL_DEPTH_TEST)
    // other state stuff
    render3Dstuff()

    // now the 2D stuff
    glViewport(2Dstuff.x, 2Dstuff.y, 2Dstuff.w, 2Dstuff.h)
    // clear depth and stencil -- if you need parts of the 3D depth / stencil
    // for some algorithm retain it or save and restore by FBO renderbuffers or
    // glReadPixels, glDrawPixels
    glClearDepth(1.0)
    glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(...)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    setup2DstuffModelview()
    glDepthFunc(...)
    glDisable(GL_DEPTH_TEST)
    // other state stuff
    render2Dstuff()

    // repeat for all the layers you need

这点非常重要:OpenGL是一个状态机。除非你能证明没有任何不确定的状态变化发生,否则在渲染特定的图形之前,你总是需要重新设置所有需要的状态。

4

看起来你在切换到GL_MODELVIEW后没有调用glLoadIdentity()。记住这个最简单的方法就是:每次你改变投影矩阵时,都要切换到GL_MODELVIEW,然后调用glLoadIdentity()(除非你真的想保留之前的设置)。

撰写回答