逐顶点光照
逐顶点光照计算是在顶点着色器函数内计算完成的,计算每个顶点的颜色输出给片段着色器进行插值计算,漫反射光照模型计算公式:
C
d
i
f
f
u
s
e
=
(
c
l
i
g
h
t
⋅
m
d
i
f
f
u
s
e
)
m
a
x
(
0
,
n
⃗
⋅
l
⃗
)
C_{diffuse}=(c_{light} ⋅m_{diffuse})max(0,\vec{n} ⋅\vec{l})
Cdiffuse=(clight⋅mdiffuse)max(0,n⋅l)
其中, c l i g h t c_{light} clight:入射光线的颜色。 m d i f f u s e m_{diffuse} mdiffuse:材质的漫反射颜色, n ⃗ \vec{n} n:表面法线, l ⃗ \vec{l} l:光源方向(指向光源)。
Shader "myShaderTest/DiffuseVertex"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
//在Pass第一行指定渲染光照模式
//Tags{"Lightmode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//引用Unity内置文件,需要用到一些内置变量
#include "UnityCG.cginc"
#include "Lighting.cginc"
//定义与Properties中匹配的变量
fixed4 _Diffuse;
//顶点着色器输入,顶点和法线
struct a2v {
//模型坐标系坐标位置
float4 vertex : POSITION;
float3 normal : NORMAL;
};
//片元着色器输入,裁剪坐标系下顶点位置和颜色
struct v2f {
//裁剪坐标系顶点位置
float4 pos : SV_POSITION;
float3 color : COLOR;
};
//顶点着色器,输入为模型顶点和法线,输出返回v2f结构体
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;
//o.color = diffuse;
return o;
}
//片元着色器,输入为顶点着色器输出值
fixed4 frag(v2f i) :SV_Target{
//直接返回计算的顶点颜色
return fixed4(i.color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐像素光照
逐像素光照计算是在片元着色器函数中实现的,可以使模型表面光线过度更加平滑。
struct a2v {
//模型坐标系坐标位置
float4 vertex : POSITION;
float3 normal : NORMAL;
};
//片元着色器输入,裁剪坐标系下顶点位置和颜色
struct v2f {
//裁剪坐标系顶点位置
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
};
//顶点着色器,输入为模型顶点和法线,输出返回v2f结构体
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);
}
半兰伯特模型
C
d
i
f
f
u
s
e
=
(
c
l
i
g
h
t
⋅
m
d
i
f
f
u
s
e
)
(
0.5
(
n
⃗
⋅
l
⃗
)
+
0.5
)
C_{diffuse}=(c_{light} ⋅m_{diffuse})(0.5(\vec{n} ⋅\vec{l})+0.5)
Cdiffuse=(clight⋅mdiffuse)(0.5(n⋅l)+0.5)
兰伯特漫反射模型在光线无法照到的表面一片漆黑,半兰伯特模型背光面也有明暗变化。
//片元着色器,输入为顶点着色器输出值
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)*0.5+0.5);
fixed3 color = ambient + diffuse;
return fixed4(color,1.0);
}
如下分别为逐顶点兰伯特、逐像素兰伯特、逐像素半兰伯特效果图。