4

オブジェクトの背面と前面の 2 つのサーフェスを通る光の屈折を計算するシェーダーを実装しようとしています。そのためには、通常の深度テスト (GL_LESS) と反転深度テスト (GL_GREATER) を使用して屈折ジオメトリをレンダリングする必要があります。背面から前面までの距離を計算できます。残念ながら、一度に 1 つしかレンダリングできず、両方の深度情報をテクスチャとしてシェーダーに渡す方法がわかりません。

シェーダー自体は問題にならないはずですが、opengl をセットアップして、シェーダーに必要なすべてを提供するのに苦労しています!

完全に明確にするために、シェーダーに 2 つのテクスチャを与える必要があります。 - オブジェクトの前面の深度情報を持つテクスチャ - オブジェクトの背面の深度情報を持つテクスチャ

これが私がやったことの大まかなことです(コードが読みにくいように単純化されています)。

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 関数の外で 2 つの FBO を作成します。init() 関数を参照して、初期化方法を確認します。最初の FBO では、前面からジオメトリ深度をレンダリングします。2 番目の 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 へのレンダリングを停止し、フルスクリーン クワッドにテクスチャを表示します。期待どおりの結果は次のとおりです (表示のために、最初のジオメトリよりも 2 番目のジオメトリを小さくレンダリングしました)。

前面と背面の深さ

コードは次のとおりです(作業画像とほぼ同じですが、フレームごとにクワッドをレンダリングします)

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

まあ、うまくいきました。シーンを移動して、オブジェクトやその他のものを問題なく追加できます。ただし、どの状態の変化がレンダリング プロセスの失敗の原因になったのか、まだ興味があります。

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 に答える