问题
如6-3所见,最佳光照由每像素光照得出,尤其是大三角形制成的曲面。你想要添加每像素光照到自己的effects。
方案
在前两节,你在每个顶点计算着色值。一个三角形的每个像素得到的着色值是三个顶点的着色值的线性插值。
在每像素光照例子中,你想要插入三个顶点的法线来得出每个像素的法线,在每个像素可以基于正确法线计算出光照因子。但是对插值着色来说,当从顶点到顶点插值的法线你将得到不完善的结果。如6-9所示。平线表示一个三角形,它的顶点包含法线。当你给三角形的所有像素用插值法线,插值法线将一直跟随虚线。在三角形中心,虽然插值法线将有正确方向,但它将比该像素的精确法线小。
解决方案是在像素着色器中接收插值法线。由于它的方向是正确的,你可以单位化它。因为每个像素缩放率不同,你需要在像素着色器中处理。
光的方向有一个同一的问题,如6-9右部.插值光方向的长度将跟随虚线,这使向量少于应有值。再次,您可以在像素着色器里单位化插值的光方向解决这一问题。
运作
你的XNA项目必须和显卡协作从一些包含3D位置和法线的顶点渲染三角形。你想要设计你的效果使你的XNA代码可以设置世界,视图和投影矩阵,以及点光源的3D位置和环境光:
float4x4 xWorld;
float4x4 xView;
float4x4 xProjection;
float3 xLightPosition;
float xAmbient;
struct PPSVertexToPixel
{
float4 Position : POSITION;
float3 Normal : TEXCOORD0;
float3 LightDirection : TEXCOORD1;
};
struct PPSPixelToFrame
{
float4 Color : COLOR0;
};
你的顶点着色器将输出法线,法线被插值遍及三角形所有像素以及从点光源到顶点的光方向。像素着色器只要计算每个像素的最终颜色。
注意 在一个定向光的简单情况,光的方向将是XNA-HLSL的变量,对所有顶点和像素都是不变的。因此,顶点着色器不用计算它。
顶点着色器
顶点着色器从顶点接收法线,用世界矩阵中包含的旋转部分旋转它,并传给像素着色器。它也通过从顶点位置减去光的位置计算光方向。你依据当前世界矩阵求出顶点的最终3D位置。
PPSVertexToPixel PPSVertexShader(float4 inPos: POSITION0, float3 inNormal: NORMAL0)
{
PPSVertexToPixel Output = (PPSVertexToPixel)0;
float4x4 preViewProjection = mul(xView, xProjection);
float4x4 preWorldViewProjection = mul(xWorld, preViewProjection);
Output.Position = mul(inPos, preWorldViewProjection);
float3 final3DPos = mul(inPos, xWorld);
Output.LightDirection = final3DPos - xLightPosition;
float3x3 rotMatrix = (float3x3)xWorld;
float3 rotNormal = mul(inNormal, rotMatrix);
Output.Normal = rotNormal;
return Output;
}
像素着色器
法线和光方向是在三角形的所有像素之间插值的。这可能有错因为插值向量的长度可能比应有值小。可以单位化它们为1来解决。
一旦每个方向被单位化,你用它们的点积找出光照因子:
PPSPixelToFrame PPSPixelShader(PPSVertexToPixel PSIn) : COLOR0
{
PPSPixelToFrame Output = (PPSPixelToFrame)0;
float4 baseColor = float4(0,0,1,1);
float3 normal = normalize(PSIn.Normal);
float3 lightDirection = normalize(PSIn.LightDirection);
float lightFactor = dot(normal, -lightDirection);
Output.Color = baseColor*(lightFactor+xAmbient);
return Output;
}
定义技术
这个技术要求显卡支持Shader 2.0
technique PerPixelShading
{
pass Pass0
{
VertexShader = compile vs_2_0 PPSVertexShader();
PixelShader = compile ps_2_0 PPSPixelShader();
}
}
确保为你的光源尝试不同位置来查看效果。