使用延迟渲染器实现阴影映射(OpenGL 4.1,GLSL)

4

我在StackOverflow和一些书籍中读了几篇文章,但我找不到代码中的错误。我有一个延迟渲染器,并且在G缓冲区中保存了反射率、深度和法线(包括镜面反射)。此外,从场景顶部创建了一个光源的阴影图。

它看起来像这样:

反射率 (R8G8B8A8): Albedo

法线 (R8G8B8A8): Normal

线性深度 (F32): enter image description here

光源的阴影图 (线性深度)(距离摄像机500米): enter image description here

好的。我的延迟着色器和阴影贴图的代码如下: 我已经上传了行主要矩阵。

顶点着色器:

layout(row_major) uniform UVSViewMatrix
{
    mat4 m_ScreenMatrix;
};

layout(location = 0) in vec3 VertexPosition;

smooth out vec2 PSTexCoord;

void main()
{
    vec4 Position = vec4(VertexPosition.xyz, 1.0f);

    PSTexCoord = Position.xy;

    gl_Position = Position * m_ScreenMatrix;
}

片元着色器:

#version 410

// -----------------------------------------------------------------------------
// Input from engine
// -----------------------------------------------------------------------------
layout(row_major) uniform UPSCameraProperties
{
    mat4 m_ProjectionMatrix;
    mat4 m_CameraView;
    vec3 m_CameraPosition;
    vec3 m_CameraDirection;
};

layout(row_major) uniform UPSLightProperties
{
    mat4 m_LightProjection;
    mat4 m_LightView;
    vec4 m_LightPosition;
    vec4 m_LightAmbientIntensity;
    vec4 m_LightDiffuseIntensity;
    vec4 m_LightSpecularIntensity;
};

uniform sampler2D PSTextureAlbedo;
uniform sampler2D PSTextureNormalSpecular;
uniform sampler2D PSTextureDepth;
uniform sampler2D PSTextureShadowMap;

// -----------------------------------------------------------------------------
// Input from vertex shader
// ----------------------------------------------------------------- ------------
smooth in vec2 PSTexCoord;

// -----------------------------------------------------------------------------
// Output to systembuffer
// -----------------------------------------------------------------------------
layout (location = 0) out vec4 PSOutput;

// -----------------------------------------------------------------------------
// Functions
// -----------------------------------------------------------------------------
vec3 GetViewSpacePositionFromDepth(float _Depth, vec2 _ScreenPosition, mat4 _InvertedProjectionMatrix)
{
    // -----------------------------------------------------------------------------
    // Information from:
    // http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/
    // -----------------------------------------------------------------------------
    vec4 ScreenPosition;

    ScreenPosition.x = _ScreenPosition.x * 2.0f - 1.0f;
    ScreenPosition.y = _ScreenPosition.y * 2.0f - 1.0f;
    ScreenPosition.z = _Depth * 2.0f - 1.0f;
    ScreenPosition.w = 1.0f;

    // -----------------------------------------------------------------------------
    // Transform by the inverse projection matrix
    // -----------------------------------------------------------------------------
    vec4 VSPosition = ScreenPosition * _InvertedProjectionMatrix;

    // -----------------------------------------------------------------------------
    // Divide by w to get the view-space position
    // -----------------------------------------------------------------------------
    return (VSPosition.xyz / VSPosition.w);
}

// -----------------------------------------------------------------------------

float GetShadowAtPosition(vec3 _WSPosition)
{
    // -----------------------------------------------------------------------------
    // Set worls space coord into light projection by multiply with light
    // view and projection matrix;
    // -----------------------------------------------------------------------------
    vec4 LSPosition = vec4(_WSPosition, 1.0f) * m_LightView * m_LightProjection;

    // -----------------------------------------------------------------------------
    // Divide xyz by w to get the position in light view's clip space.
    // -----------------------------------------------------------------------------
    LSPosition.xyz /= LSPosition.w;

    // -----------------------------------------------------------------------------
    // Get uv texcoords for this position
    // -----------------------------------------------------------------------------
    vec3 ShadowCoord = LSPosition.xyz * 0.5f + 0.5f;

    // -----------------------------------------------------------------------------
    // Get final depth at this texcoord and compare it with the real
    // position z value (do a manual depth test)
    // -----------------------------------------------------------------------------
    float DepthValue = texture( PSTextureShadowMap, vec2(ShadowCoord.x, ShadowCoord.y) ).r;

    float Shadow = 1.0f;

    if (ShadowCoord.z > DepthValue)
    {
        Shadow = 0.3f;
    }

    return Shadow;
}

