HLSL Shader 中正确计算漫反射贡献





5.00/5 (1投票)
如何在 HLSL Shader 中正确计算光的漫反射贡献
顶点和像素 Shader 已经发布很多年了,固定流水线也已弃用数年,但论坛中仍然有很多关于如何正确计算光照的漫反射贡献的问题。 这篇论文 提供了关于该问题的很好的教程,并包含一个模拟固定流水线行为的完整 Shader。 但是,在这里我们将看到如何执行基本的计算,以防您不需要模拟完整的流水线。
首先是编写一些 D3D9 代码,允许您使用相同的参数在旧的固定流水线和您自己的 Shader 之间切换。 这样,您可以轻松找到光照计算中的任何行为差异。 您可以阅读更多关于 D3D9 固定流水线如何计算光照的信息,请访问 此页面。
编写 Shader 时,人们倾向于像这样计算漫反射贡献:
Out.Color = (materialAmbient * lightAmbient) + (materialDiffuse * lightDiffuse * dot(Normal, L));
其中 L
是从顶点位置(在世界坐标系中)到光照的向量。
除了不进行镜面或发射计算(这在许多情况下可能不是必需的,具体取决于您的场景)之外,该方法还存在一些错误
- 您不希望点积返回负值,因为它会错误地使颜色变黑。 因此,您需要使用
saturate
运算符将其限制在 0..1 范围内:saturate(dot(Normal, L))
- 为了获得与固定流水线相同的结果,您应该包括衰减计算,因为它们会根据被照点和光源之间的距离来修改光照强度。 衰减(与它的名字相反),不仅会衰减光照,而且在某些情况下还可以增加强度。(请参阅下文如何正确计算衰减因子)
- 一旦您计算了衰减,您应该从先前的方程中删除
materialDiffuse
因子,因为您不希望它也被衰减。 您将在整个光照贡献被正确计算和衰减后,稍后应用它。
牢记这 3 点,顶点 Shader 中的最终计算将是:
float4 LightContrib = (0.f, 0.f, 0.f, 0.f);
float fAtten = 1.f;
// 1.- First, we store the total ambient light in the scene
// (multiplication of material_ambient, light_ambient, and any other global ambient component)
Out.Color = mMaterialAmbient * mLightAmbient;
// 2.- Calculate vector from point to Light (both normalized and not-normalized versions,
// as we might need to calculate its length later)
float pointToLightDif = mLightPos - P;
float3 pointToLightNormalized = normalize(pointToLightDif);
// 3.- Calculate dot product between world_normal and pointToLightNormalized
float NDotL = dot(Nw, pointToLightNormalized);
if(NDotL > 0)
{
LightContrib = mLightDiffuse * NDotL * mLightDivider;
float LD = length(pointToLightDif);
if(LD > mLightRange)
fAtten = 0.f;
else
fAtten = 1.f/(mLightAtt0 + mLightAtt1*LD + mLightAtt2*LD*LD);
LightContrib *= fAtten;
}
Out.Color += LightContrib * mMaterialColor;
Out.Color = saturate(Out.Color);
比较
第一张图片是可编程版本。 您可以通过窗户上的反射略微看出它。
第二张图片是固定流水线版本(窗户上没有实时反射)