XNA Shader 程序设计
教程2 - 漫反射
大家好,今天我们将在教程一的基础上继续学习,在光照算式中加上漫反射光。
漫反射光
环境光计算等式为:
I = Aintensity * Acolor
漫反射基于这个等式,添加了一道有方向的光线:
I = Aintensity x Acolor + Dintensity x Dcolor x N.L (2.1)
通过计算式,你可以看到我们仍然使用了环境光,但添加了两个变量,分别表示漫反射光的光强和颜色,还添加了两个向量N和L,L表示光照方向,N表示表面法线。
我们可以认为漫反射表现了一个表面如何反射光线。法线方向N和光线方向L的夹角越小,表面反射的光就越强。
当L和N平行,反射光最强,当L与物体表面平行,反射光最弱。
我们可以通过计算向量的点积(或叫数量积)来得到L与N的夹角。这条计算向量夹角的法则定义如下:N.L = |N| x |L| x cos(a)。|N|是向量N的长度,|L|是向量L的长度,cos(a)表示两向量的夹角。
实现shader
我们需要三个全局变量
float4x4 matWorldViewProj;
float4x4 matInverseWorld;
float4 vLightDirection;
我们仍然需要教程一中介绍的worldviewprojection矩阵,同时需要添加InverseWorld矩阵来变换法线,添加vLightDirection表示光线方向。
我们仍需为顶点着色器定义OUT结构体,从而把数据传给像素着色器:
struct OUT
{
float4 Pos: POSITION;
float3 L: TEXCOORD0;
float3 N: TEXCOORD1;
};
我们把位置Pos,光线方向L,法线N存在不同的寄存器中。TEXCOORDn可以用来表示任意值,在我们没有使用贴图的情况下,我们用它来存储向量。
OK,下面是顶点着色器:
OUT VertexShader( float4 Pos: POSITION, float3 N: NORMAL )
{
OUT Out = (OUT) 0;
Out.Pos = mul(Pos, matWorldViewProj);
Out.L = normalize(vLightDirection);
Out.N = normalize(mul(matInverseWorld, N));
return Out;
}
我们从模型文件中获得顶点和法线的数据然后传递给shader,然后变换位置坐标,归一化光线方向,变换并归一化物体表面的法线。
然后,在像素着色器中,把TEXCOORD0的值给L,TEXCOORD1的值给N,寄存器的值来自顶点着色器。下面在像素着色器中实现算式2.1:
float4 PixelShader(float3 L: TEXCOORD0, float3 N: TEXCOORD1) : COLOR
{
float Ai = 0.8f;
float4 Ac = float4(0.075, 0.075, 0.2, 1.0);
float Di = 1.0f;
float4 Dc = float4(1.0, 1.0, 1.0, 1.0);
return Ai * Ac + Di * Dc * saturate(dot(L, N));
}
下面是这个shader的technique
technique DiffuseLight
{
pass P0
{
VertexShader = compile vs_1_1 VertexShader();
PixelShader = compile ps_1_1 PixelShader();
}
}
OK,这就是漫反射!下载代码调试一下吧。
Download: Executable + Source