Phong和Blinn-Phong光照模型
Phong和Blinn-Phong是计算镜面反射光的两种光照模型,两者仅仅有很小的不同之处。
1.Phong模型
Phone模型计算中的一个关键步骤就是反射向量R的计算:
上图中的位于表面“下面”的向量 ‘I’ 是原始 ‘I’ 向量的拷贝,并且二者是一样的,现在我们的目标计算出向量 ‘R’ 。根据向量相加原则,向量 ‘R’ 等于 'I' + 'V',‘I’ 是已知的,所以我们需要做的就是找出向量 ‘V’。注意法向量 ‘N’ 的负方向就是 ‘-N’,我们可以在 ‘I’ 和 ‘-N’ 之间使用一个点乘运算就能得到 ‘I’ 在 ‘-N’ 上面的投影的模。这个模正好是 ‘V’ 的模的一半,由于 ‘V’ 与 ‘N’ 有相同的方向,我们可以将这个模乘上 ‘N’ (其模为 1 )再乘上 2 即可得到 ‘V’。总结一下就是下面的公式:
2.Blinn-Phong模型
Phong模型中计算反射光线的向量是一件相对比较耗时的任务,因此Blinn-Phong对这一点进行了改进。
Ks:物体对于反射光线的衰减系数
N:表面法向量
H:光入射方向L和视点方向V的中间向量
Shininess:高光系数
可见,通过该式计算镜面反射光是符合基本规律的,当视点方向和反射光线方向一致时,计算得到的H与N平行,dot(N,H)取得最大;当视点方向V偏离反射方向时,H也偏离N。
同时H的计算比起反射向量R的计算简单的多,R向量的计算需要若干次的向量乘法与加法,而H的计算仅仅需要一次加法。
下面是用cg着色语言书写的Phong和Blinn-Phong的顶点和片段着色程序
Phong_FragmentLighting_v.cg
1 struct V2F{ 2 float4 position:POSITION; 3 float3 worldPosition: TEXCOORD0; 4 float3 worldNormal :TEXCOORD1; 5 }; 6 void Phong_FragmentLighting_v(float4 position :POSITION, 7 float4 normal:NORMAL, 8 uniform float4x4 modelMatrix, 9 uniform float4x4 modelMatrix_IT, 10 uniform float4x4 modelViewProj, 11 out V2F O){ 12 O.position=mul(modelViewProj,position); 13 O.worldPosition=mul(modelMatrix,position).xyz; 14 O.worldNormal=normalize(mul(modelMatrix_IT,normal)).xyz; 15 }
Phong_FragmentLighting_f.cg
1 void Phong_FragmentLighting_f(float3 position :TEXCOORD0, 2 float3 normal: TEXCOORD1, 3 uniform float3 globalAmbient, 4 uniform float3 lightColor, 5 uniform float3 lightPosition, 6 uniform float3 eyePosition, 7 uniform float3 Ke, 8 uniform float3 Ka, 9 uniform float3 Kd, 10 uniform float3 Ks, 11 uniform float shininess, 12 out float4 color:COLOR) 13 { 14 float3 N=normalize(normal); 15 float3 L=normalize(lightPosition-position); 16 float3 V=normalize(eyePosition-position); 17 18 float3 R=reflect(-L,N); 19 R=normalize(R); 20 21 // Compute emissive term 22 float3 emissive = Ke; 23 24 // Compute ambient term 25 float3 ambient = Ka * globalAmbient; 26 27 // Compute the diffuse term 28 float diffuseLight = max(dot(N, L), 0); 29 float3 diffuse = Kd * lightColor * diffuseLight; 30 31 // Compute the specular term 32 float specularLight = pow(max(dot(V, R), 0), shininess); 33 if (diffuseLight <= 0) specularLight = 0; 34 float3 specular = Ks * lightColor * specularLight; 35 36 //color.xyz = emissive + ambient + diffuse + specular; 37 color.xyz=ambient + diffuse + specular; 38 color.w = 1; 39 }
BlinnPhong_FragmentLighting_v.cg
1 struct V2F{ 2 float4 position:POSITION; 3 float3 worldPosition: TEXCOORD0; 4 float3 worldNormal :TEXCOORD1; 5 }; 6 void BlinnPhong_FragmentLighting_v(float4 position :POSITION, 7 float4 normal:NORMAL, 8 uniform float4x4 modelMatrix, 9 uniform float4x4 modelMatrix_IT, 10 uniform float4x4 modelViewProj, 11 out V2F O){ 12 O.position=mul(modelViewProj,position); 13 O.worldPosition=mul(modelMatrix,position).xyz; 14 O.worldNormal=normalize(mul(modelMatrix_IT,normal)).xyz; 15 }
BlinnPhong_FragmentLighting_f.cg
1 void BlinnPhong_FragmentLighting_f(float3 position :TEXCOORD0, 2 float3 normal: TEXCOORD1, 3 uniform float3 globalAmbient, 4 uniform float3 lightColor, 5 uniform float3 lightPosition, 6 uniform float3 eyePosition, 7 uniform float3 Ke, 8 uniform float3 Ka, 9 uniform float3 Kd, 10 uniform float3 Ks, 11 uniform float shininess, 12 out float4 color:COLOR) 13 { 14 float3 N=normalize(normal); 15 float3 L=normalize(lightPosition-position); 16 float3 V=normalize(eyePosition-position); 17 18 float3 H=normalize(L+V); 19 20 // Compute emissive term 21 float3 emissive = Ke; 22 23 // Compute ambient term 24 float3 ambient = Ka * globalAmbient; 25 26 // Compute the diffuse term 27 float diffuseLight = max(dot(N, L), 0); 28 float3 diffuse = Kd * lightColor * diffuseLight; 29 30 // Compute the specular term 31 float specularLight = pow(max(dot(H, N), 0), shininess); 32 if (diffuseLight <= 0) specularLight = 0; 33 float3 specular = Ks * lightColor * specularLight; 34 35 color.xyz=ambient + diffuse + specular; 36 color.w = 1; 37 }
效果对比:
Phong光照模型
Blinn-Phong光照模型
通过简单的对比发现,在相同条件下Blinn-Phong的高光范围要比Phong更大,写实效果Phong光照模型更好。但算法简单,运行速度快是Blinn-Phong光照模型的优点。