[翻译]XNA 3.0 Game Programming Recipes之forty-three
PS:自己翻译的,转载请著明出处格
6-7 添加HLSL Per-Pixel 照明
问题
如6-3节所显示的,最好的光照结果被获得使用per-pixel光照,特别是曲线的表面制成的大型的三角形。你想添加per-pixel光照到你自己的效果。
解决方案
在前面的两节,你计算阴影的值在每个顶点中。一个三角形的三个顶点的阴影值是线形内插值去获得阴影的值为每个顶点。
在per-pixel 光照这种情况下,你想以内插值替换三个顶点的法线以获得每个顶点的法线,这样在每个顶点中你能计算光照因子在正确的法线的基础上。虽然,如同内插值阴影,你会得到错误结果,当添加这个法线从一个顶点到另一个顶点。如图6-9的左边所示。那个平直的线表示一个三角形,它的顶点包含法线。当你在这个三角形里把两个法线添加到象素中时,这个以内插值替换的法线有一个正确的方向,它比原样的法线要小在那个象素里,它在图象上显示出来。
解决的办法是在收到此插值法线在像素着色器里。因为它的方向是正确的,你可以规格化它,使结果法线的单位长度。由于每一个象素的比例因数不同,你需要这方面的在象素着色器中。
注意:为什么你想使你的法线长度等于1,阅读"规格化你的法线"在6-1节。较小的法线将导致较小的光照强度,同时你想要光照的强度只依靠于法线和入射光之间的角度。
光的方向有一个同样的问题,如图6-9右边部分所显示的那样。内插值光照方向的长度将沿着虚线曲线,这将导致向量,它比应该的短。再次,你可以解决这个通过规格化内插值光照方向在象素着色器中。
它是如何工作的
在本章所有的小节,你的XNA工程必须与你的图形卡相结合去绘制三角形从一些顶点,它至少包含3D位置和法线。
你想设计你的效果,所以你的XNA代码能设置世界,视景和投影矩阵,和光照点的3D位置,在你的场景中的环境光:
注意:在这简单的光照方向的情况下,光的方向将会是一个XNA-to-HLSL变量,它是由所有顶点和象素组成。因此,顶点着色器不需要计算这些。
顶点着色器
顶点着色器接收来自顶点的法线,用包含世界矩阵的旋转方法来旋转它,传递它到象素着色器中。它同样计算光的方向通过减去光照点的位置从顶点的位置。你考虑最终顶点3D位置依靠当前的世界矩阵。
法线和光照方向在三角形的三个顶点之间的所有象素都被内插值替换。在前面的叙述中,这能得到一个错误因为内插值向量的长度可以比它应该的小。通过规格化它们,确保它们的长度正好为1来解决这事。
一旦两个方向已经规格化,你接收它们的点乘积去找到光照因子:
这个技巧要求Shader2.0-编译图形卡:
6-7 添加HLSL Per-Pixel 照明
问题
如6-3节所显示的,最好的光照结果被获得使用per-pixel光照,特别是曲线的表面制成的大型的三角形。你想添加per-pixel光照到你自己的效果。
解决方案
在前面的两节,你计算阴影的值在每个顶点中。一个三角形的三个顶点的阴影值是线形内插值去获得阴影的值为每个顶点。
在per-pixel 光照这种情况下,你想以内插值替换三个顶点的法线以获得每个顶点的法线,这样在每个顶点中你能计算光照因子在正确的法线的基础上。虽然,如同内插值阴影,你会得到错误结果,当添加这个法线从一个顶点到另一个顶点。如图6-9的左边所示。那个平直的线表示一个三角形,它的顶点包含法线。当你在这个三角形里把两个法线添加到象素中时,这个以内插值替换的法线有一个正确的方向,它比原样的法线要小在那个象素里,它在图象上显示出来。
解决的办法是在收到此插值法线在像素着色器里。因为它的方向是正确的,你可以规格化它,使结果法线的单位长度。由于每一个象素的比例因数不同,你需要这方面的在象素着色器中。
注意:为什么你想使你的法线长度等于1,阅读"规格化你的法线"在6-1节。较小的法线将导致较小的光照强度,同时你想要光照的强度只依靠于法线和入射光之间的角度。
光的方向有一个同样的问题,如图6-9右边部分所显示的那样。内插值光照方向的长度将沿着虚线曲线,这将导致向量,它比应该的短。再次,你可以解决这个通过规格化内插值光照方向在象素着色器中。
它是如何工作的
在本章所有的小节,你的XNA工程必须与你的图形卡相结合去绘制三角形从一些顶点,它至少包含3D位置和法线。
你想设计你的效果,所以你的XNA代码能设置世界,视景和投影矩阵,和光照点的3D位置,在你的场景中的环境光:
1 float4*4 xWorld;
2 float4*4 xView;
3 float4*4 xProjection;
4 float3 xLightPosition;
5 float xAmbient;
6 struct PPSVertexToPixel
7 {
8 float4 Position:POSITION;
9 float3 Normal:TEXCOORD0;
10 float3 LightDirection:TEXCOORD1;
11 };
12 struct PPSPixelToFrame
13 {
14 float4 Color :COLOR0;
15 };
正如前面介绍的,你的顶点着色器输出这法线,它将内插值三角形的所有的象素,和光从光照点到顶点的方向。象素着色器必须计算每个象素的唯一最终颜色。2 float4*4 xView;
3 float4*4 xProjection;
4 float3 xLightPosition;
5 float xAmbient;
6 struct PPSVertexToPixel
7 {
8 float4 Position:POSITION;
9 float3 Normal:TEXCOORD0;
10 float3 LightDirection:TEXCOORD1;
11 };
12 struct PPSPixelToFrame
13 {
14 float4 Color :COLOR0;
15 };
注意:在这简单的光照方向的情况下,光的方向将会是一个XNA-to-HLSL变量,它是由所有顶点和象素组成。因此,顶点着色器不需要计算这些。
顶点着色器
顶点着色器接收来自顶点的法线,用包含世界矩阵的旋转方法来旋转它,传递它到象素着色器中。它同样计算光的方向通过减去光照点的位置从顶点的位置。你考虑最终顶点3D位置依靠当前的世界矩阵。
1 PPSVertexToPixel PPSVertexShader(float4 inPos:POSITION0,float3 inNormal:NORMAL0)
2 {
3 PPSVertexToPixel Output=(PPSVertexToPixel)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 float3*3 rotMatrix=(float3*3)xWorld;
10 float3 rotNormal=mul(inNormal,rotMatrix);
11 Output.Normal=rotNormal;
12 return Output;
13 }
象素着色器2 {
3 PPSVertexToPixel Output=(PPSVertexToPixel)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 float3*3 rotMatrix=(float3*3)xWorld;
10 float3 rotNormal=mul(inNormal,rotMatrix);
11 Output.Normal=rotNormal;
12 return Output;
13 }
法线和光照方向在三角形的三个顶点之间的所有象素都被内插值替换。在前面的叙述中,这能得到一个错误因为内插值向量的长度可以比它应该的小。通过规格化它们,确保它们的长度正好为1来解决这事。
一旦两个方向已经规格化,你接收它们的点乘积去找到光照因子:
1 PPSPixelToFrame PPSixelShader(PPSVertexToPixel PSIn):COLOR0
2 {
3 PPSPixelToFrame Output=(PPSPixelToFrame)0;
4 float4 baseColor=float4(0,0,1,1)
5 float3 normal=normalize(PSIn.Normal);
6 float3 lightDirection=normalize(PSIn.LightDirection);
7 float lightFactor=dot(normal,-lightDirection);
8 Output.Color=baseColor*(lightFactor+xAmbient);
9 return Output;
10 }
技巧解说2 {
3 PPSPixelToFrame Output=(PPSPixelToFrame)0;
4 float4 baseColor=float4(0,0,1,1)
5 float3 normal=normalize(PSIn.Normal);
6 float3 lightDirection=normalize(PSIn.LightDirection);
7 float lightFactor=dot(normal,-lightDirection);
8 Output.Color=baseColor*(lightFactor+xAmbient);
9 return Output;
10 }
这个技巧要求Shader2.0-编译图形卡:
1 technique PerPixelShading
2 {
3 pass Pass0
4 {
5 VertexShader=compile vs_2_0 PPSVertexShader();
6 PixelShader=compile ps_2_0 PPSPixelShader();
7 }
8 }
2 {
3 pass Pass0
4 {
5 VertexShader=compile vs_2_0 PPSVertexShader();
6 PixelShader=compile ps_2_0 PPSPixelShader();
7 }
8 }