6-8.用HLSL定义聚光灯
问题
你想定义一个聚光灯,它的行为很像点光源,但它照亮一个光锥,如6-10.
方案
在像素着色器,确定当前像素是否在照明锥中。可以求光的方向和锥方向间的点积。
运作
从上一节的代码开始。因为聚光灯比点光灯更专用,你将添加这些额外的XNA-HLSL变量到你的.fx文件:
float xLightStrength; // 光强度
float3 xConeDirection; // 光锥方向
float3 xConeAngle; // 光锥角
float3 xConeDecay; // 光锥衰减
第一个变量允许你增加/减少光的强度。这对其他类型的光也是有用的,并且当你的场景中有多个光源时也是必要的。接着,你可以调整聚光灯的光锥的中心方向,以及光锥的宽度。最后,你可以指定朝着光锥的边缘要减少多少光强度。
除此之外,您需要稍微扩展您的像素着色器。通常,你将做的和每像素点光源类似,但你将添加一个检查证实像素是否在光的光锥中:
SLPixelToFrame SLPixelShader(SLVertexToPixel PSIn) : COLOR0
{
SLPixelToFrame Output = (SLPixelToFrame)0;
float4 baseColor = float4(0,0,1,1);
float3 normal = normalize(PSIn.Normal);
float3 lightDirection = normalize(PSIn.LightDirection);
float coneDot = dot(lightDirection, normalize(xConeDirection));
float shading = 0;
if (coneDot > xConeAngle)
{
float coneAttenuation = pow(coneDot, xConeDecay);
shading = dot(normal, -lightDirection);
shading *= xLightStrength;
shading *= coneAttenuation;
}
Output.Color = baseColor*(shading+xAmbient);
return Output;
}
一旦你单位化法线和光方向,你应该发现当前像素是否在光锥中。要做到这一点,你要检查两个方向间的夹角:
当前像素和光源间的方向
光锥中心方向
第一个方向是lightDirection,而第二个在xConeDirection变量中规定。只有如果这个角度低于某一阈值,像素才被照亮。
快速的检查方法是求两个方向的点积。如果值接近1表示两个方向的夹角很小,值越小,角越大。
要决定角是否不会太大,你检查点积是否不小于存在xConeAngle中的某一阈值。如果像素在锥中,你计算光照因子,叫做shading。要削弱接近锥的边界的照明效果,你求coneDot变量指定xConeDecay次方的结果。结果,离光锥中心方向很远的像素的coneDot值等于或小于1。
光锥外边的像素的着色值为0,所以光不影响这些像素。