我目前遇到的问题是来自移动(类似太阳的)光源的定向光影贴图。
当我最初实现时,光投影矩阵被计算为3D,并且阴影贴图显得很漂亮。然后我了解到,对于我正在尝试做的事情,正交投影会更好地工作,但我很难用适当的投影矩阵代替。
每个蜱,太阳沿着圆圈移动一定量,正如人们所期望的那样。我使用自己开发的“lookAt”方法来确定正确的查看矩阵。因此,例如,日光从早上6点到晚上6点发生。当太阳位于9AM位置(45度)时,它应该查看原点并将阴影贴图渲染到帧缓冲区。正投影似乎正在发生的事情是它不会向原点“向下倾斜”。它只是直接沿着Z轴向下看。事情在早上6点和下午6点看起来很好,但例如中午12点,绝对没有显示任何内容。
以下是我的设置方法:
原始3D投影矩阵:
Matrix4f projectionMatrix = new Matrix4f();
float aspectRatio = (float) width / (float) height;
float y_scale = (float) (1 / cos(toRadians(fov / 2f)));
float x_scale = y_scale / aspectRatio;
float frustum_length = far_z - near_z;
projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = (far_z + near_z) / (near_z - far_z);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * near_z * far_z) / frustum_length);
LookAt方法:
public Matrix4f lookAt( float x, float y, float z,
float center_x, float center_y, float center_z ) {
Vector3f forward = new Vector3f( center_x - x, center_y - y, center_z - z );
Vector3f up = new Vector3f( 0, 1, 0 );
if ( center_x == x && center_z == z && center_y != y ) {
up.y = 0;
up.z = 1;
}
Vector3f side = new Vector3f();
forward.normalise();
Vector3f.cross(forward, up, side );
side.normalise();
Vector3f.cross(side, forward, up);
up.normalise();
Matrix4f multMatrix = new Matrix4f();
multMatrix.m00 = side.x;
multMatrix.m10 = side.y;
multMatrix.m20 = side.z;
multMatrix.m01 = up.x;
multMatrix.m11 = up.y;
multMatrix.m21 = up.z;
multMatrix.m02 = -forward.x;
multMatrix.m12 = -forward.y;
multMatrix.m22 = -forward.z;
Matrix4f translation = new Matrix4f();
translation.m30 = -x;
translation.m31 = -y;
translation.m32 = -z;
Matrix4f result = new Matrix4f();
Matrix4f.mul( multMatrix, translation, result );
return result;
}
正交投影(使用宽度100,高度75,接近1.0,远100)我尝试了许多不同的值:
Matrix4f projectionMatrix = new Matrix4f();
float r = width * 1.0f;
float l = -width;
float t = height * 1.0f;
float b = -height;
projectionMatrix.m00 = 2.0f / ( r - l );
projectionMatrix.m11 = 2.0f / ( t - b );
projectionMatrix.m22 = 2.0f / (far_z - near_z);
projectionMatrix.m30 = - ( r + l ) / ( r - l );
projectionMatrix.m31 = - ( t + b ) / ( t - b );
projectionMatrix.m32 = -(far_z + near_z) / (far_z - near_z);
projectionMatrix.m33 = 1;
阴影贴图顶点着色器:
#version 150 core
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
in vec4 in_Position;
out float pass_Position;
void main(void) {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_Position;
pass_Position = gl_Position.z;
}
阴影贴图片段着色器:
#version 150 core
in vec4 pass_Color;
in float pass_Position;
layout(location=0) out float fragmentdepth;
out vec4 out_Color;
void main(void) {
fragmentdepth = gl_FragCoord.z;
}
我觉得我在这里缺少一些非常简单的东西。正如我所说,这适用于3D投影矩阵,但我希望阴影在用户穿越世界时保持不变,这对于定向照明和正投影是有意义的。
答案 0 :(得分:0)
实际上,谁告诉过你使用正交投影矩阵对阴影贴图来说是一个好主意?这可能适用于太阳这样的东西,它们实际上是无限远的,但对于局部光线来说,透视是非常相关的。你必须要小心透视投影和阴影贴图,因为采样频率随距离而变化,你会在某些距离上获得很多精度,而在其他距离上则不够,除非你使用像级联或透视翘曲这样的东西;这可能比你现在应该考虑的更多:)
此外,正交投影矩阵不是3D或不比透视3D,只要它们通过将3D“图像”投影到2D观察平面上工作......它们与透视之间的唯一区别是平行线保持平行。换句话说,(x,y,near)和(x,y,far)理想地投影到正交投影中的屏幕上的相同位置。
在片段着色器中使用gl_FragCoord.z
是不常见的。由于这是写入深度缓冲区的值,因此您也可以在片段着色器中编写 NOTHING 并重新使用深度缓冲区。除非您的实现不支持浮点深度缓冲,否则您将深度写入两个位置会浪费内存带宽。在构建阴影贴图时,使用glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
的仅深度通道通常可以获得更高的吞吐量。
如果您实际使用了pass_Position
的值(这是剪辑空间中非透视校正的Z坐标),我可以看到使用单独的颜色附件来编写它,但是您正在编写透视图 - 当前正确的深度范围调整深度(gl_FragDepth
)。
在任何情况下,当太阳直接在头顶并且您正在使用正交投影时,可以预期不会投射阴影。这可以追溯到我之前解释的属性,其中平行线保持平行。由于物体距离太阳的距离不会影响物体投射的位置(正交),如果它直接在头顶上,您将看不到任何阴影。尝试沿着球体而不是圆圈跟踪太阳的位置,以最大限度地减少这种情况。