在上一篇教程中介绍了phong model下的一般光照,并且用Cg着色语言实现了固定渲染管线中gouraud shading光照效果。由于是基于vertex的光照,虽然采用gouraud shading要比flat shading效果好的多,但是和phong shading的效果相差很大。这里大家要注意的是,phong model和phong shading的区别。上一篇教程所讲的光照模型叫phong model,而这篇教程要介绍的一种着色方法叫phong shading,必须要使用shader才能实现。下面的图中对比了采用flat shading,gouraud shading和phong shading技术渲染的一个圆环。
有了上一篇教程的基础,用Cg来实现基于pixel lighing的phong shading光照就容易多了。绝大部分的Cg shader代码都是一样的,主要的改变就是这次不是在vertex shader里光照,而是在fragment shader里计算光照。所以整个vertex shader的代码很简单,将要渲染的物体的顶点位置,顶点法线传入fragment shader就是vertex shader的全部工作。整个vertex shader的代码如下。
03vs.cg
- struct output
- {
- float4 position : POSITION;
- float3 objectPos : TEXCOORD0;
- float3 normal : TEXCOORD1;
- };
- output vs_main( float4 position : POSITION,
- float3 normal : NORMAL,
- uniform float4x4 MV,
- uniform float4x4 MVP
- )
- {
- output OUT;
- OUT.position = mul(MVP, position);
- OUT.objectPos = mul(MV, position).xyz;
- OUT.normal = mul(MV, float4(normal,0.0)).xyz;
- return OUT;
- }
上面的代码的输出结构体中有三个成员。Position是传入的物体的顶点,该顶点将用ModelViewProjection矩阵转换成剪裁坐标系中的坐标供光栅化使用。一旦将坐标转换后,我们就无法在fragment shader中使用物体的顶点坐标了。由于要在fragment shader中计算光照,所以我们要将物体的顶点位置,顶点法线都传入到fragment shader中。这里物体转换前的顶点和法线分别使用了语义TEXCOORD0和TEXCOORD1,代表将它们作为纹理坐标后传入fragment shader。这样GPU会把顶点和法线信息当做纹理坐标来处理,在贴图的时候,GPU会根据纹理自动插值计算每个像素对应的颜色,而现在GPU插值计算出来的就是每个像素对应的坐标和法线信息了。有了这些数据就可以进行phong shading的计算了。光照计算的公式和方法和上一个教程介绍的一模一样。下面是fragment shader的代码。
03fs.cg
- uniform float3 LightPosition;
- uniform float3 eyePosition;
- uniform float3 I;
- uniform float3 Ka;
- uniform float3 Kd;
- uniform float3 Ks;
- uniform float shininess;
- struct input{
- float3 objectPos: TEXCOORD0;
- float3 normal : TEXCOORD1;
- };
- struct output{
- float4 color : COLOR;
- };
- output fs_main( in input IN )
- {
- output OUT;
- float3 N = normalize(IN.normal);
- float3 P = IN.objectPos;
- float3 L = normalize(LightPosition - P);
- float NdotL = max(dot(N,L),0);
- float3 ambient = Ka * I;
- float3 diffuse = Kd * I * NdotL;
- float3 V = normalize(eyePosition - P);
- float3 H = normalize(L+V);
- float NdotH = pow(max(dot(N,H), 0), shininess);
- if(NdotL<=0)
- NdotH = 0.0;
- float3 specular = Ks*I*NdotH;
- float3 color = ambient + diffuse + specular;
- OUT.color.xyz= color;
- OUT.color.w = 1.0;
- return OUT;
- }