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);
}

下面是我如何使用 FBO:我首先在渲染函数之外创建了两个 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状态,并在quad之后恢复它......

            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();

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

4

2 回答 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();
    }

}
于 2013-03-30T17:15:47.283 回答
1

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

于 2013-03-27T22:56:48.720 回答