阴影贴图未正确投影

Posted

技术标签:

【中文标题】阴影贴图未正确投影【英文标题】:Shadow Mapping is not projected correctly 【发布时间】:2014-04-16 15:18:38 【问题描述】:

我在 OpenGL 图形引擎中实现阴影映射时遇到问题。

在第一阶段,我从光线的角度将阴影贴图渲染到帧缓冲区对象(附加了深度纹理):

Vector3f lightPosition = Vector3f(mainLight->getPosition().x, mainLight->getPosition().y, mainLight->getPosition().z);

shadowMapFBO->BindForWriting();

glUseProgramObjectARB(0);

glViewport(0, 0, screenWidth * SHADOW_Q, screenHeight * SHADOW_Q);

glClear(GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);

glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); //Avoid self shadowing

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-20, 20, -20, 20, 0.0f, +300.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(lightPosition.X, lightPosition.Y, lightPosition.Z, 0, 0, 0, 0, 1, 0);

drawSceneTree();

setTextureMatrix();

我使用函数drawSceneTree()绘制所有场景,并使用函数setTextureMatrix()将光照矩阵存储到OpenGL TEXTURE7中,内容是这样的:

static double modelView[16];
static double projection[16];

const GLdouble bias[16] = 
    0.5, 0.0, 0.0, 0.0,
    0.0, 0.5, 0.0, 0.0,
    0.0, 0.0, 0.5, 0.0,
    0.5, 0.5, 0.5, 1.0
;

// Grab modelview and transformation matrices
glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
glGetDoublev(GL_PROJECTION_MATRIX, projection);

glActiveTextureARB(GL_TEXTURE7);
glMatrixMode(GL_TEXTURE);

glLoadIdentity();
glLoadMatrixd(bias);

// concatating all matrice into one.
glMultMatrixd(projection);
glMultMatrixd(modelView);


// Go back to normal matrix mode
glMatrixMode(GL_MODELVIEW);

然后我从相机的角度渲染场景,并使用着色器渲染阴影:

glViewport(0, 0, screenWidth, screenHeight);

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

// Clear previous frame values
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_LIGHTING);

//Using the shadow shader
glUseProgram(shadowMapFBO->getShader());
glUniform1iARB(shadowMapFBO->getShadowMapUniform(), 7);
glUniform1iARB(shadowMapFBO->getTextureUniform(), 0);

shadowMapFBO->BindForReading(7);

setupMatrices(0, 4, -13, 0, 5, 0);

glEnable(GL_CULL_FACE);

glCullFace(GL_BACK);
drawSceneTree();

setupMatrices() 函数设置投影和模型视图矩阵。我将shadowMapFBO绑定到OpenGL TEXTURE7中,使用函数BindForReading(7),其内容为:

glActiveTexture(GL_TEXTURE0 + TextureUnit); 
glBindTexture(GL_TEXTURE_2D, depthTextureId);

最后,顶点和片段着色器如下:

顶点:

varying vec4 ShadowCoord;

varying vec3 normal;
varying vec3 vertex_to_light_vector;
varying vec2 texture_coordinate;

void main()

        ShadowCoord = gl_TextureMatrix[7] * gl_Vertex;

        gl_Position = ftransform();

        gl_FrontColor = gl_Color;

        normal = gl_NormalMatrix * gl_Normal;

        vec4 vertex_in_modelview_space = gl_ModelViewMatrix * gl_Vertex;

        vertex_to_light_vector = vec3(gl_LightSource[0].position -vertex_in_modelview_space);

        texture_coordinate = vec2(gl_MultiTexCoord0);

片段:

uniform sampler2D ShadowMap;
uniform sampler2D Texture;

varying vec4 ShadowCoord;
varying vec3 normal;
varying vec3 vertex_to_light_vector;
varying vec2 texture_coordinate;


void main()
   
    const vec4 AmbientColor = vec4(0.7, 0.7, 0.7, 1.0);
    const vec4 DiffuseColor = vec4(0.5, 0.5, 0.5, 1.0);

    vec3 normalized_normal = normalize(normal);
    vec3 normalized_vertex_to_light_vector = normalize(vertex_to_light_vector);

    float DiffuseTerm = clamp(dot(normal, vertex_to_light_vector), 0.0, 1.0);


    vec4 shadowCoordinateWdivide = ShadowCoord / ShadowCoord.w ;

    // Used to lower moiré pattern and self-shadowing
    shadowCoordinateWdivide.z += 0.0005;

    float distanceFromLight = texture2D(ShadowMap,shadowCoordinateWdivide.st).z;


    float shadow = 1.0;
    if (ShadowCoord.w > 0.0)
        shadow = distanceFromLight < shadowCoordinateWdivide.z ? 0.5 : 1.0 ;


    gl_FragColor = texture2D(Texture, texture_coordinate) * shadow * (AmbientColor + DiffuseColor * DiffuseTerm);
    gl_FragColor.a = 1.0;


我将游戏角色的阴影投射到场景的所有对象中。我已经将它记录在一个 gif 中:

https://dl.dropboxusercontent.com/u/658766/captura.gif

P.S: 阴影贴图也在屏幕右上角渲染以进行调试。

【问题讨论】:

ShadowCoord 应该是偏差 * lightMVP * 模型 * 顶点。 顺便说一句,没有称为“模型视图”空间的坐标空间。您的困惑可能来自一个名为“ModelView”的矩阵。但这实际上是两个独立矩阵的组合(模型:对象->世界和视图:世界->眼睛)。乘以该矩阵后,您的实际顶点位于“视图”、“眼睛”或“相机”(是的,同一事物的名称太多了:-\)空间。 【参考方案1】:

您的阴影坐标必须由 ModelWold 矩阵处理(没有投影或视图矩阵)。在我的着色器中,我用这个来计算它:

 ShadowCoord = gl_TextureMatrix[7] *  inverse(modelView) * gl_ModelViewMatrix * gl_Vertex;

但您可以将其作为制服传递给您的着色器。这是在将 ModelViewProjectionMatrix 设置为查看原点后应用于场景中每个单独对象的变换矩阵。 (肯定有更聪明的方法来处理这个问题,但我没有深入挖掘)

【讨论】:

以上是关于阴影贴图未正确投影的主要内容,如果未能解决你的问题,请参考以下文章

✠OpenGL-8-阴影

Shadowmap 适用于正射投影,但不适用于透视投影

带有立方体贴图的 OpenGL 点光阴影映射

全向阴影贴图

确定级联阴影贴图分割范围

OpenGL 阴影贴图