【问题标题】:Rendering multiple depth information with FBOs使用 FBO 渲染多个深度信息
【发布时间】:2013-03-27 21:25:53
【问题描述】:

我正在尝试实现一个着色器来计算通过两个表面的光折射:对象的背面和正面。 为此,我需要使用正常深度测试 (GL_LESS) 和反向深度测试 (GL_GREATER) 来渲染折射几何。它可以让我计算从背面到正面的距离。 不幸的是,我一次只能渲染其中一个,而且我不知道如何将这两个深度信息作为纹理传递给着色器。

着色器本身应该不是问题,但我正在努力设置 opengl,以便它为着色器提供所需的一切!

为了非常清楚,我需要为我的着色器提供两个纹理: - 带有我的对象正面深度信息的纹理 - 带有对象背面深度信息的纹理

这大致是我所做的(已简化,以便代码不会太乱而无法阅读)。

void FBO::init() {
    initDepthTexture();
    initFBO();
}

void FBO::initDepthTexture() {
    //32 bit depth texture, mWidth*mHeight
    glGenTextures(1, &mDepthTex);
    glBindTexture(GL_TEXTURE_2D, mDepthTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
            GL_COMPARE_R_TO_TEXTURE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

    //NULL means reserve texture memory, but texels are undefined
    //You can also try GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24 for the internal format.
    //If GL_DEPTH24_STENCIL8_EXT, go ahead and use it (GL_EXT_packed_depth_stencil)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, mWidth, mHeight, 0,
            GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
}

void FBO::initFBO() {
    glGenFramebuffersEXT(1, &mFrameBuffer);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFrameBuffer);
    //Attach
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
            GL_TEXTURE_2D, mDepthTex, 0);
    //-------------------------
    //Does the GPU support current FBO configuration?
    //Before checking the configuration, you should call these 2 according to the spec.
    //At the very least, you need to call glDrawBuffer(GL_NONE)
    glDrawBuffer(GL_NONE);
    glReadBuffer(GL_NONE);

    checkFBO();

    renderToScreen();
}


void FBO::renderToFBO() {
    cout << "Render to FBO: " << mFrameBuffer << endl;
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFrameBuffer); // Bind our frame buffer for rendering
    //-------------------------
    //----and to render to it, don't forget to call
    //At the very least, you need to call glDrawBuffer(GL_NONE)
    glDrawBuffer(GL_NONE);
    glReadBuffer(GL_NONE);
}

/**
 * Static
 */
void FBO::renderToScreen() {
    cout << "Render to screen " << endl;
    // Finish all operations
    //glFlush();
    //-------------------------
    //If you want to render to the back buffer again, you must bind 0 AND THEN CALL glDrawBuffer(GL_BACK)
    //else GL_INVALID_OPERATION will be raised
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our texture
    glDrawBuffer(GL_BACK);
    glReadBuffer(GL_BACK);
}

以下是我使用 FBO 的方式: 我首先在render函数之外创建了两个FBO,看init()函数看看它是如何初始化的。 在第一个 FBO 上,我从前面渲染几何深度 在第二个 FBO 上,我从后面渲染几何深度 然后我将两个深度纹理渲染到全屏四边形。

