文章目录
上一次我们学习了如何实现一个简单的顶点/片元着色器,今天来学习一下漫反射光照模型以及半兰伯特光照模型的实现。
一、基础光照模型中漫反射部分的计算公式
其中n为模型表面法线,l为光源方向
二、逐顶点光照实现
在unity中的操作步骤与上一次相同
逐顶点代码
Shader "Unity Shader Book/Chapter6-DiffuseVertexLevel"
{
Properties
{
_Diffuse("Diffuse",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 pos:SV_POSITION;
fixed3 color:COLOR;
};
v2f vert(a2v v)
{
v2f o;
//利用MVP矩阵将顶点位置从模型空间转换到裁剪空间
o.pos = UnityObjectToClipPos(v.vertex);
//获取环境光
fixed3 Ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
//将顶点法线坐标从模型空间转到世界空间坐标下
fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
//获取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
o.color=Ambient+diffuse;
return o;
}
fixed4 frag(v2f i) : SV_Target{
return fixed4(i.color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐顶点效果图
从该效果图中我们可以看出,虽然整体效果还不错,但是一旦我们仔细观察就会发现在暗面与亮面的交汇处有一些明细的锯齿,接下来我们可以使用逐像素的方式来实现该光照模型,这一方法能够很好改善模型的锯齿效果。
三、逐像素光照模型实现
要实现逐像素光照模型,我们只需在逐顶点的代码中进行修改
逐像素代码
Shader "Unity Shader Book/Chapter6-DiffusePixelLevel"
{
Properties
{
_Diffuse("Diffuse",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target{
fixed3 Ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLightDir));
fixed3 color=Ambient+diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐像素效果图
通过该效果图,我们可以明显看出在亮面与暗面的交界处明显平滑了许多,但同样的逐像素光照同样也有一些缺点,就是它的阴影部分几乎是全黑的,没有亮度的变化,为此我们可以使用一种新的模型,半兰伯特光照模型。
四、半兰伯特光照模型实现
广义半兰伯特光照模型公式
通过该公式我们可以看出该模型没有用max操作来防止nl的点积为负值,而是对点积的结果进行α倍的缩放再加上一个β值的偏移,其中在大多数情况下α和β的值均为0.5.
半兰伯特光照模型代码实现
Shader "Unity Shader Book/Chapter6-DiffuseHalfLambertLevel"
{
Properties
{
_Diffuse("Diffuse",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target{
fixed3 Ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
fixed halfLambert=dot(worldNormal,worldLightDir)*0.5+0.5;
fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*halfLambert;
fixed3 color=Ambient+diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
半兰伯特效果图
半兰伯特光照模型的阴影部分是由亮逐渐变暗具有层次感,该光照模型的效果相对于上面两种模型更加贴近于真实关照模型。