UnityShader之六 表面着色器-自定义光照模式
UnityShader系列文章转载于 @浅墨_毛星云,为本人的学习笔记,转载请注明出处。
文章链接:https://blog.****.net/poem_qianmo/article/details/42611531
漫反射
漫反射,是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这种反射的光称为漫射光。
Lambert模型
漫反射光的强度近似地服从于Lambert定律,即漫反射光的光强仅与入射光的方向和反射点处表面法线向量夹角的余弦成正比。
原理公式: diffuse = I*cosθ;
-
diffuse:反射光线的光强
-
I:入射光线的光强
-
cosθ:入射光线和该顶点法线的余弦,即:cosθ=dot(L,N);
Lambert模型较好地表现了粗糙表面上的光照现象。
镜面反射
如果光照射到相当光滑的表面,就会产生镜面反射(specular reflection),镜面反射的特点是在光滑表面会产生一块称之为高光(high light)的特亮区域。
镜面反射遵循光的反射定律:反射光与入射光位于表面法线向量的两侧,对理想反射面(如镜面),入射角等于反射角,观察者只能在表面法线向量的反射方向一侧才能看到反射。
Phong光照模型
Phong模型提出了计算镜面高光的经验模型,镜面反射光强与反射光线和视线的夹角a相关。
原理公式: Ispecular = Ks*Is*pow(cos a,n)
-
Ks为物体表面的高光系数
-
Is为光强
-
a是反射光与视线的夹角
-
n为高光指数,n越大,则表面越光滑,反射光越集中,高光范围越小。
Blinn-Phong光照模型
Blinn-Phong模型是基于Phong模型进行改造的
原理公式: Ispecular = Ks*Is*pow(dot(N,H),n)
-
N为顶点法线向量
-
H = (L+V)/ |L+V| 即:normalize(lightDir + viewDir);
在Unity中,Phong实际上指的是Blinn-Phong,两者指的同一种内置光照模型
自定义光照模式(custom lighting model)
在编写表面着色器的时候,我们通常要描述一个表面的属性(反射率颜色,法线,。。。)、并通过光照模式来计算灯光的相互作用。在Unity中,内置的光照模式有两种,分别是Lambert(漫反射光diffuse lighting)和Blinn-Phong(镜面反射光(高光),specular lighting)模式。另外,我们可以自定义光照模式。
光照模式的声明方式
在Unity Shaderlab和CG语言中,光照模式是一个以Lighting开头+自定义文字组合在一起的函数。
即函数名为:
Lighting+[自定义部分]
一个可行的函数名:LightingTypedefLightingMode
光照模式函数的物种函数原型
-
half4 LightingName (SurfaceOutput s, half3 lightDir, half atten);
-
此种形式的函数可以表示在正向渲染路径(forward rendering path)中的光照模式,且此函数不取决于视图方向(view direction)
-
-
half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);
-
此种形式的函数可以表示在正向渲染路径(forward rendering path)中的光照模式,且此函数包含了视图方向(view direction)
-
-
half4 LightingName_PrePass (SurfaceOutput s, half4 light);
-
此种形式的函数可以在延时光照路径(deferred lighting path)中使用
-
-
half4 LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal);
-
这种形式也是不依赖于视图方向(view direction)的光照模式。例如:漫反射(diffuse)。
-
-
half4 LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, half3 viewDir, bool surfFuncWritesNormal,out half3 specColor);
-
这种形式依赖于视图方向(view direction)的光照模式(light model)
-
对于形式四和形式五的选择,主要取决于我们的光照模式是否依赖视图方向。需要注意的是,这两个函数将自动处理正向和延时光照路径(forwardand deferred lighting rendering paths)。
需要特别注意的是:Unity在移动平台中暂时不支持延延迟光照渲染
自定义光照模式使用示例
#pragma surface surf TypedefLigtingMode
half4 LightingTypedefLigtingMode (SurfaceOutputs, half3 lightDir, half3 viewDir, half atten);
综合应用
Shader "typedef/Volume6/5.自定义卡通光照模型v2" {
Properties {
_MainTex ("【主纹理】 (RGB)", 2D) = "white" {}
_Ramp("【渐变纹理】",2D) = "white"{}
_DetailTex("【细节纹理】",2D) = "gray"{}
_RimColor("【边缘颜色】",Color) = (0.26,0.19,0.16,0.0)
_RimPower("【边缘颜色强度】",Range(0.6,9)) = 1
}
SubShader {
//子着色器标签
Tags{"RenderType" = "Opaque"}
//开始CG着色器编程语言段
CGPROGRAM
//声明光照模式
#pragma surface surf TypedefCartoonShader
//声明变量
sampler2D _MainTex;
sampler2D _Ramp;
sampler2D _DetailTex;
float4 _RimColor;
float _RimPower;
//自定光照函数
half4 LightingTypedefCartoonShader(SurfaceOutput s,half3 lightDir,float atten){
//漫反射光照强度
half diff = max(0,dot(s.Normal,lightDir));
//增强光强
half hLambert = diff* 0.5 +0.5;
//从纹理中定义渐变效果
half3 ramp = tex2D(_Ramp,float2(hLambert,hLambert)).rgb;
//计算最终的光照颜色
half4 color;
color.rgb = s.Albedo * _LightColor0 * ramp * (atten);
color.a = s.Alpha;
return color;
}
//输入结构
struct Input{
float2 uv_MainTex;
float2 uv_DetailTex;
float3 viewDir;
};
//表面着色函数
void surf(Input IN,inout SurfaceOutput o){
//设置基础纹理
o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
//添加细节纹理
o.Albedo *= tex2D(_DetailTex,IN.uv_DetailTex).rgb;
//计算自发光强度系数
half rim = 1.0 - saturate(dot(normalize(IN.viewDir),o.Normal));
o.Emission = _RimColor.rgb * pow(rim,_RimPower);
}
//结束CG着色器编程语言段
ENDCG
}
FallBack "Diffuse"
}