void Viewer::onRender() {
        FBO::renderToScreen();

            // XXX: Need of Z-Depth sorting to get alpha blending right!!
            glEnable(GL_DEPTH_TEST);

            glClearColor(0., 0., 0.2, 1.);
            glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

            glClearDepth(1.);
            glDepthFunc(GL_LESS);

            // set the projection transformation
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(45.0f, (GLdouble) m_width / (GLdouble) m_height,
                    m_scale * 5.0, m_scale * 10000.0);


            // set the model transformation
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glm::vec3 pos = mCamera->getPosition();
            glm::vec3 view = mCamera->getView();
            glm::vec3 up = mCamera->getUp();
            gluLookAt(pos.x, pos.y, pos.z, view.x, view.y, view.z, up.x, up.y,
                    up.z);


            static float rotationAngle = 0;
            rotationAngle+=5;

            static int i = 0;
            if(i++ < 200) {
            /**
             * Render geometry twice to FBOs
             */
            mFBO->renderToFBO();
            glClear(GL_DEPTH_BUFFER_BIT);
            glClearDepth(0.);
            glDepthFunc(GL_LESS);
            glPushMatrix();
            glRotatef(1, 1, 0, 120);
            glColor3f(0., 1., 0.);
            // Draw teapot
            glutSolidTeapot(1.8);
            glPopMatrix();

            mFBO2->renderToFBO();
            glClear(GL_DEPTH_BUFFER_BIT);
            glClearDepth(0.);
            glDepthFunc(GL_GREATER);
            glPushMatrix();
            glColor3f(0., 1., 0.);
            // Draw teapot
            glutSolidTeapot(3.5);
            glPopMatrix();


            /**
             * Render the same geometry to the screen
             */
            FBO::renderToScreen();
            } else {
            mShader->enable();
            mShader->setTextureFromId("frontDepth", mFBO->getDepthTextureId());
            mShader->setTextureFromId("backDepth", mFBO2->getDepthTextureId());
            glBegin(GL_QUADS); // Draw A Quad
            glTexCoord2f(0, 1);
            glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left
            glTexCoord2f(1, 1);
            glVertex3f(1.0f, 1.0f, 0.0f); // Top Right
            glTexCoord2f(1, 0);
            glVertex3f(1.0f, -1.0f, 0.0f); // Bottom Right
            glTexCoord2f(0, 0);
            glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left
            glEnd(); // Done Drawing The Quad
            mShader->disable();
        }
    }

如果渲染到 FBO 然后在四边形上渲染,这将完美运行。在上面的示例中,我向 FBO 渲染 200 次,然后停止向 FBO 渲染并在我的全屏四边形上显示纹理。 这是结果,正如预期的那样(出于显示目的,我将第二个几何图形渲染为小于第一个几何图形):

这是代码(与工作图像几乎相同,但为每一帧渲染四边形)

void Viewer::onRender() {
            FBO::renderToScreen();

        // XXX: Need of Z-Depth sorting to get alpha blending right!!
        glEnable(GL_DEPTH_TEST);

        glClearColor(0., 0., 0.2, 1.);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        glClearDepth(1.);
        glDepthFunc(GL_LESS);

        // set the projection transformation
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0f, (GLdouble) m_width / (GLdouble) m_height,
                m_scale * 5.0, m_scale * 10000.0);


        // set the model transformation
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glm::vec3 pos = mCamera->getPosition();
        glm::vec3 view = mCamera->getView();
        glm::vec3 up = mCamera->getUp();
        gluLookAt(pos.x, pos.y, pos.z, view.x, view.y, view.z, up.x, up.y,
                up.z);


        static float rotationAngle = 0;
        rotationAngle+=5;


        /**
         * Render geometry twice to FBOs
         */
        mFBO->renderToFBO();
        glClear(GL_DEPTH_BUFFER_BIT);
        glClearDepth(0.);
        glDepthFunc(GL_LESS);
        glPushMatrix();
        glRotatef(1, 1, 0, 120);
        glColor3f(0., 1., 0.);
        // Draw teapot
        glutSolidTeapot(1.8);
        glPopMatrix();

        mFBO2->renderToFBO();
        glClear(GL_DEPTH_BUFFER_BIT);
        glClearDepth(0.);
        glDepthFunc(GL_GREATER);
        glPushMatrix();
        glColor3f(0., 1., 0.);
        // Draw teapot
        glutSolidTeapot(3.5);
        glPopMatrix();


            /**
             * Render both depth texture on a fullscreen quad
             **/
        FBO::renderToScreen();
        mShader->enable();
        mShader->setTextureFromId("frontDepth", mFBO->getDepthTextureId());
        mShader->setTextureFromId("backDepth", mFBO2->getDepthTextureId());
        glBegin(GL_QUADS); // Draw A Quad
        glTexCoord2f(0, 1);
        glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left
        glTexCoord2f(1, 1);
        glVertex3f(1.0f, 1.0f, 0.0f); // Top Right
        glTexCoord2f(1, 0);
        glVertex3f(1.0f, -1.0f, 0.0f); // Bottom Right
        glTexCoord2f(0, 0);
        glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left
        glEnd(); // Done Drawing The Quad
        mShader->disable();
    }
}

但是现在,当我渲染到 FBO,然后尝试在每一帧显示四边形时,我的问题就出现了。 我得到了一个奇怪的结果,这似乎只考虑了几何的一小部分:

我不知道为什么会这样。它肯定会渲染到深度纹理,但似乎由于某种原因渲染全屏四边形会改变 FBO 几何体的渲染。

