一步步学shader系列(3):高光(specular light)

这次我们将实现一个叫做镜面反射的光线算法。该算法是建立在前面环境光照和漫反射光照的基础上的,所以,如果你前面没有弄懂,现在是时候了。:)

 

在这个教程中,您需要一些shader编程的基本知识,矢量和矩阵的数学知识。

镜面反射光照

迄今为止,我们已经实现了一个很好的照明模式。但是,如果我们想绘制一个抛光或闪耀的物体该怎么办?比如说金属表面,塑料,玻璃,瓶子等。为了模拟这种情况,我们需要在照明算法中加入一个新的向量:eye 向量。您可能会想什么是“eye”向量?这很容易回答:“eye”向量是从相机位置指向观察目标的向量。

其实就是 vector3 eyePos = objectPos - CamraPos

 

"The eye" 的位置在这:

 

Vector3(x, y, zHeight)

让我们把这个向量存储在一个变量中: Vector4 vecEye = new Vector4(x, y, zHeight,0);

让我们深入讨论如何在创建这个向量后使用shader。镜面高光的公式是

 

I=AiAc+Di*Dc*N.L+Si*Sc*(R.V)^n

其中R=2*(N.L)*N-L

 

我的另外一篇博客文章有详细介绍,大家可以翻阅。

 

如我们所见,我们需要新的Eye向量和一个反射向量R。为了计算镜面光,我们需要将R点乘V并进行n次幂的运算,而指数n表示光泽属性,n越大说明物体表面越光滑,反光越强。

 

实现Shader

如前面的截图可见,现在这个对象看起来很有光泽,只需通过shader就能实现!很酷吧!

首先声明一些变量:

float4x4 matViewProjection;
float4x4 matWorld;
float4   ambientMtl;
float    Aintensity;
float4   diffuseMtl;
float4   directionLight;
float    Diffusesity;
float    specularsity; //高光系数
int      Ispecular; //光泽属性
float4   ViewDirection; //EYE
float4   specularMtl; //高光光泽颜色,对应游戏里面就是材质

然后是INT 和 OUT结构。仍然和前面一样是:

 

struct VS_INPUT
{
   float4 Position : POSITION0;
   float4 Normal   : NORMAL0;
};

struct VS_OUTPUT
{
   float4 Position : POSITION0;
   float4 Color    : COLOR0;
};

 

除了V向量,在vertex shader没有新的东西。

 

这里的vecEye向量是通过参数(相机位置)传递到shader中的。

VS_OUTPUT vs_main( VS_INPUT Input )
{
   VS_OUTPUT Output;
  
   Output.Position = mul( Input.Position, matViewProjection );
   float4 directionLightTemp = directionLight;
   directionLightTemp.w = 0;
  
   float4 dir = mul(directionLightTemp, matViewProjection);
   float4 Normaltemp = mul(Input.Normal, matViewProjection);
   float s = dir * Normaltemp;
   if (s < 0.0f)
      s = 0;
     
   float4 posWorld = mul(Input.Position, matWorld);
   float4 eyeDir   = normalize(ViewDirection- posWorld);
   float4 R = 2 * s * Normaltemp - dir;
   Output.Color = Aintensity * ambientMtl +
                   + Diffusesity * diffuseMtl * s
                  + specularsity * specularMtl * pow((eyeDir * R), Ispecular);
   Output.TexCoord1 = Input.TexCoord1;
   return( Output );
  
}

 

Pixel Shader中新的东西是通过L和N计算反射向量R,并使用这个向量计算镜面反射光。因此,首先计算反射向量R:

R = 2 * (N.L) * N – L

在前面我们已经在计算漫反射光时计算了N和L的点乘。所以可以使用如下代码:

float4 R = 2 * s * Normaltemp - dir;

译者注:也可以使用HLSL内置的函数reflect计算Reflect向量,注意在LightDir前有个负号,想想为什么:

float3 R = normalize(reflect(-dir, Normaltemp ));

现在只剩下计算镜面反射光了。我们知道,这需要计算反射向量R和观察向量V的n次幂:(R.V)^n

n表示物体的光泽程度,n越大,反光区域越小。您可能注意到,我们使用了一个新的HLSL函数pow(a,b),它返回a的b次方。

float Specular = pow(saturate(dot(R , ViewDirection)), 15); 

我们终于准备将一切整合在一起并计算出最终的像素颜色:

return vAmbient + vDiffuseColor * Diff + vSpecularColor * Specular; 

这个公式你应该很熟悉了,对不对? 我们首先计算环境光和漫反射光并把它们相加。然后将镜面反光的颜色乘以刚才算出的反光强度Specular,并和环境光颜色和漫反射颜色相加。

 

当然,我们还要指定一个technique 并编译Vertex 和Pixel shader:

technique SpecularLight
{
    pass P0 
    {
        // compile shaders VertexShader = compile vs_1_1 VS(); 
        PixelShader = compile ps_2_0 PS();
    }
}

你可能感兴趣的:(vector,struct,input,float,shader,output)