OpenGL 中的反射和深度

Posted

技术标签:

【中文标题】OpenGL 中的反射和深度【英文标题】:Reflection and depth in OpenGL 【发布时间】:2013-01-20 06:30:23 【问题描述】:

我正在尝试在 OpenGL 中创建镜像。我发现的大多数参考资料都建议使用模板缓冲区来定义镜像本身的边界,并结合使用平移和缩放矩阵来进行实际反射。当观察场景时,我设法让它工作,这样镜子前面就没有物体了。然而,当一个物体在镜子前面时,重叠部分不会显示出来,使它看起来好像它在镜子后面。我很有可能误解了模板缓冲区的工作原理,因为这是我第一次尝试使用它,但我也可能在深度或其他方面犯了一些错误。我会很感激任何建议。这是代码。

#include <gl/glut.h>

// perspective
void view(void) 
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-2.0, 2.0, -2.0, 2.0, -2.0, 2.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotatef(-85.0, 1.0, 0.0, 0.0);
    glScalef(0.25, 0.25, 0.25);


void init(void) 
    glEnable(GL_DEPTH_TEST);
    glClearStencil(0);
    glEnable(GL_STENCIL_TEST);


void mirror(GLboolean inside, GLfloat vertSet[4][3]) 
    GLint i;
    if(inside)
        glBegin(GL_QUADS); // draw inside of mirror
    else
        glBegin(GL_LINE_LOOP); // draw frame of mirror
            for(i = 0; i < 4; i++)
                glVertex3fv(vertSet[i]);
    glEnd();


void scene(void) 
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glRotatef(90.0, 1.0, 0.0, 0.0);
    glutSolidTeapot(1.0);
    glPopMatrix();


void display(void) 
    GLfloat vertSet1[4][3] = -3.0, 3.0, 0.0, 2.0, 3.0, 0.0, 
        2.0, 3.0, 2.0, -3.0, 3.0, 2.0;

    view();

    // store mirror shape in the stencil buffer.
    glClear(GL_STENCIL_BUFFER_BIT);
    glStencilFunc(GL_ALWAYS, 1, 1);
    glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
    mirror(true, vertSet1);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // draw mirror frame.
    glColor3f(0.0, 0.0, 0.0);
    mirror(false, vertSet1);

    // draw scene outside mirror
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glStencilFunc(GL_NOTEQUAL, 1, 1);
    scene();

    // draw reflection of scene in mirror
    glStencilFunc(GL_EQUAL, 1, 1);
    glTranslatef(0.0, 3.0, 0.0);
    glScalef(1.0, -1.0, 1.0);
    glTranslatef(0.0, -3.0, 0.0);
    scene();

    glFlush();
    glutSwapBuffers();
    glutPostRedisplay();


int main (int argc, char **argv) 
    glutInit(&argc, argv);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(0, 0);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_STENCIL | GLUT_DEPTH);
    glutCreateWindow("");

    glClearColor(1.0, 1.0, 1.0, 0.0);

    init();

    glutDisplayFunc(display);

    glutMainLoop();

【问题讨论】:

【参考方案1】:

模板的想法是,当您通常在场景中渲染镜子的玻璃时绘制它。您希望对模板的绘图进行深度测试。完成场景渲染后,在镜像平面中添加一个剪辑平面,然后清除深度缓冲区并重新绘制场景。由于您清除了深度缓冲区,因此您需要先对模板进行深度测试,以使镜子不会过度绘制世界上已绘制的对象。

请注意,绘制镜子本质上与绘制门户相同。

【讨论】:

【参考方案2】:

这是我在旧 opengl 中进行反射的代码,希望对您有所帮助 请注意大部分代码来自NeHe教程:http://nehe.gamedev.net/tutorial/clipping__reflections_using_the_stencil_buffer/17004/

double eqr[] =  0.0f, -1.0f, 0.0f, 0.0f ;
glColorMask(0, 0, 0, 0);

glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 1); 
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  

glDisable(GL_DEPTH_TEST);   
DrawFloor();    // draw flor to the stencil buffer

glColorMask(1, 1, 1, 1);            
glStencilFunc(GL_EQUAL, 1, 1);      

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

glEnable(GL_CLIP_PLANE0);       

glClipPlane(GL_CLIP_PLANE0, eqr);
glPushMatrix();
    glScalef(1.0f, -1.0f, 1.0f);
    //glLightfv(GL_LIGHT0, GL_POSITION, light_pos);     
    glFrontFace(GL_CW);
    glTranslatef(0.0f, 0.3f, 0.0);
    RenderScene();
    glFrontFace(GL_CCW);
glPopMatrix();
glDisable(GL_CLIP_PLANE0);                  
glDisable(GL_STENCIL_TEST); 

glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glColor4f(0.6f, 0.7f, 1.0f, 0.5f);
    DrawFloor();    // draw floor second time
glDisable(GL_BLEND);

// draw normal scene
glPushMatrix();
    glColor3f(1.0f, 1.0f, 1.0f);
    glTranslatef(0.0f, 0.3f, 0.0);
    RenderScene();
glPopMatrix();

为了获得更好的结果,您可以使用 RenderToTexture 或使用立方体贴图

【讨论】:

您在拨打DrawFloor()后忘记拨打glEnable(GL_DEPTH_TEST);

以上是关于OpenGL 中的反射和深度的主要内容,如果未能解决你的问题,请参考以下文章

从固定管线到可编程管线:十段代码入门OpenGL

从固定管线到可编程管线:十段代码入门OpenGL

OpenGL水反射

OpenGL编程 基础篇OpenGL中几种光照参数

Linux C++ 上 Gtkmm 中的 OpenGL 模板缓冲区

OpenGL - 链接两个纹理不起作用