级联阴影图-正投影问题

时间:2019-02-17 10:51:35

标签: c# opengl 3d

我正在尝试将OpenGL游戏中的阴影贴图渲染升级为this OGLdev tutorial中所述的级联阴影贴图方法。一切工作正常,但在某些摄影机角度下,阴影贴图不能覆盖整个视锥。

There's a quick video of the problem here,在这里我在最终渲染的场景和中间的阴影切片纹理之间切换。您会看到播放器的阴影在阴影纹理(白色视图)中被切除得太早了。

每个阴影贴图都渲染到其自己的FBO中,并在稍后进行后期处理时进行采样(简单的fragDepth> textureDepth)。代码如下:

const int SLICE_AMOUNT = 3;
Matrix4[] lightViewSpaces = new Matrix4[SLICE_AMOUNT];
Matrix4[] shadowProjections = new Matrix4[SLICE_AMOUNT];
OrthoInfo[] orthoInfo = new OrthoInfo[SLICE_AMOUNT];

const float zNear = 1;
const float zFar = 256;

Vector3 up = new Vector3(0, 1, 0);
Vector3 LightDirection = new Vector3(-1, 1, 1).Normalized();

float[] cascadeEnds = new float[SLICE_AMOUNT + 1]
{
    zNear,
    32f,
    64f,
    zFar,
};        

Vector3[] frustumCenters = new Vector3[SLICE_AMOUNT];


public void RenderShadowShader()
{
    CalculateOrthographicFrustums();

    // Create shadows maps at each view frustum slice
    for (int i = 0; i < SLICE_AMOUNT; i++)
    {
        // Bind the shadow map FBO for this slice
        Gl.BindFramebuffer(FramebufferTarget.DrawFramebuffer, CurrentCollection.shadowFBO[i]);

        var o = orthoInfo[i];

        // Combine the light view projection and orthographic projection                                   -200 for leniency
        shadowProjections[i] = lightViewSpaces[i] * Matrix4.CreateOrthoProjection(o.l, o.t, o.r, o.b, o.n - 200, o.f - o.n);

        Gl.Viewport(0, 0, (int)screenWidth, (int)screenHeight);
        Gl.Clear(ClearBufferMask.DepthBufferBit);
        Gl.Enable(EnableCap.DepthTest);
        Gl.Disable(EnableCap.CullFace);

        // Render scene objects to the shadowFBO depth buffer
        RenderShadowMeshes(shadowProjections[i]);
    }
}

// Convert each view frustum slice to light space
void CalculateOrthographicFrustums()
{
    // Get the world view projection from player camera
    Matrix4 Cam = Matrix4.CreateLookAt(player.headPosition, player.headPosition - player.LookAtVector(), up);
    Matrix4 worldViewProj = Cam.Inverse();

    var aspectRatio = screenHeight / screenWidth;
    var tanHalfHFOV = (float)Math.Tan(Math.PI * (parent.FieldOfView / 180.0) / 2.0);
    var tanHalfVFOV = (float)Math.Tan(Math.PI * (parent.FieldOfView / 180.0) * aspectRatio / 2.0);

    for (int i = 0; i < SLICE_AMOUNT; i++)
    {
        float xn = cascadeEnds[i] * tanHalfHFOV;
        float xf = cascadeEnds[i + 1] * tanHalfHFOV;
        float yn = cascadeEnds[i] * tanHalfVFOV;
        float yf = cascadeEnds[i + 1] * tanHalfVFOV;

        Vector4[] frustumCorners =
        { 
            // Near face
            new Vector4(xn,   yn, cascadeEnds[i], 1.0),
            new Vector4(-xn,  yn, cascadeEnds[i], 1.0),
            new Vector4(xn,  -yn, cascadeEnds[i], 1.0),
            new Vector4(-xn, -yn, cascadeEnds[i], 1.0),

            // Far face                 
            new Vector4(xf,   yf, cascadeEnds[i + 1], 1.0),
            new Vector4(-xf,  yf, cascadeEnds[i + 1], 1.0),
            new Vector4(xf,  -yf, cascadeEnds[i + 1], 1.0),
            new Vector4(-xf, -yf, cascadeEnds[i + 1], 1.0)
        };

        // Store the frustum corners in light space
        var frustumCornersLS = new Vector4[8];

        // Transform frustum corners to world space
        for (int j = 0; j < 8; j++)
            frustumCorners[j] *= worldViewProj;

        frustumCenters[i] = GetFrustumCenter(frustumCorners);

        // Create light view projection from from world-space frustum center
        lightViewSpaces[i] = Matrix4.CreateLookAt(frustumCenters[i] + LightDirection, frustumCenters[i], up);

        // Convert frustum corners to light-space
        for (int j = 0; j < 8; j++)
            frustumCornersLS[j] = frustumCorners[j] * lightViewSpaces[i];

        // Calculate AABB of light-space frustum
        GetMinMax(frustumCornersLS, out Vector3 min, out Vector3 max);

        // Store AABB in orthoInfo
        orthoInfo[i].r = max.X;
        orthoInfo[i].l = min.X;
        orthoInfo[i].b = min.Y;
        orthoInfo[i].t = max.Y;
        orthoInfo[i].f = max.Z;
        orthoInfo[i].n = min.Z;
    }
}

我认为这可能是问题的一部分是将平截头角转换为光空间。虽然它可以正确设置阴影的位置,但是当我更改相机角度时似乎无法应付:

// Create light view projection from from world-space frustum center
lightViewSpaces[i] = Matrix4.CreateLookAt(frustumCenters[i] + LightDirection, frustumCenters[i], up);

任何对此的帮助将不胜感激。愿意提供更多图片和代码。

0 个答案:

没有答案