[编辑] 我只是尝试保存 opengl 状态,并在四边形之后恢复它...

            FBO::renderToScreen();
    glPushAttrib(GL_ALL_ATTRIB_BITS);

        mShader->enable();
        mShader->setTextureFromId("frontDepth", mFBO->getDepthTextureId());
        mShader->setTextureFromId("backDepth", mFBO2->getDepthTextureId());
        glBegin(GL_QUADS); // Draw A Quad
        glTexCoord2f(0, 1);
        glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left
        glTexCoord2f(1, 1);
        glVertex3f(1.0f, 1.0f, 0.0f); // Top Right
        glTexCoord2f(1, 0);
        glVertex3f(1.0f, -1.0f, 0.0f); // Bottom Right
        glTexCoord2f(0, 0);
        glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left
        glEnd(); // Done Drawing The Quad
        mShader->disable();
            glPopAttrib();

好吧,这行得通,我可以在场景中四处移动添加对象和任何东西,而不会遇到任何麻烦。 但是我仍然很好奇哪个状态变化可能导致渲染过程失败这么多,知道吗?

【问题讨论】:

    标签: opengl fbo depth-buffer


    【解决方案1】:

    由于问题已解决(即使我不明白是哪个状态推迟了一切),这里是我的 FBO 类和渲染函数的完整代码,以防有人遇到同样的问题:

        /*
     * FBO.cpp
     *
     *  Created on: 28 Mar 2013
     *      Author: arnaud
     */
    
    // Include GLEW
    #include <GL/glew.h>
    #include "FBO.h"
    #include <GL/glext.h>
    #include <iostream>
    #include "FBOException.h"
    
    using namespace std;
    
    FBO::FBO(int width, int height) {
        mWidth = width;
        mHeight = height;
        // TODO Auto-generated constructor stubc
        init();
    
    }
    
    FBO::~FBO() {
        // TODO Auto-generated destructor stub
        glDeleteFramebuffersEXT(1, &mFrameBuffer);
    }
    
    void FBO::init() {
        initDepthTexture();
        initFBO();
    }
    
    void FBO::initDepthTexture() {
        //32 bit depth texture, mWidth*mHeight
        glGenTextures(1, &mDepthTex);
        glBindTexture(GL_TEXTURE_2D, mDepthTex);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
                GL_COMPARE_R_TO_TEXTURE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
    
        //NULL means reserve texture memory, but texels are undefined
        //You can also try GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24 for the internal format.
        //If GL_DEPTH24_STENCIL8_EXT, go ahead and use it (GL_EXT_packed_depth_stencil)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, mWidth, mHeight, 0,
                GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
    }
    
    void FBO::initFBO() {
        glGenFramebuffersEXT(1, &mFrameBuffer);
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFrameBuffer);
        //Attach
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                GL_TEXTURE_2D, mDepthTex, 0);
        //-------------------------
        //Does the GPU support current FBO configuration?
        //Before checking the configuration, you should call these 2 according to the spec.
        //At the very least, you need to call glDrawBuffer(GL_NONE)
        glDrawBuffer(GL_NONE);
        glReadBuffer(GL_NONE);
    
        checkFBO();
    
        renderToScreen();
    }
    
    void FBO::checkFBO() throw () {
        GLenum status;
        status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
        switch (status) {
        case GL_FRAMEBUFFER_COMPLETE_EXT:
            cout << "Good Framebuffer" << endl;
            break;
        case GL_FRAMEBUFFER_UNDEFINED:
            throw new FBOException(
                    "Framebuffer undefined. Usually returned if  returned if target is the default framebuffer, but the default framebuffer does not exist.");
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
            throw new FBOException(
                    "Incomplete Attachement: is returned if any of the framebuffer attachment points are framebuffer incomplete.");
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
            throw new FBOException(
                    "Incomplete Missing Attachment: is returned if the framebuffer does not have at least one image attached to it.");
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
            throw new FBOException(
                    "Incomplete Draw Buffer: is returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) named by GL_DRAWBUFFERi");
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
            throw new FBOException(
                    "Incomplete Read Buffer: is returned if GL_READ_BUFFER is not GL_NONE and the value of\\"
                            " GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point named by GL_READ_BUFFER");
            break;
        case GL_FRAMEBUFFER_UNSUPPORTED:
            throw new FBOException(
                    "Framebuffer Unsupported: is returned if the combination of internal formats of the attached images violates an implementation-dependent set of restrictions.");
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
            throw new FBOException("Incomplete Multisample");
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
            throw new FBOException("Incomplete Layer Targets");
            break;
        default:
            throw new FBOException("Bad Framebuffer");
        }
    }
    
    
    /****
     * PUBLIC Functions
     */
    
    void FBO::renderToFBO() {
        cout << "Render to FBO: " << mFrameBuffer << endl;
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFrameBuffer); // Bind our frame buffer for rendering
        //-------------------------
        //----and to render to it, don't forget to call
        //At the very least, you need to call glDrawBuffer(GL_NONE)
        glDrawBuffer(GL_NONE);
        glReadBuffer(GL_NONE);
    }
    
    /**
     * Static
     */
    void FBO::renderToScreen() {
        cout << "Render to screen " << endl;
        // Finish all operations
        //glFlush();
        //-------------------------
        //If you want to render to the back buffer again, you must bind 0 AND THEN CALL glDrawBuffer(GL_BACK)
        //else GL_INVALID_OPERATION will be raised
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Unbind our texture
        glDrawBuffer(GL_BACK);
        glReadBuffer(GL_BACK);
    }
    

    这里是渲染函数

    void Viewer::onRender() {
        // Get elapsed time since last loop
        sf::Time time = mClock.getElapsedTime();
        float ellapsedTime = time.asMilliseconds();
        if (time.asMilliseconds() > 1000 / 60) {
    
            // XXX: Need of Z-Depth sorting to get alpha blending right!!
            glEnable(GL_DEPTH_TEST);
    
            glClearColor(0., 0., 0.2, 1.);
            glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    
            glClearDepth(1.);
            glDepthFunc(GL_LESS);
    
            // set the projection transformation
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(45.0f, (GLdouble) m_width / (GLdouble) m_height,
                    m_scale * 5.0, m_scale * 10000.0);
    
    
            // set the model transformation
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glm::vec3 pos = mCamera->getPosition();
            glm::vec3 view = mCamera->getView();
            glm::vec3 up = mCamera->getUp();
            gluLookAt(pos.x, pos.y, pos.z, view.x, view.y, view.z, up.x, up.y,
                    up.z);
    
    
            static float rotationAngle = 0;
            rotationAngle+=5;
    
            /**
             * Render geometry twice to FBOs
             */
            mFBO->renderToFBO();
            glClear(GL_DEPTH_BUFFER_BIT);
            glClearDepth(0.);
            glDepthFunc(GL_LESS);
            glPushMatrix();
            glColor3f(0., 1., 0.);
            // Draw teapot
            glutSolidTeapot(3.5);
            glPopMatrix();
    
            mFBO2->renderToFBO();
            glClear(GL_DEPTH_BUFFER_BIT);
            glClearDepth(0.);
            glDepthFunc(GL_GREATER);
            glPushMatrix();
            glColor3f(0., 1., 0.);
            // Draw teapot
            glutSolidTeapot(3.5);
            glPopMatrix();
    
    
            /**
             * Render the same geometry to the screen
             */
            FBO::renderToScreen();
            // XXX: Save attribs bits to fix FBO problem... (why is this needed!?)
            glPushAttrib(GL_ALL_ATTRIB_BITS);
            mShader->enable();
            mShader->setTextureFromId("frontDepth", mFBO->getDepthTextureId());
            mShader->setTextureFromId("backDepth", mFBO2->getDepthTextureId());
            glBegin(GL_QUADS); // Draw A Quad
            glTexCoord2f(0, 1);
            glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left
            glTexCoord2f(1, 1);
            glVertex3f(1.0f, 1.0f, 0.0f); // Top Right
            glTexCoord2f(1, 0);
            glVertex3f(1.0f, -1.0f, 0.0f); // Bottom Right
            glTexCoord2f(0, 0);
            glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left
            glEnd(); // Done Drawing The Quad
            mShader->disable();
            glPopAttrib();
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      您正在尝试做的是一种称为“深度剥离”的技术,它本质上可以描述为一种插入排序到多个深度缓冲层的形式。网上有许多演示文稿和论文。

      【讨论】:

      • 谢谢,即使我正在实施的不是真正的深度剥离,它确实非常接近这个概念。我清理了很多代码,并使用两个单独的 FBO 渲染了深度缓冲区。它几乎可以工作,但对于我在编辑的第一条消息中解释的问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多