// -----------------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------------
void main() 
{
    // -----------------------------------------------------------------------------
    // Get informations from g-buffer
    // -----------------------------------------------------------------------------
    vec2 TexCoord = vec2(PSTexCoord.s, 1.0f - PSTexCoord.t);

    vec4  AlbedoColor      = texture(PSTextureAlbedo        , TexCoord);
    vec4  NormalSpec       = texture(PSTextureNormalSpecular, TexCoord);
    float Depth            = texture(PSTextureDepth         , TexCoord).r;
    vec3  VSPosition       = GetViewSpacePositionFromDepth(Depth, TexCoord, inverse(m_ProjectionMatrix));
    vec3  Normal           = normalize(NormalSpec.xyz);
    float SpecularExponent = NormalSpec.w;

    vec4  WSPosition = vec4(VSPosition, 1.0f) * inverse(m_CameraView);

    // -----------------------------------------------------------------------------
    // Compute lighting (Light Accumulation)
    // -----------------------------------------------------------------------------
    vec3 CameraPosition  = m_CameraPosition.xyz;
    vec3 LightPosition   = m_LightPosition.xyz;
    vec3 EyeDirection    = WSPosition.xyz - CameraPosition;
    vec3 LightDirection  = normalize(LightPosition - WSPosition.xyz);
    vec3 LightReflection = normalize(-reflect(LightDirection, Normal));

    vec4 AmbientColor  = m_LightAmbientIntensity;
    vec4 DiffuseColor  = clamp(m_LightDiffuseIntensity * max(dot(Normal, LightDirection), 0.0f), 0.0f, 1.0f);
    vec4 SpecularColor = clamp(m_LightSpecularIntensity * pow(max(dot(LightReflection, EyeDirection), 0.0f), SpecularExponent), 0.0f, 1.0f);

    float Shadow = GetShadowAtPosition(WSPosition.xyz);

    // -----------------------------------------------------------------------------
    // Final result
    // -----------------------------------------------------------------------------
    PSOutput = vec4((AlbedoColor * (AmbientColor + DiffuseColor) * Shadow).xyz, 1.0f);
}

最终的结果如下: 始终带有阴影的结果

有人能发现我的错误吗?

一些测量结果:

  • ShadowCoord.xy 始终为0.5,0.5

  • ShadowCoord.z 看起来是1.0f

以下是某些变量值:

   LightProjection
    (
     1.29903805f, 0.0f,         0.0,          0.0f,
     0.0f,        1.73205066f,  0.0f,         0.0f,
     0.0f,        0.0f,        -1.00024426f, -1.0f,
     0.0f,        0.0f,        -1.00024426f,  0.0f
     );

   LightView
    (
     1.0f, 0.0f,     0.0f, 0.0f,
     0.0f, 1.0f,     0.0f, 0.0f,
     0.0f, 0.0f,     1.0f, 0.0f,
     0.0f, 0.0f, -1500.0f, 1.0f
     );

   CameraProjection
    (
     1.29903805f, 0.0f,         0.0f,         0.0f,
     0.0f,        1.73205066f,  0.0f,         0.0f,
     0.0f,        0.0f,        -1.00024426f, -1.0f,
     0.0f,        0.0f,        -1.00024426f,  0.0f
     );

   CameraView
    (
     1.0f, 0.0f,    0.0f, 0.0f,
     0.0f, 1.0f,    0.0f, 0.0f,
     0.0f, 0.0f,    1.0f, 0.0f,
     0.0f, 0.0f, -1000.0f, 1.0f
     );

从你的矩阵来看,你的光线几乎与相机平行。起初我被你的行主要矩阵绊住了,但是光源位于(0,0,-1000.0),相机位于(0,0,-1500.0),并且x/y/z轴的方向相同。你应该考虑将光源移动到X或Y方向的其他位置,看看是否会有变化。 - Andon M. Coleman
谢谢你的建议。但是那并没有改变什么。我选择这些位置是因为很容易概括理论。 - Tobias
1个回答

1
我能看到的一个问题是线性深度和双曲深度之间的差异。看起来你的“线性深度(F32)”G缓冲深度实际上是双曲深度,这很好,因为代码期望这样。然而,光的深度实际上是线性的,但在透视除法后会与剪辑空间深度进行比较。
最简单的方法可能是使光缓冲区的深度双曲线(gl_FragCoord.z),但改为与眼空间Z进行比较也应该可以工作:
float ShadowCoordZ = -(vec4(_WSPosition, 1.0f) * m_LightView).z;

关于“ShadowCoord.xy始终为0.5, 0.5”的部分令人困惑。G-buffer纹理坐标似乎有效。我无法确定阴影是否有效,但是光照方程是否有效?如果有效,也许矩阵有问题?我还会将矩阵的逆传递为uniform变量,这样就不必在着色器中计算时间了。

好的。有趣的一点。有趣的事实是,每当我处于阴影区域内时,我的屏幕就会变暗。如果我待在光线照射的地方,我的屏幕就正常了。似乎从深度到位置(世界空间位置)的重建是错误的。这个向量中的Z值对于每个像素都是相同的。那应该是错的,对吧? - Tobias
好的。在计算视图空间位置时出现了一个错误。我已经在g-buffer中创建了一个位置纹理。现在我从纹理中获取视图空间位置。是的,有一些“正确”的阴影。问题是:使用深度计算视图空间位置出了什么问题? - Tobias
如果深度实际上是透视除法后的0到1双曲深度(即使用gl_FragCoord.z编写),而不是线性深度,那么您所拥有的看起来非常直截了当(除了我对所有转置矩阵都不太熟悉)。@TobiasSchwandt - jozxyqk
谢谢大家的建议。最后我在矩阵中发现了一个错误...因此我没有得到正确的结果。尽管jozxyqk的建议是正确的。我更改了我的源代码,现在它可以正常工作。 - Tobias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接