我正在尝试在OpenGL / C ++渲染器中实现级联阴影映射。当正交矩阵由任意数字构建并停留在场景的原点时,我已经成功地实现了定向阴影映射。从我所看到的一切来看,我遇到的问题是基于视锥的视图确定正交矩阵的边界。
我发现了两种计算方式,两者都相关;第一种方法使用当前切片的近/远平面创建投影矩阵,然后将其反转。然后,我将NDC空间(每个轴的范围为[-1:+1])中的立方体的角,分别乘以反向投影矩阵,反向视图矩阵,然后乘以光空间矩阵;然后将整个内容除以角落的“ w”。
vec4 corners[8] =
{
// Near plane
{ 1, 1, 1, 1 },
{ -1, 1, 1, 1 },
{ 1, -1, 1, 1 },
{ -1, -1, 1, 1 },
// Far plane
{ 1, 1, -1, 1 },
{ -1, 1, -1, 1 },
{ 1, -1, -1, 1 },
{ -1, -1, -1, 1 }
};
// Create a projection matrix for this cascade.
// This will transform the corner from camera space to view space.
mat4 proj = mat4::perspective(fov, aspect, near_plane, far_plane);
// B.inverted()*A.inverted() == C.inverted() == (A*B).inverted()
// This is now an inverted viewproj matrix, which transforms the corner from camera space to world space.
mat4 inv_viewproj = (proj * view_matrix).inverted();
// This is the matrix that transforms each corner from camera space to light space.
// (this_frame.view is the light-space view matrix)
mat4 matrix = this_frame.view * matrix;
float minx = FLT_MAX;
float maxx = -FLT_MAX;
float miny = FLT_MAX;
float maxy = -FLT_MAX;
float minz = FLT_MAX;
float maxz = -FLT_MAX;
// For each frustum corner...
for (uint32_t i = 0; i < 8; ++i)
{
corners[i] = matrix * corners[i];
corners[i] /= corners[i].w;
minx = min(minx, corners[i].x);
maxx = max(maxx, corners[i].x);
miny = min(miny, corners[i].y);
maxy = max(maxy, corners[i].y);
minz = min(minz, corners[i].z);
maxz = max(maxz, corners[i].z);
}
this_frame.proj = mat4::ortho(minx, maxx, miny, maxy, minz, maxz);
第二种方法是相似的,它以视图空间中的一组角开始,从而减少了用w除的需要,并节省了一些矩阵求逆。
mat4 inv_view = view_matrix.inverted();
float tan_half_hfov = tanf(to_radians(fov * 0.5f));
float tan_half_vfov = tanf(to_radians((fov * 0.5f) / aspect));
//...
float xn = near_plane * tan_half_hfov;
float yn = near_plane * tan_half_vfov;
float xf = far_plane * tan_half_hfov;
float yf = far_plane * tan_half_vfov;
// These are the corners of the screen in view space.
// We'll be transforming these in order to get the dimensions of the view frustum.
vec4 corners[8] =
{
// Near plane
{ xn, yn, -near_plane, 1 },
{ -xn, yn, -near_plane, 1 },
{ xn, -yn, -near_plane, 1 },
{ -xn, -yn, -near_plane, 1 },
// Far plane
{ xf, yf, -far_plane, 1 },
{ -xf, yf, -far_plane, 1 },
{ xf, -yf, -far_plane, 1 },
{ -xf, -yf, -far_plane, 1 }
};
// This is the matrix that transforms each corner from camera space to light space.
mat4 matrix = this_frame.view * inv_view;
float minx = FLT_MAX;
float maxx = -FLT_MAX;
float miny = FLT_MAX;
float maxy = -FLT_MAX;
float minz = FLT_MAX;
float maxz = -FLT_MAX;
// For each frustum corner...
for (uint32_t i = 0; i < 8; ++i)
{
corners[i] = matrix * corners[i];
minx = min(minx, corners[i].x);
maxx = max(maxx, corners[i].x);
miny = min(miny, corners[i].y);
maxy = max(maxy, corners[i].y);
minz = min(minz, corners[i].z);
maxz = max(maxz, corners[i].z);
}
this_frame.proj = mat4::ortho(minx, maxx, miny, maxy, minz, maxz);
两者均产生不正确的结果,但有点接近正确的结果。第二种方法似乎可以产生更紧密的盒子。
有趣的是,当相机在光空间中沿x轴移动时,两者似乎都可以正常工作。每当相机在明亮空间中沿y / z轴移动时,都会得到一些非常错误的结果。
以下是我正在谈论的图片。每对中的第一个是根据正常摄影机角度的场景,第二个是从灯光的角度来看(第一个级联,在第一个级联正常工作之前,我什至不尝试第二个级联)。红色区域是第一个级联中应该包括的区域,绿色区域是后来的级联中包含的区域,而无阴影区域位于视锥范围之外。
(这些屏幕截图是使用方法#1拍摄的]
到目前为止一切顺利,对吗?好...
一段时间以来,我认为问题可能出在我的mat4::inverted()
函数中,但是我尝试了2种不同的实现,它们都给出了相同的结果。我当前正在使用的是:
mat4 mat4::inverted() const
{
float invdet = 1.0f / determinant();
// Note: This constructor for mat4 accepts a series of 16 floats as a row-major matrix,
// as that's simply more natural to write out,
// then converts it automatically to the correct column-major storage.
return {
(m12*m23*m31 - m13*m22*m31 + m13*m21*m32 - m11*m23*m32 - m12*m21*m33 + m11*m22*m33) * invdet,
(m03*m22*m31 - m02*m23*m31 - m03*m21*m32 + m01*m23*m32 + m02*m21*m33 - m01*m22*m33) * invdet,
(m02*m13*m31 - m03*m12*m31 + m03*m11*m32 - m01*m13*m32 - m02*m11*m33 + m01*m12*m33) * invdet,
(m03*m12*m21 - m02*m13*m21 - m03*m11*m22 + m01*m13*m22 + m02*m11*m23 - m01*m12*m23) * invdet,
(m13*m22*m30 - m12*m23*m30 - m13*m20*m32 + m10*m23*m32 + m12*m20*m33 - m10*m22*m33) * invdet,
(m02*m23*m30 - m03*m22*m30 + m03*m20*m32 - m00*m23*m32 - m02*m20*m33 + m00*m22*m33) * invdet,
(m03*m12*m30 - m02*m13*m30 - m03*m10*m32 + m00*m13*m32 + m02*m10*m33 - m00*m12*m33) * invdet,
(m02*m13*m20 - m03*m12*m20 + m03*m10*m22 - m00*m13*m22 - m02*m10*m23 + m00*m12*m23) * invdet,
(m11*m23*m30 - m13*m21*m30 + m13*m20*m31 - m10*m23*m31 - m11*m20*m33 + m10*m21*m33) * invdet,
(m03*m21*m30 - m01*m23*m30 - m03*m20*m31 + m00*m23*m31 + m01*m20*m33 - m00*m21*m33) * invdet,
(m01*m13*m30 - m03*m11*m30 + m03*m10*m31 - m00*m13*m31 - m01*m10*m33 + m00*m11*m33) * invdet,
(m03*m11*m20 - m01*m13*m20 - m03*m10*m21 + m00*m13*m21 + m01*m10*m23 - m00*m11*m23) * invdet,
(m12*m21*m30 - m11*m22*m30 - m12*m20*m31 + m10*m22*m31 + m11*m20*m32 - m10*m21*m32) * invdet,
(m01*m22*m30 - m02*m21*m30 + m02*m20*m31 - m00*m22*m31 - m01*m20*m32 + m00*m21*m32) * invdet,
(m02*m11*m30 - m01*m12*m30 - m02*m10*m31 + m00*m12*m31 + m01*m10*m32 - m00*m11*m32) * invdet,
(m01*m12*m20 - m02*m11*m20 + m02*m10*m21 - m00*m12*m21 - m01*m10*m22 + m00*m11*m22) * invdet };
}
float mat4::determinant() const
{
return
m03*m12*m21*m30 - m02*m13*m21*m30 - m03*m11*m22*m30 + m01*m13*m22*m30 +
m02*m11*m23*m30 - m01*m12*m23*m30 - m03*m12*m20*m31 + m02*m13*m20*m31 +
m03*m10*m22*m31 - m00*m13*m22*m31 - m02*m10*m23*m31 + m00*m12*m23*m31 +
m03*m11*m20*m32 - m01*m13*m20*m32 - m03*m10*m21*m32 + m00*m13*m21*m32 +
m01*m10*m23*m32 - m00*m11*m23*m32 - m02*m11*m20*m33 + m01*m12*m20*m33 +
m02*m10*m21*m33 - m00*m12*m21*m33 - m01*m10*m22*m33 + m00*m11*m22*m33;
}
我在做什么错?
vec4 corners[8] =
{
// Near plane
{ 1, 1, 1, 1 },
{ -1, 1, 1, 1 },
{ 1, -1, 1, 1 },
{ -1, -1, 1, 1 },
// Far plane
{ 1, 1, -1, 1 },
{ -1, 1, -1, 1 },
{ 1, -1, -1, 1 },
{ -1, -1, -1, 1 }
};
让我告诉你为什么这里错了。您正在寻找屏幕。近处是深度0。远处是深度1。
vec4 corners[8] =
{
// Near plane
{ 1, 1, 0, 1 },
{ -1, 1, 0, 1 },
{ 1, -1, 0, 1 },
{ -1, -1, 0, 1 },
// Far plane
{ 1, 1, 1, 1 },
{ -1, 1, 1, 1 },
{ 1, -1, 1, 1 },
{ -1, -1, 1, 1 }
};
.....// somestuff
// For each frustum corner...
for (uint32_t i = 0; i < 8; ++i)
{
corners[i] = invVP * corners[i];
corners[i] /= corners[i].w;
... go on....
}
让我知道是否可行。