使用FBO渲染多个深度信息

4
我正在尝试实现一个着色器,计算光线在物体的前表面和后表面间的折射。为此,我需要使用正常深度测试(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);
}

以下是我如何使用FBOs: 在渲染函数之外,我首先创建两个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上,然后在四边形上渲染,它就能完美地工作。在上面的示例中,我将其渲染了200次到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;


        /**
         * 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时,然后尝试在每一帧显示四边形时。我得到了一个奇怪的结果,似乎只考虑了几何的一小部分: Only a small wrong part of the geometry 我无法弄清楚为什么会发生这种情况。它明确地渲染到深度纹理,但似乎由于某种原因,渲染全屏幕四边形会改变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();

好的,这样做可以让我自由地在场景中移动、添加物体而不会遇到任何问题。然而,我仍然很好奇是哪个状态改变导致了渲染过程的失败,你有什么想法吗?

2个回答

2

既然问题已经解决(尽管我不知道是哪个状态导致了一切关掉),这是我的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();
    }

}

1
您正在尝试的是一种称为“深度剥离”的技术,它可以被描述为向多个深度缓冲区层中插入排序的形式。在线上有许多演示和论文可供参考。

谢谢,即使我正在实现的不是真正的深度剥离,但它确实非常接近这个概念。 我清理了很多我的代码,并使用两个分离的FBO渲染深度缓冲区。它几乎可以工作,但存在我在编辑后的第一条消息中解释的问题。 - geenux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接