6.4在Unity Shader中实现漫反射光照模型
基本光照模型中的漫反射部分的计算公式: 漫反射光照模型符合兰伯特定律:平面上一个点的漫反射光照强度与其法线与入射光夹角的余弦值呈正比。 由上式可以得出,计算漫反射需要四个参数,分别是clight:入射光线的强度以及方向、mdiffuse:材质的漫反射系数、n:顶点法线、l:光源方向。截断负值除了max函数外,还可以使用CG函数saturate 用法:saturate(x) 参数:x,可以是矢量或者标量,类型可以为float, float2, float3 作用:把x截取在[0,1]之间,如果x是矢量,就对x的每一个分量这样操作。
6.4.1逐顶点光照
Shader "Unity Shader Book/Chapter 6/Diffuse Vertex-Level"
{
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;
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"
}
6.4.2逐像素光照
Shader "Unity Shader Book/Chapter 6/Diffuse Pixel Level Mat"
{
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 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"
}
左:逐顶点光照 右:逐像素光照 逐像素光照的效果可以比逐顶点光照达到更加平滑的效果,但是在暗部(光照无法达到的区域)是全黑的,没有任何的渐变,背光区域像是一个平面,缺失了细节。虽然可以通过增加环境光来进行改善,但是依旧改变不了背光面明暗一样。由此产生了在原兰伯特模型上出现的半兰伯特模型。
6.4.3 半兰伯特模型
广义的半兰伯特光照模型公式:
通常情况下,该公式为: 这种方法可以将法线与入射光线的点积结果从[-1,1]映射到[0,1]内,使背光区域也有了明暗变化。 但是半兰伯特没有任何物理依据,只是一个视觉加强技术。
Shader "Unity Shader Book/Chapter 6/Half Lambert"
{
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 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);
//Compute diffuse term
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
}
}
|