我正在尝试将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);
任何对此的帮助将不胜感激。愿意提供更多图片和代码。