65.9K
CodeProject 正在变化。 阅读更多。
Home

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

2012年10月26日

CPOL

2分钟阅读

viewsIcon

7949

如何在 HLSL Shader 中正确计算光的漫反射贡献

顶点和像素 Shader 已经发布很多年了,固定流水线也已弃用数年,但论坛中仍然有很多关于如何正确计算光照的漫反射贡献的问题。 这篇论文 提供了关于该问题的很好的教程,并包含一个模拟固定流水线行为的完整 Shader。 但是,在这里我们将看到如何执行基本的计算,以防您不需要模拟完整的流水线。

首先是编写一些 D3D9 代码,允许您使用相同的参数在旧的固定流水线和您自己的 Shader 之间切换。 这样,您可以轻松找到光照计算中的任何行为差异。 您可以阅读更多关于 D3D9 固定流水线如何计算光照的信息,请访问 此页面

编写 Shader 时,人们倾向于像这样计算漫反射贡献:

Out.Color = (materialAmbient * lightAmbient) + (materialDiffuse * lightDiffuse * dot(Normal, L));

其中 L 是从顶点位置(在世界坐标系中)到光照的向量。

除了不进行镜面或发射计算(这在许多情况下可能不是必需的,具体取决于您的场景)之外,该方法还存在一些错误

  1. 您不希望点积返回负值,因为它会错误地使颜色变黑。 因此,您需要使用 saturate 运算符将其限制在 0..1 范围内:saturate(dot(Normal, L))
  2. 为了获得与固定流水线相同的结果,您应该包括衰减计算,因为它们会根据被照点和光源之间的距离来修改光照强度。 衰减(与它的名字相反),不仅会衰减光照,而且在某些情况下还可以增加强度。(请参阅下文如何正确计算衰减因子)
  3. 一旦您计算了衰减,您应该从先前的方程中删除 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);

比较

第一张图片是可编程版本。 您可以通过窗户上的反射略微看出它。

image

第二张图片是固定流水线版本(窗户上没有实时反射)

image

© . All rights reserved.