兰伯特光照模型是一种理想的漫反射光照模型,又分为全兰伯特光照模型和半兰伯特光照模型,以下以全兰伯特光照模型举例:
逐顶点光照:
Shader "MyShader/Diffuse_Vertex"{
Properties{
_Diffuse("Diffuse Color",Color)=(1,1,1,1)//定义颜色属性
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"} //只有定义了正确的LightMode才能得到一些Unity的内置光照变量
CGPROGRAM
#include "Lighting.cginc"//类似C#中的命名空间,引用之后才能使用unity内置的一些变量
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;//小细节:使用属性之前必须在Pass块中再次重复定义
struct av{
float4 ps:POSITION;//告诉Unity把模型空间下的顶点坐标填充给ps
float3 nm:NORMAL;//告诉Unity把模型空间下的法线方向填充给nm
};
struct vf{
float4 position:SV_POSITION;
fixed3 cl : COLOR;
};
vf vert(av v){
vf f;
f.position=UnityObjectToClipPos(v.ps);
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;//获取环境光的颜色并将其填充给ambient
fixed3 normalDir=normalize(mul(v.nm,(float3x3)unity_WorldToObject));//把法线方向从模型空间转换到世界空间并将其单位化
fixed3 lightDir=normalize(_WorldSpaceLightPos0.xyz);//lightDir接收被单位化之后的直射光坐标
fixed3 diffused=_LightColor0.rgb*max(0,dot(normalDir,lightDir))*_Diffuse.rgb; //漫反射=直射光颜色*max(0,光和法线的夹角)*需要融合的颜色 注意:融合颜色直接在公式中相乘
f.cl=diffused+ambient;//注意:叠加颜色直接相加
return f ;
}
fixed4 frag(vf f):SV_Target{
return fixed4(f.cl,1);
}
ENDCG
}
}
Fallback "VertexLit"
}
逐像素光照:
Shader "MyShader/Diffuse_Fragment"{
Properties{
_Diffuse("Diffuse Color",Color)=(1,0,1,1)
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed3 _Diffuse;
struct a2v{
float4 ps:POSITION;
float3 nm:NORMAL;
};
struct v2f{
float4 position:SV_POSITION;
fixed3 cl:COLOR;
};
v2f vert(a2v v){
v2f f;
f.position=UnityObjectToClipPos(v.ps);
f.cl=mul(v.nm,(float3x3)unity_WorldToObject);
return f;
}
fixed4 frag(v2f f):SV_Target{
float3 ambient0=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 normalDir=normalize(f.cl);
fixed3 lightDir=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffused=_LightColor0.rgb*max(0,dot(lightDir,normalDir))*_Diffuse;
fixed3 tempColor=diffused+ambient0;
return fixed4 (tempColor,1);
}
ENDCG
}
}
Fallback "Diffuse"
}
逐顶点光照和逐像素光照渲染出来的效果区别如下:
很显然,通过逐像素光照渲染出来的效果明显比前者平滑很多,但是相对应的,耗费的性能也更多。
半兰伯特光照模型:
半兰伯特光照模型基本上与全兰伯特光照模型公式相差不大,代码稍作修改即可:
fixed3 diffused=_LightColor0.rgb*max(0,dot(lightDir,normalDir))*_Diffuse;
修改为:
float3 halfLambert=dot(lightDir,normalDir)*0.5+0.5;
fixed3 diffused=_LightColor0.rgb*halfLambert*_Diffuse;
全兰伯特光照模型与半兰伯特光照模型对比:
可以看出,半兰伯特光照模型对于阴影部分的处理的更好。全兰伯特光照模型在计算阴影部分时就有点不尽人意。
涉及到的知识点:
1:什么是光照模型?
光照模型就是一个公式,使用这个公式来计算某个点的光照效果。
2:漫反射公式?
漫反射=直射光颜色*max(0,cos夹角(光和法线的夹角)) cosθ=光方向·法线方向
3:#include “Lighting.cginc”:类似C#中引用命名空间,引用之后才能使用Unity内置的一些变量
4:Tags{“LightMode”=“ForwardBase”}:只有定义了正确的LightMode才能得到一些Unity的内置光照变量
5:max(x,n):两个参数之间取最大的一个
6:dot(x,n):取得两个向量点积
7:_WorldSpaceLightPos0 :取得直射光的位置
8:_LightColor0:取得直射光的颜色
9:(float3x3)unity_WorldToObject:模型矩阵的逆(补充:unity_ObjectToWorld是模型矩阵)
10:UNITY_LIGHTMODEL_AMDIENT:获取环境光
11:normalize():把一个向量单位化,方向不变,长度为1
12:颜色融合和颜色叠加:
1)颜色融合:颜色融合直接在公式中相乘,如漫反射融合物体本身材质颜色就相乘。
2)颜色叠加:因为一个物体是受到自发光、高光反射、漫反射及环境光的综合作用,所以相互叠加。颜色叠加一般亮度会增加。
13:通过片元函数实现光照模型比通过顶点函数实现更消耗性能,因为片元函数是逐像素计算,而顶点函数是依赖模型的顶点进行计算。