6-9 添加HLSL镜子的高光(Hightlights)
问题
你要添加镜子的高光到你的3D场景绘制你的自定义HLSL效果。镜子的高光是高的照明区域围绕一个光源的反射,如图6-11所示。
解决方案
接下来的讨论将帮助你决定哪个象素应该有一个镜子光部分。
图6-11的左边所示一个线偏震光束,L,来自一个光源撞击一个三角形象素。同样在左边的图象,人眼向量被显示,它是摄象机朝着这象素去的方向。如果L的反射几乎与E相同,这象素应该有一个镜面光部分。
你可以找到L的反射通过镜象L在象素的法线之上。这反映的方向几乎和人眼的方向相同,如果在它们之间的角度很小。你可以检查在它们两个向量之间的角度,通过接收它们的点乘积。
如果角度是0,意思是两个方向都是一样的,你应该添加一个镜子的部分到你的光照,这点乘积将会是1。如果两个方向互相不同,这个点乘积将会变小。
注意:一个点乘积在两个向量A和B之间,只不过是(A的长度)*(B的长度)*(它们之间角度的余玄)。如果两个A和B被规格化,甚至减少到(它们之间角的余玄)。如果在A和B之间的角度是0,余玄将会是1。如果两个向量互相垂直,这个角度将会是90度,这个余玄将会是0。如图6-11图的右上所示的那样。如果两个向量互相相对,180的角度余玄将会是-1。
这个点乘积对与它的反射的所有光向量是正的,小于从人眼向量的90度,正如你看见的图6-11右上。你不能立即使用这值到你的镜子高光的基础上,因为这可能添加一个镜子部分到所有反射向量在人眼向量的90度。你想缩小这个向量在10度或者更低。
你可以获得这个通过接收点乘积的结果到一个高的强度。取得这个点乘积到第12的强度,例如,将产生的一个值比0大,向量只是10以内,它同样显示在图6-11右下方。
这将导致一个单一结果为每一个象素,显示镜子部分的强度在这个象素里。
它是如何工作的
一如往常,你可能想设置世界,视景和投影矩阵去转换你的3D位置到2D屏幕坐标。因为这节为一个光照点所写。你可能需要指定它的位置。计算人眼的向量,你需要知道相机的位置。你可能应该设置镜子的强度去控制镜子高光的大小。因此总共的光照可能变的比1大,你可能应该缩放光的强度去避免oversaturation.
注意:在多数情况,你想去缩放光源的强度。有一个以上光可能另外导致更多的象素是饱和的在光中,浪费的照明效果。
1 float4*4 xWorld;
2 float4*4 xView;
3 float4*4 xProjection;
4 float3 xLightPosition;
5 float3 xCameraPos;
6 float xAmbient;
7 float xSpecularPoswer;
8 float xLightStrength;
9 struct SLVertexToPixel
10 {
11 float4 Position:POSITION;
12 float3 Normal:TEXCOORD0;
13 float3 LightDirection:TEXCOORD1;
14 float3 EyeDirection:TEXCOORD2;
15 };
16 struct SLPixelToFrame
17 {
18 float4 Color:COLOR0;
19 };
顶点着色器同样计算EyeDrection和使它内插值到所有象素。象素着色器仍然只输出每个象素的颜色。2 float4*4 xView;
3 float4*4 xProjection;
4 float3 xLightPosition;
5 float3 xCameraPos;
6 float xAmbient;
7 float xSpecularPoswer;
8 float xLightStrength;
9 struct SLVertexToPixel
10 {
11 float4 Position:POSITION;
12 float3 Normal:TEXCOORD0;
13 float3 LightDirection:TEXCOORD1;
14 float3 EyeDirection:TEXCOORD2;
15 };
16 struct SLPixelToFrame
17 {
18 float4 Color:COLOR0;
19 };
顶点着色器
顶点着色器不是不同于前面的节。唯一的新的有趣的点是人眼向量被计算在顶点着色器中。一个向量从一个点到另外一个点,通过从目标减去光源被找到。
1 SLVertexToPixel SLVertexShader(float4 inPos:POSITION0,float3 inNormal:NORMAL0)
2 {
3 SLVertexToPixel Output=(SLVertexToPixel)0;
4 float4*4 preViewProjection=mul(xView,xProjection);
5 float4*4 preWorldViewProjection=mul(xWorld,preViewProjection);
6 Output.Position=mul(inPos,preWorldViewProjection);
7 float3 final3DPos=mul(inPos,xWorld);
8 Output.LightDirection=final3DPos-xLightPosition;
9 Output.EyeDirection=final2DPos-xCameraPos;
10 float3*3 rotMatrix=(float3*3)xWorld;
11 float3 rotNormal=mul(inNormal,rotMatrix);
12 Output.Normal=rotNormal;
13 return Output;
14 }
象素着色器2 {
3 SLVertexToPixel Output=(SLVertexToPixel)0;
4 float4*4 preViewProjection=mul(xView,xProjection);
5 float4*4 preWorldViewProjection=mul(xWorld,preViewProjection);
6 Output.Position=mul(inPos,preWorldViewProjection);
7 float3 final3DPos=mul(inPos,xWorld);
8 Output.LightDirection=final3DPos-xLightPosition;
9 Output.EyeDirection=final2DPos-xCameraPos;
10 float3*3 rotMatrix=(float3*3)xWorld;
11 float3 rotNormal=mul(inNormal,rotMatrix);
12 Output.Normal=rotNormal;
13 return Output;
14 }
象素着色器非常有趣。再一次,这基础颜色是固定的蓝色,所以你不用去浪费你的注意力。作为一个很好的练习,你规格化每个方向你接收的在你的象素着色器中,由于它的长度不会正好是1。
以以往一样,你计算普通光照,你用xLightStrength乘以它去缩放一点它(假定xLightStrength小于1)。
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 flaot3 lightDirection=normalize(PSIn.LightDirection);
7 float shading=dot(normal,-lightDirection);
8 shading*=xLightStrength;
9 float3 reflection=-reflect(lightDirection,normal);
10 float3 eyeDirection=normalize(PSIn.EyeDirection);
11 float specular=dot(reflection,eyeDirection);
12 specular=pow(specular,xSpecularPower);
13 specular*-xLightStrength;
14 Output.Color=baseColor*(shading+xAmbient)+specular;
15 return Output;
16 }
接下来,你反映这个光的方向通过法线使用reflect值。因为这光的方向是照向象素的,它的反射将朝着人眼方向前进。这是你人眼向量的反方向,所以你要使它为负。2 {
3 SLPixelToFrame Output=(SLPixelToFrame)0;
4 float4 baseColor=float4(0,0,1,1);
5 float3 normal=normalize(PSIn.Normal);
6 flaot3 lightDirection=normalize(PSIn.LightDirection);
7 float shading=dot(normal,-lightDirection);
8 shading*=xLightStrength;
9 float3 reflection=-reflect(lightDirection,normal);
10 float3 eyeDirection=normalize(PSIn.EyeDirection);
11 float specular=dot(reflection,eyeDirection);
12 specular=pow(specular,xSpecularPower);
13 specular*-xLightStrength;
14 Output.Color=baseColor*(shading+xAmbient)+specular;
15 return Output;
16 }
这镜子的值通过接收点乘积在人眼向量和负的反射光方向之间被找到。接收这个值到一个高的强度,确保这个值显著大于0,只为这些象素,两个向量不同小于10左右。再次,这个值通过用xLightStrength值乘以它来削弱。
最后,周围环境,阴影和镜子光照部分是由获得象素最终颜色所组成。
注意:镜子的部分添加白色到最终的颜色。如果你的光有一个不同的颜色,你应该乘以镜子的值用你的光的颜色。
技巧说明
这里是技巧说明
1 technique SpecularLighting
2 {
3 pass Pass0
4 {
5 VertexShader=compile vs_2_0 SLVertexShader();
6 PixelShader=compile ps_2_0 SLPixelShader();
7 }
8 }
代码2 {
3 pass Pass0
4 {
5 VertexShader=compile vs_2_0 SLVertexShader();
6 PixelShader=compile ps_2_0 SLPixelShader();
7 }
8 }
由于所有的HLSL已经显示完全有序,我列出只有XNA代码需要去绘制一些三角形用这个效果:
1 effect.CurrentTechnique=effect.Technique["SpecularLighting"];
2 effect.Parameters["xWorld"].SetValue(Matrix.Identity);
3 effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix);
4 effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix);
5 effect.Parameters["xAmbient"].SetValue(0.0f);
6 effect.Parameters["xLightStrength"].SetValue(new Vector3(5.0f,2.0f,-15.0f));
7 effect.Parameters["xCameraPos"].SetValue(fpsCam.Position);
8 effect.Parameters["xSpecularPower"].SetValue(128.0f);
9 effect.Parameters["xLightStrength"].SetValue(0.5f);
10 effect.Begin();
11 foreach(EffectPass pass in effect.CurrentTechnique.Passes)
12 {
13 pass.Begin();
14 device.VertexDeclaration=myVertexDeclaration;
15 device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList,vertices,0,6);
16 pass.End();
17 }
18 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["xAmbient"].SetValue(0.0f);
6 effect.Parameters["xLightStrength"].SetValue(new Vector3(5.0f,2.0f,-15.0f));
7 effect.Parameters["xCameraPos"].SetValue(fpsCam.Position);
8 effect.Parameters["xSpecularPower"].SetValue(128.0f);
9 effect.Parameters["xLightStrength"].SetValue(0.5f);
10 effect.Begin();
11 foreach(EffectPass pass in effect.CurrentTechnique.Passes)
12 {
13 pass.Begin();
14 device.VertexDeclaration=myVertexDeclaration;
15 device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList,vertices,0,6);
16 pass.End();
17 }
18 effect.End();