我正在尝试实现一个着色器,计算通过两个表面的光折射:对象的背面和正面。
为此,我需要使用正常深度测试 (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();
嗯,这行得通,我可以在场景中移动添加对象和一切,而不会遇到任何麻烦。
但是我仍然很好奇哪个状态更改可能导致渲染过程失败这么多,知道吗?
最佳答案
由于问题已解决(即使我不明白是哪个状态将一切都关闭了),这里是我的 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();
}
}