延迟渲染中Point light双抛物面VSM实现问题求助
我帮你梳理几个最可能导致阴影效果异常的问题点,你可以逐一排查:
1. lit_factor的逻辑完全错误
原FX代码里的float lit_factor = (mydepth <= moments[0]);是将布尔判断结果转为float——当当前像素在阴影外(自身深度小于等于阴影贴图存储的深度)时,lit_factor为1.0(全亮),否则为0.0(全暗),这是阴影判断的基础逻辑。
但你现在的代码把深度值直接赋值给lit_factor,完全违背了原逻辑:
// 错误的写法 float lit_factor = 0; if (mydepth <= moments.x) lit_factor = mydepth; else lit_factor = moments.x;
正确的转换应该是:
// 简洁的布尔转float写法 float lit_factor = float(mydepth <= moments.x); // 或者更清晰的分支写法 float lit_factor = (mydepth <= moments.x) ? 1.0 : 0.0;
这个错误会直接导致阴影亮度计算完全偏离预期,是最可能的元凶。
2. 混淆了「光源最大范围」和「衰减系数」
原FX代码里的LightAttenuation是点光源的最大影响距离(半径),但你把它替换成了自己计算的衰减系数attenuation = 1.0 / (denom * denom)——这两个是完全不同的概念,会引发两个致命问题:
- 半球判断失效:原代码用
pos.z / LightAttenuation归一化z坐标来区分前/后半球,你现在用vert.z / attenuation(attenuation≤1)会放大z值,导致alpha的范围完全混乱,错误选择前/后阴影贴图。 - 深度比较完全错位:原代码
P0.z = L / LightAttenuation是将顶点到光源的距离归一化到0-1范围(和阴影贴图存储的深度匹配),你现在用L / attenuation会得到极大的数值,和阴影贴图里的深度值完全不匹配。
修正方法:把所有参与阴影计算的attenuation替换为你的lightRad(光源最大半径):
// 修正半球判断的alpha计算 float alpha = 0.5 + vert.z / lightRad; // 修正mydepth的归一化计算 P0.z = L / lightRad; P1.z = L / lightRad;
你自己计算的衰减系数attenuation应该用来最终控制光照强度,而非参与阴影的半球判断和深度计算。
3. 纹理坐标系的y轴翻转问题
原FX代码针对DirectX的纹理坐标系(左上为原点),所以用P0.y = -.5f * P0.y + .5f;翻转y轴。但GLSL默认纹理坐标系是左下为原点,如果渲染双抛物面贴图时没有做对应翻转,现在的y轴翻转会导致采样坐标完全颠倒,阴影错位。
你可以尝试去掉y轴的负号,改成适配GL的坐标转换:
P0.y = 0.5 * P0.y + 0.5; P1.y = 0.5 * P1.y + 0.5;
或者检查渲染双抛物面贴图时的顶点输出,确认是否已经处理过y轴翻转,再决定是否保留负号。
4. 阴影贴图的深度存储一致性
必须保证你在渲染双抛物面贴图时,存储的深度值和mydepth的计算方式完全一致:
- 如果阴影贴图存储的是归一化到0-1的深度(距离光源的距离÷lightRad),那
mydepth必须也是L / lightRad。 - 如果存储的是原始距离L,那
mydepth就直接用L,同时阴影贴图要选择足够精度的格式(比如GL_RG32F)。
深度存储不一致会导致深度比较完全错误,阴影要么全黑要么全亮。
5. 左手坐标系下的View矩阵方向
确认你的lightViewMat是否正确将世界空间顶点转换到点光源局部空间,且z轴方向符合教程预期:原教程用z坐标判断前/后半球,需要保证点光源空间下,前半球z值为正、后半球为负(如果方向相反,要调整alpha的判断条件)。如果View矩阵z轴方向颠倒,会导致前/后贴图采样完全错误。
内容的提问来源于stack exchange,提问作者Manh Nguyen Tien

