一周来,我一直在努力解决应用程序中的一个奇怪问题。我实现了一个非常简单的阴影映射应用程序,我得到了一个阴影,但随着游戏相机的移动,它会以一种奇怪的方式移动和扭曲。
我认为一个明显的解释是矩阵的计算方式错误,但我已经证明这不是原因。我的着色器非常简单,我可以正确渲染深度贴图,并在应用程序中可视化它。
以下是我对该应用程序最有想法的地方:http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/
这是我的阴影着色器:
shadow_shader.frag
#version 120
varying vec4 shadowCoord;
uniform sampler2D shadowMapSampler;
void main() {
// THE WEIRDEST THING IS THAT THIS MUST BE ENABLED
// works only if this is here, drops fps to 4 though
if(shadowCoord.w <= 0.0) {
gl_FragColor = vec4(0.0);
return;
}
float shadow = 1.0f;
if(shadowCoord.w > 0.0f) {
vec4 shadowCoordinateWdivide = shadowCoord / shadowCoord.w;
shadowCoordinateWdivide.xyz = shadowCoordinateWdivide.xyz;
if(shadowCoordinateWdivide.x > 0.0f && shadowCoordinateWdivide.x < 1.0f &&
shadowCoordinateWdivide.y > 0.0f && shadowCoordinateWdivide.y < 1.0f &&
shadowCoordinateWdivide.z > 0.0f && shadowCoordinateWdivide.z < 1.0f)
{
// Used to lower moiré pattern and self-shadowing
shadowCoordinateWdivide.z -= 0.02f / shadowCoord.w;
float distanceFromLight =
texture2D(shadowMapSampler, shadowCoordinateWdivide.st).r;
shadow = (distanceFromLight < shadowCoordinateWdivide.z) ? 0.4f : 1.0f;
}
}
gl_FragColor = vec4(shadow, 0.0f, 0f, 1.0f);
}
shadow_shader.vert
#version 120
varying vec4 shadowCoord;
uniform mat4 depthBiasMVP;
uniform mat4 MVP;
void main() {
shadowCoord = depthBiasMVP * gl_Vertex;
gl_Position = MVP * gl_Vertex;
}
如果我在片段着色器的开头写这篇文章,我可以让代码正常工作
...
if(shadowCoord.w <= 0.0) {
gl_FragColor = vec4(0.0);
return;
}
...
我认为我的FBO创作相当标准。以下是创建FBO和渲染内容最重要的代码行:
bool GLPanelObjects::InitFBO() {
int w = m_viewDepth.w();
int h = m_viewDepth.h();
glGenTextures(1, &m_depth_tex);
glBindTexture(GL_TEXTURE_2D, m_depth_tex);
GLfloat v_bc[] = {1.0f,1.0f,1.0f,1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, v_bc);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
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_TEXTURE_COMPARE_MODE, GL_NONE);
glTexParameteri (GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_DEPTH_COMPONENT32, // tried 16 and 24
w, h,
0,
GL_DEPTH_COMPONENT,
GL_UNSIGNED_BYTE, // tried GL_FLOAT and GL_UNSIGNED_BYTE
NULL);
glBindTexture(GL_TEXTURE_2D, 0);
//-------------------------
glGenFramebuffers(1, &m_fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fb);
glDrawBuffer(GL_NONE); // No color buffer is drawn
glReadBuffer(GL_NONE);
//-------------------------
//Attach depth texture to FBO
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D, m_depth_tex, 0/*mipmap level*/);
//-------------------------
//Does the GPU support current FBO configuration?
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if(status != GL_FRAMEBUFFER_COMPLETE) {
return false;
}
glClearColor(0, 0, 0, 1);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return true;
}
bool GLPanelObjects::create() {
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
m_viewDepth = types::Rectangle<int>(m_viewport.x(),
m_viewport.y(),
m_viewport.w() * 0.5,
m_viewport.h());
m_viewScene = types::Rectangle<int>(m_viewport.x() + m_viewport.w()*0.5,
m_viewport.y(),
m_viewport.w() * 0.5,
m_viewport.h());
GLint maxbuffers;
GLint maxTexUnits;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxbuffers);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTexUnits);
printf("max color attachements: %dn", maxbuffers);
printf("max texture units: %dn", maxTexUnits);
// initialize random seed
srand(time(NULL));
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
m_pShader = new Shader();
if(!m_pShader->create("shaders/basic.vert",
"shaders/basic.frag")) {
m_strErr = m_pShader->getErrorString();
return false;
}
if(!m_pShader->use()) {
m_strErr = m_pShader->getErrorString();
return false;
}
m_pShadowShader = new Shader();
if(!m_pShadowShader->create("shaders/shadow.vert",
"shaders/shadow.frag")) {
m_strErr = m_pShadowShader->getErrorString();
return false;
}
if(!m_pShadowShader->use()) {
m_strErr = m_pShadowShader->getErrorString();
return false;
}
Shader::useDefault();
engn::ProjectionData projData;
projData.m_fFovY = fovy;
projData.m_fNear = zNear;
projData.m_fFar = zFar;
projData.m_fAspect = (float)m_viewScene.w() / (float)m_viewScene.h();
projData.m_projectionMode = engn::PROJECTION_PERSPECTIVE;
projData.m_fLeft = -20.0f;
projData.m_fRight = 20.0f;
projData.m_fBottom = -20.0f;
projData.m_fTop = 20.0f;
projData.m_viewport = m_viewScene;
Vec3f start(-8.0f, 0.0f, 8.0f);
Vec3f end(1.0f, 0.0f, -4.0f);
m_cam.setProjectionData(projData);
m_cam.lookAt(Vec3f(-8, 0, 0), Vec3f(1, 0.2, 0));
projData.m_fAspect = (float)m_viewDepth.w() / (float)m_viewDepth.h();
// projData.m_projectionMode = engn::PROJECTION_ORTHO;
projData.m_viewport = m_viewDepth;
m_camShadow.setProjectionData(projData);
m_camShadow.lookAt(Vec3f(-10.0f, 0.0f, -4.2f), Vec3f(1.0f, 0.0f, 0.0f));
//m_camShadow.lookAt(start, end);
m_pSphere = new GLCube(this, 0.9f);
m_pSphere->moveTo(Vec3f(0.0f, 0.0f, -0.0f));
m_pWall = new GLWall(this);
m_pWall->moveTo(Vec3f(4.0f, 0.0f, -0.0f));
m_pWall->rotateY(-pi*0.5f);
// get the uniform locations from the basic shader
m_uniforms.shadowMap = m_pShader->getUniformLocation("shadowMapSampler");
if(m_uniforms.shadowMap == -1) {
printf("Could not get uniform location: shadowMapn");
return false;
}
m_uniforms.depthBiasMVP = m_pShader->getUniformLocation("depthBiasMVP");
if(m_uniforms.depthBiasMVP == -1) {
printf("Could not get uniform location: depthBiasMVPn");
return false;
}
m_uniforms.MVP = m_pShader->getUniformLocation("MVP");
if(m_uniforms.MVP == -1) {
printf("Could not get uniform location: MVPn");
return false;
}
// get the uniform locations from the shadow shader
m_uniforms.depthMVP = m_pShadowShader->getUniformLocation("depthMVP");
if(m_uniforms.depthMVP == -1) {
printf("Could not get uniform location: depthMVPn");
return false;
}
return InitFBO();
}
void GLPanelObjects::drawShadowScene(std::vector<GL3DObject *> vecObj) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fb);
glDrawBuffersARB(0, NULL);
glShadeModel(GL_FLAT);
glDisable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
const GLdouble x[] = {1.0, 0.0, 0.0, 0.0};
const GLdouble y[] = {0.0, 1.0, 0.0, 0.0};
const GLdouble z[] = {0.0, 0.0, 1.0, 0.0};
const GLdouble w[] = {0.0, 0.0, 0.0, 1.0};
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGendv(GL_S, GL_EYE_PLANE, x);
glTexGendv(GL_T, GL_EYE_PLANE, y);
glTexGendv(GL_R, GL_EYE_PLANE, z);
glTexGendv(GL_Q, GL_EYE_PLANE, w);
glClear(GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glDrawBuffer(GL_NONE); // No color buffer is drawn
glReadBuffer(GL_NONE);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glCullFace(GL_BACK);
const Mat4x4f mtxInvCam = inverse(m_camShadow.getTransformation());
const Mat4x4f mtxProj = m_camShadow.getProjection();
const Mat4x4f mtxDepthVP = mtxProj * mtxInvCam;
for(uint i = 0; i < vecObj.size(); ++i) {
const Mat4x4f &mtxModel = vecObj[i]->getTransformation();
const Mat4x4f mtxDepthMVP = mtxDepthVP * mtxModel;
if(!m_pShadowShader->use()) {
std::string strErr = m_pShadowShader->getErrorString();
printf("shader use error: %sn", strErr.c_str());
return;
}
glUniformMatrix4fvARB(m_uniforms.depthMVP,
1,
GL_FALSE,
&mtxDepthMVP(0, 0));
vecObj[i]->render();
}
}
void GLPanelObjects::render() {
const unsigned char *ks = SDL_GetKeyState(NULL);
moveCam(ks, m_cam);
if(ks[SDLK_PLUS]) {
m_camShadow.move(Vec3f(0, 0, -0.01));
}
if(ks[SDLK_MINUS]) {
m_camShadow.move(Vec3f(0, 0, 0.01));
}
// set the viewport
glViewport(0,
0,
m_viewDepth.w(),
m_viewDepth.h());
// place the objects to a vector
std::vector<GL3DObject *> vecObj;
vecObj.push_back(m_pWall);
vecObj.push_back(m_pSphere);
// draw into the shadow depth buffer
drawShadowScene(vecObj);
// switch to the screen buffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
int nUnit = 7;
// draw the scene to the window
if(!m_pShader->use()) {
std::string strErr = m_pShader->getErrorString();
printf("shader use error: %sn", strErr.c_str());
return;
}
// set the viewport
glViewport(m_viewScene.x(),
m_viewScene.y(),
m_viewScene.w(),
m_viewScene.h());
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniform1iARB(m_uniforms.shadowMap, nUnit);
glActiveTexture(GL_TEXTURE0 + nUnit);
glBindTexture(GL_TEXTURE_2D, m_depth_tex);
glEnable(GL_DEPTH_TEST);
const Mat4x4f mtxBias(0.5f, 0.0f, 0.0f, 0.5f, // row 0
0.0f, 0.5f, 0.0f, 0.5f, // row 1
0.0f, 0.0f, 0.5f, 0.5f, // row 2
0.0f, 0.0f, 0.0f, 1.0f); // row 3
const Mat4x4f mtxShadowCamProjection = m_camShadow.getProjection();
const Mat4x4f mtxShadowCamInv = inverse(m_camShadow.getTransformation());
const Mat4x4f mtxDepthBiasVP =
mtxBias * mtxShadowCamProjection * mtxShadowCamInv;
const Mat4x4f VP =
m_cam.getProjection() * inverse(m_cam.getTransformation());
//glDisable(GL_CULL_FACE);
for(size_t i = 0; i < vecObj.size(); ++i) {
// draw the scene to the window
if(!m_pShader->use()) {
std::string strErr = m_pShader->getErrorString();
printf("shader use error: %sn", strErr.c_str());
return;
}
glUniform1iARB(m_uniforms.shadowMap, nUnit);
const Mat4x4f &mtxModel = vecObj[i]->getTransformation();
const Mat4x4f mtxDepthBiasMVP = mtxDepthBiasVP * mtxModel;
const Mat4x4f mtxMVP = VP * mtxModel;
glUniformMatrix4fvARB(m_uniforms.depthBiasMVP,
1,
GL_FALSE,
&mtxDepthBiasMVP(0, 0));
glUniformMatrix4fvARB(m_uniforms.MVP,
1,
GL_FALSE,
&mtxMVP(0, 0));
vecObj[i]->render();
}
Shader::useDefault();
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_depth_tex);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q);
// set the viewport
glViewport(m_viewDepth.x(),
m_viewDepth.y(),
m_viewDepth.w(),
m_viewDepth.h());
// set the projection and model view matrices
GLWidget::prepareOrtho(m_viewDepth);
glBegin(GL_QUADS); {
//glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2d(0.0, 0.0); glVertex2d(0.0, 0.0);
glTexCoord2d(1.0, 0.0); glVertex2d(m_viewDepth.w(), 0.0);
glTexCoord2d(1.0, 1.0); glVertex2d(m_viewDepth.w(), m_viewDepth.h());
glTexCoord2d(0.0, 1.0); glVertex2d(0.0, m_viewDepth.h());
} glEnd();
{
Mat4x4f mtxProj = m_camShadow.getProjection();
Mat4x4f mtxView = inverse(m_camShadow.getTransformation());
Vec4f poi = mtxProj * mtxView * Vec4f(m_hitPoint, 1.0f);
poi /= poi[3];
float x = m_viewDepth.x() + (poi[0]*0.5f + 0.5f) * m_viewDepth.w();
float y = m_viewDepth.y() + (poi[1]*0.5f + 0.5f) * m_viewDepth.h();
glPointSize(5.0f);
glBegin(GL_POINTS);
glVertex2f(x, y);
glEnd();
}
}
我开始怀疑这里有一个驱动程序错误,但在很多情况下,错误通常在代码中。
我在Linux Mint Debian Edition中运行这个应用程序,我的lspci说:
Intel Corporation Mobile 945GM/GMS、943/940GML Express集成图形控制器(03版)
glxinfo说:
server glx version string: 1.4
client glx version string: 1.4
GLX version: 1.4
OpenGL version string: 1.4 Mesa 9.1.6
我花了几个小时在谷歌上搜索类似的问题,但没有任何运气。我很有信心我的阴影映射数学是可以的,但还有一些其他问题我没有看到。如果有人能阐明这一点,我会很高兴在这里谈论它。
我已经验证了这是一个驱动程序错误或类似的东西。刚刚用支持glsl1.30的grapchics卡测试了代码,应用程序正常工作。