本代碼只適用于平行光,
1、逐頂點漫反射光照
1.1漫反射光照原理

1.2代碼實作
在Properties語意塊中宣告一個漫反射顏色屬性
Properties
{
//漫反射引數,用于調整漫反射效果
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
}
在SubShader語意塊中定義一個Pass語意塊,
在Pass的第一行指明光照模式,
Tags{"LightMode" = "ForwardBase"}
在Pass中,使用CGPROGRAM與ENDCG包圍Cg代碼片,并告訴Unity頂點著色器與片元著色器的名字,
使用#include包含Unity的內置檔案,
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//包含內置檔案
#include "Lighting.cginc"
ENGCG
接著在CGPROGRAM與ENDCG中定義此前在Properties中定義的屬性,
fixed4 _Diffuse;
定義輸入與輸出結構體
struct a2v
{
//模型變換
float4 vertex_position : POSITION;
//法線方向(計算)
float3 normal : NORMAL;
};
struct v2f
{
//輸出頂點位置
float4 pos : SV_POSITION;
//存盤頂點著色器輸出的顏色
fixed3 color_in : COLOR;
};
主要計算都集中在頂點著色器,在頂點著色器中,我們有兩個目標:把坐標從模型空間轉換至裁剪空間中,以及輸出漫反射計算之后的color,
坐標變換很簡單,代碼如下:
//pos變換
o.pos = UnityObjectToClipPos(v.vertex_position);
計算顏色要復雜一些,根據最上方的公式,我們一共需要四個變數,_Diffuse變數我們已經定義好,光源顏色不需要運算(_LightColor0.rgb可直接得到),接下來需要獲取世界坐標下的光源方向、世界坐標下的法線方向,
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
//法線原本是模型空間下的,需要變換至世界坐標,因此需要mul()
//光源方向本來就是世界坐標下的,因此不需要mul()
//使用normalize()進行歸一化
隨后可以進行最重要的漫反射運算
防止法線和光源方向點乘的結果為負值,將其截取到0,這可以防止物體被從后面來的光源照亮,
saturate函式是Cg提供的函式,可以將引數截取在[0,1]的范圍內,
dot()函式點乘兩個向量,
//漫反射部分計算
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
加上環境光輸出
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
return o;
別忘了在shader最后Fallback,FallBack "Diffuse"
最終寫成如下代碼:
Shader "Practise/DiffuseVertexShader"
{
Properties
{
//漫反射引數,用于調整漫反射效果
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//包含內置檔案
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v
{
//模型變換
float4 vertex_position : POSITION;
//法線方向(計算)
float3 normal : NORMAL;
};
struct v2f
{
//輸出頂點位置
float4 pos : SV_POSITION;
//存盤頂點著色器輸出的顏色
fixed3 color_in : COLOR;
};
v2f vert(a2v v)
{
//兩個目標:輸出變換后的pos坐標、輸出計算后的color
v2f o;
//pos變換
o.pos = UnityObjectToClipPos(v.vertex_position);
//color計算
//變數準備
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); //法線原本是模型空間下的,需要變換至世界坐標
//環境光部分
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射部分計算
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
//整理輸出
o.color_in = ambient + diffuse;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return fixed4(i.color_in, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
效果如下:

2、逐像素漫反射光照
逐像素光照中,主要的計算程序需要放到片元著色器中,頂點著色器只需要將變換后的位置坐標、變換后的世界空間中的發現坐標傳到片元著色器中即可,
在逐頂點漫反射的基礎上修改:
//輸入頂點著色器
struct a2v{
float3 pos : POSITION;
float3 normal : NORMAL;
};
//頂點著色器輸出
struct v2f{
float4 worldpos : SV_POSITION;
fixed3 worldnormal : TEXCOORD0;
};
TEXCOORD0、TEXCOORD1 等語意用于指示任意高精度資料,如紋理坐標和位置,
把計算挪到片元著色器:
v2f vert(a2v v)
{
// 回傳
v2f o;
//變換pos
o.worldpos = UnityObjectToClipPos(v.pos);
//變換法線
o.worldnormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
fixed3 worldNormal = normalize(i.worldnormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
//計算漫反射
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
//環境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0);
}
得到代碼:
Shader "Prcatice/DiffusePixelShader"
{
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{
float3 pos : POSITION;
float3 normal : NORMAL;
};
//頂點著色器輸出
struct v2f{
float4 worldpos : SV_POSITION;
fixed3 worldnormal : TEXCOORD0;
};
v2f vert(a2v v)
{
// 回傳
v2f o;
//變換pos
o.worldpos = UnityObjectToClipPos(v.pos);
//變換法線
o.worldnormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
fixed3 worldNormal = normalize(i.worldnormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
//計算漫反射
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
//環境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
最終效果:

3、對比
逐頂點漫反射可能會在向光面與背光面的之間形成一些鋸齒,
(如下圖,左側為逐像素漫反射,右側為逐頂點漫反射,右側陰影明顯有菱形鋸齒)

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/465033.html
標籤:其他
