6-8 用HLSL定义一个聚光灯
问题
一个光照点作为在前面定义的一样,闪耀的光从一个点向所有方向。你想定义一个聚光灯,它它的行为完全像一个点光源,除了它照耀的象一个光核一样,如图6-10所显示。
解决方案
在你的象素着色器中,判断当前的象素是否在光照核内。你可以通过使用点乘积在光的方向和核的方向之间来实现这个。
它是如何工作的
开始前面小节的代码。由于一个聚光灯比一个光照点更个性化,你添加这些额外的XNA-to-HLSL变量到你的.fx文件中:
1 float xLightStrength;
2 float3 xConeDiection;
3 float xConeAngle;
4 float xConeDecay;
第一个变量将允许你增加/减少你的光照的强度。这同样很有用为任何光照的类型也是非常有必要的,当你有多个光照在你的屏幕中。接下来,你可以指定聚光灯核心的中心方向,和核心的宽度。最后,你可以指定多大的光照强度应该减少朝着核心的边缘。2 float3 xConeDiection;
3 float xConeAngle;
4 float xConeDecay;
除了这个,你需要扩展一下你的象素着色器。一般来说,你将会做相同的per-pixel光照点,但是你要添加一个检查去核实象素在光的核心中:
1 SLPixelToFrame SLPixelShader(SLVertexToPixel PSIn):COLOR0
2 {
3 SLPixelToFrame Output=(SLPixelToFrame)0;
4 float4 baseColor=float4(0,0,1,1);
5 float3 normal=normalize(PSIn.Normal);
6 float3 lightDirection=normalize(PSIn.LightDirection);
7 float coneDot=dot(lightDirection,normalize(xConeDiection));
8 float shading=0;
9 if(coneDot>xConeAngle)
10 {
11 float coneAttenuation=pow(coneDot,xConeDecay);
12 shading=dot(normal,-lightDirection);
13 shading*=xLightStrength;
14 shading*=coneAttenuation;
15 }
16 Output.Color=baseColor*(shading+xAmbient);
17 return Output;
18 }
一旦你已经规格化法线和光照方向,你应该检测当前的象素是否在光的核心内。要做到这一点,你要检查两个方向之间的角度:2 {
3 SLPixelToFrame Output=(SLPixelToFrame)0;
4 float4 baseColor=float4(0,0,1,1);
5 float3 normal=normalize(PSIn.Normal);
6 float3 lightDirection=normalize(PSIn.LightDirection);
7 float coneDot=dot(lightDirection,normalize(xConeDiection));
8 float shading=0;
9 if(coneDot>xConeAngle)
10 {
11 float coneAttenuation=pow(coneDot,xConeDecay);
12 shading=dot(normal,-lightDirection);
13 shading*=xLightStrength;
14 shading*=coneAttenuation;
15 }
16 Output.Color=baseColor*(shading+xAmbient);
17 return Output;
18 }
1。当前象素和光源之间的方向。
2。核心的中心方向。
第一个方向是lightDirection,同时第二个被指定在xConeDirection变量。只有在这个角度是低于某一临界值,像素应点亮。
一种为这个的快速检查是通过接收点乘积在两个方向之间 。一个值接近到1,意思是一个非常小的角度在两个方向之间,同时值越小表明角度越大。
为了测定角度是否太大,你检查这点乘积是否比临界值小,保存在xConeAngle内,如果象素是在核心内,你计算光照因子,称为shading.为了削弱光照效果接近于核心的边,你使用coneDot值到指定的强度在xConeDecay里,作为一个结果,coneDot值等于或者比1小,象素将变的更小,它离核心中心方向就越远。
核心以外的象素会得到一个shading值为0,所以光将有没有影响这些象素。
代码
你的完整的象素着色器已经列出来了。
在你的XNA代码的Draw方法中,一个有活力的效果,设置它的参数,绘制到你的屏幕中:
1 effect.CurrentTechnique=effect.Technique["SpotLight"];
2 effect.Parameters["xWorld"].SetValue(Matrix.Identity);
3 effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix);
4 effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix);
5 effect.Parameters["xLightDirection"].SetValue(new Vector3(5.0f,2.0f,-15.0f+variation));
6 effect.Parameters["xAmbient"].SetValue(2.0f);
7 effect.Parameters["xConeDirection"].SetValue(new Vector3(0,-1,0));
8 effect.Parameters["xConeAngle"].SetValue(0.5f);
9 effect.Parameters["xConeDecay"].SetValue(2.0f);
10 effect.Parameters["xLightStrength"].SetValue(0.7f);
11 effect.Begin();
12 foreach(EffectPass pass in effect.CurrentTechnique.Passes)
13 {
14 pass.Begin();
15 device.VertexDeclaration=myVertexDeclaration;
16 device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList,vertices,0,6);
17 pass.End();
18 }
19 effect.End();
2 effect.Parameters["xWorld"].SetValue(Matrix.Identity);
3 effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix);
4 effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix);
5 effect.Parameters["xLightDirection"].SetValue(new Vector3(5.0f,2.0f,-15.0f+variation));
6 effect.Parameters["xAmbient"].SetValue(2.0f);
7 effect.Parameters["xConeDirection"].SetValue(new Vector3(0,-1,0));
8 effect.Parameters["xConeAngle"].SetValue(0.5f);
9 effect.Parameters["xConeDecay"].SetValue(2.0f);
10 effect.Parameters["xLightStrength"].SetValue(0.7f);
11 effect.Begin();
12 foreach(EffectPass pass in effect.CurrentTechnique.Passes)
13 {
14 pass.Begin();
15 device.VertexDeclaration=myVertexDeclaration;
16 device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList,vertices,0,6);
17 pass.End();
18 }
19 effect.End();