Shader(四)邊緣發光和卡通光照

上一章我們講了一個基本光照,其中起決定性作用的就是法線。那我們就接著講講法線的一些常用途徑。(其實是最近太忙了)

邊緣發光是特效中最最常見的一種,下面我們來看看是如何實現的。

最終效果

源代碼

Shader "QQ/Rim"n{ntPropertiesnt{ntt_RimColor("Color",Color) = (0,0,1,1)ntt_MainTex("Texture", 2D) = "white" {}nt}nttSubShadernt{nttTags { "RenderType" = "Opaque" }nttLOD 100nnttPassntt{ntttCGPROGRAMnttt#pragma vertex vertnttt#pragma fragment fragnttt#pragma multi_compile_fognttt#include "UnityCG.cginc"nntttstruct a2vnttt{nttttfloat4 vertex : POSITION;nttttfloat2 uv : TEXCOORD0;nttttfloat3 normal : NORMAL;nttt};nntttstruct v2fnttt{nttttfloat4 pos : SV_POSITION;nttttfloat2 uv : TEXCOORD0;nttttfloat4 wPos : TEXCOORD1;nttttfloat3 normal : TEXCOORD2;nttt};nntttfixed4 _RimColor;ntttsampler2D _MainTex;ntttfloat4 _MainTex_ST;nntttv2f vert(a2v v)nttt{nttttv2f o;ntttto.pos = mul(UNITY_MATRIX_MVP, v.vertex);ntttto.uv = TRANSFORM_TEX(v.uv, _MainTex);ntttto.normal = UnityObjectToWorldNormal(v.normal);ntttto.wPos = mul(unity_ObjectToWorld, v.vertex);nttttreturn o;nttt}nntttfixed4 frag(v2f i) : SV_Targetnttt{nttttfixed4 col = tex2D(_MainTex, i.uv);nttttfloat3 view = normalize(_WorldSpaceCameraPos.xyz - i.wPos.xyz);nttttfloat NdotV = dot(i.normal,view);nttttfloat fresnel = max(0, _RimColor.a * 2.0 - NdotV);nttttcol.rgb += _RimColor.rgb * fresnel;nttttreturn col;nttt}ntttENDCGntt}nt}n}n

解讀代碼

這裡我們定義了一個顏色屬性,來決定邊緣發光的顏色是什麼

下面的代碼和上一篇相同,這裡就不再贅述了。

直接跳到片元函數如下。

這裡和上篇不同的是,上篇我們求的是光線的入射角度,

而這裡求的是攝像機的角度。

同樣的,我們對攝像機入射角度和法線點乘,得出攝像機和法線的角度關係。

因為當入射角和法線重合的話,值為1。即:cos(0°),而互相正交的話,則值為0。即cos(90°)

我們需要的是邊緣,所以這裡 1 - dot(normal,view)。

這裡我為了用顏色的alpha通道來做控制。所以用了alpha來減。

這裡max,是為了保證值最終不會為負。

max的函數內部實現大概為:

float max(float a,float b)n{n if(b>a)n return b;n elsen return a;n}n

而,其他min,saturate,clamp等限制函數類似。

最終我們把顏色加到之前的顏色上,輸出。就得到了邊緣發光的效果~

那這裡我們為什麼要把變數名定為fresnel呢。這個值還能做什麼呢?

下一篇我們通過法線,來實現lineout的外描邊效果。

感興趣的可以先試下。

下面為上篇文章的結尾問題

lambert 就自行百度吧,哈哈

Q:最後我們的模型還缺少那些信息才算比較完整且貼近現實呢?

A:缺少環境光,現實世界中,因為光的散射、反射、穿透等原因,會造成不被光直射的地方也會產生光亮。比如:泊松光斑(表示這與我什麼關係),所以我們要加行代碼

UNITY_LIGHTMODEL_AMBIENT,為unity環境光的宏變數。是個float4類型,這裡我們只取RGB通道。

Q:代碼中有一個BUG?

A:如果你實實在在的手寫了並測試了,你會發現燈光方向的背面是黑色的。

還記得上篇我們說的點乘的結果是1到-1。因為如果兩個向量模長為1的話,結果就是cosθ。即一個餘弦函數。

所以當光的方向與模型法線成反方向時,數值成負數,這是不對的,如果自乘就造成你的顏色全變黑等問題。所以正確的是

這裡,我們用max限定值最低為0。

那我們這篇的邊緣發光需要max來限定點乘的結果嗎??

Q:平行光該如何處理呢?

平行光最簡單了,直接拿來用就行了。即:

Q:結合這一篇內容,你能寫出分階的卡通光照的效果嗎?

很簡單,對點乘值分段處理即可。

最終效果

完整代碼

Shader "QQ/Cartoon"n{ntPropertiesnt{ntt_LightColor("Light Color",color) = (1,1,1,1)ntt_LightProperty("xyz:Light Position w:intentsity",vector) = (0,1,0,1)ntt_MainTex("Texture", 2D) = "white" {}ntt_Step("Step,",float) = 3.0nt}nttSubShadernt{nttTags { "RenderType" = "Opaque" }nttLOD 100nnttPassntt{ntttCGPROGRAMnttt#pragma vertex vertnttt#pragma fragment fragnttt#include "UnityCG.cginc"nntttstruct a2vnttt{nttttfloat4 vertex : POSITION;nttttfloat2 uv : TEXCOORD0;nttttfloat3 normal :NORMAL;nttt};nntttstruct v2fnttt{nttttfloat4 pos : SV_POSITION;nttttfloat2 uv : TEXCOORD0;nttttfloat3 wPos : TEXCOORD1;nttttfloat3 normal : TEXCOORD2;nttt};nntttsampler2D _MainTex;ntttfloat4 _MainTex_ST;ntttfixed4 _AmbintColor;ntttfixed4 _LightColor;ntttfloat4 _LightProperty;ntttfloat _Step;ntttv2f vert(a2v v)nttt{nttttv2f o;ntttto.pos = mul(UNITY_MATRIX_MVP, v.vertex);ntttto.uv = TRANSFORM_TEX(v.uv, _MainTex);ntttto.wPos = mul(unity_ObjectToWorld,v.vertex);ntttto.normal = UnityObjectToWorldNormal(v.normal);nttttreturn o;nttt}nntttfixed4 frag(v2f i) : SV_Targetnttt{nttttfloat3 lightDir = normalize(_LightProperty.xyz - i.wPos);nttttfloat atten = max(0,dot(lightDir,i.normal));nttttatten = ((int)(atten * _Step))/ _Step;nttttatten = atten * _LightProperty.w;nttttfixed4 tex = tex2D(_MainTex, i.uv);nttttfixed4 col = tex * _LightColor * atten;nttttcol.rgb += tex.rgb * UNITY_LIGHTMODEL_AMBIENT.rgb;nttttreturn col;nttt}ntttENDCGntt}nt}n}n

你寫對了嗎?

推薦閱讀:

如何學好 Unity?
Chrome 不支持 NPAPI 後,以前 Unity 項目該何去何從?
如何評價使用Unity引擎製作的《琪亞娜·極樂凈土》?
我該如何進入USC遊戲設計專業?

TAG:Unity游戏引擎 | shader |