Unity Shader系列文章:Unity Shader目錄-初級篇
Unity Shader系列文章:Unity Shader目錄-中級篇
效果:
溶解效果
所需貼圖:
箱子貼圖
法線貼圖
噪聲貼圖
shader代碼:
// 溶解效果 (附帶正確陰影投射)
Shader "Custom/Dissolve"
{
Properties
{
_BurnAmount ("Burn Amount", Range(0, 1)) = 0 // 控制消融程度
_LineWidth ("Line Width", Range(0, 0.2)) = 0.1 // 消融時(shí)邊緣的線寬
_MainTex ("Texture", 2D) = "white" { }
_BumpMap ("Normal Map", 2D) = "bump" { }// 法線紋理
_BurnFirstColor ("Burn First Color", Color) = (1, 0, 0, 1) // 消融邊緣第一種顏色
_BurnSecondColor ("Burn Second Color", Color) = (1, 0, 0, 1) // 消融邊緣第二種顏色
_BurnMap ("Burn Map", 2D) = "white" { }// 噪聲紋理
}
SubShader
{
Tags { "RenderType" = "Opaque" "Queue" = "Geometry" }
// Base Pass 計(jì)算平行光蚓胸、環(huán)境光
Pass
{
Tags { "LightMode" = "ForwardBase" }
// 關(guān)閉剔除盐固,正面和背面都會渲染 (因?yàn)橄跁?dǎo)致裸露模型內(nèi)部的構(gòu)造)
Cull Off
CGPROGRAM
#include "Lighting.cginc"
#include "AutoLight.cginc"
// 編譯指令剖煌,保證在pass中得到Pass中得到正確的光照變量
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
fixed _BurnAmount;
fixed _LineWidth;
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _BurnFirstColor;
fixed4 _BurnSecondColor;
sampler2D _BurnMap;
// 紋理的縮放和偏移系數(shù)
float4 _MainTex_ST;
float4 _BumpMap_ST;
float4 _BurnMap_ST;
// 應(yīng)用傳遞給定點(diǎn)著色器的數(shù)據(jù)
struct a2v
{
float4 vertex: POSITION; // 語義: 頂點(diǎn)坐標(biāo)
float3 normal: NORMAL; // 語義: 法線
float4 tangent: TANGENT; // 語義: 切線
float4 texcoord: TEXCOORD0; // 語義: 紋理坐標(biāo)
};
// 頂點(diǎn)著色器傳遞給片元著色器的數(shù)據(jù)
struct v2f
{
float4 pos: SV_POSITION; // 語義: 裁剪空間的頂點(diǎn)坐標(biāo)
float2 uvMainTex: TEXCOORD0;
float2 uvBumpMap: TEXCOORD1;
float2 uvBurnMap: TEXCOORD2;
float3 lightDir: TEXCOORD3;
float3 worldPos: TEXCOORD4;
SHADOW_COORDS(5) // 內(nèi)置宏:聲明一個(gè)用于對陰影紋理采樣的坐標(biāo) (這個(gè)宏參數(shù)需要是下一個(gè)可用的插值寄存器的索引值桦踊,這里是5)
};
// 頂點(diǎn)著色器
v2f vert(a2v v)
{
v2f o;
// 將頂點(diǎn)坐標(biāo)從模型空間變換到裁剪空間
// 等價(jià)于o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
// 計(jì)算紋理坐標(biāo) (縮放和平移)
// 等價(jià)于o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
// 內(nèi)置宏:TANGENT_SPACE_ROTATION(切線空間到模型空間的變換矩陣)等價(jià)于:
// 使用模型空間下的法線方向和切線方向叉積得到副切線方向
// float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
//定義 3x3 變換矩陣 rotation芳悲,分別將切線方向、副切線方向和法線方向按行擺放組成了這個(gè)矩陣漠趁。
// float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
TANGENT_SPACE_ROTATION;
// 將光向量從模型空間變換到切線空間
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
// 將觀察向量從模型空間變換到切線空間
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// 內(nèi)置宏:用于計(jì)算聲明的陰影紋理坐標(biāo)
TRANSFER_SHADOW(o);
return o;
}
// 片元著色器
fixed4 frag(v2f i): SV_TARGET
{
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
// burn.r - _BurnAmount < 0 則放棄此片元不繪制
clip(burn.r - _BurnAmount);
fixed3 tangentLightDir = normalize(i.lightDir);
// 若法線紋理Texture Type未設(shè)置成Normal map离唐,
// 要從像素映射回法線,即[0, 1]轉(zhuǎn)化到[-1, 1]
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// 如果設(shè)置了Normal map類型沸毁,Unity會根據(jù)平臺使用不同的壓縮方法峰髓,
// _BumpMap.rbg值不是對應(yīng)的切線空間的xyz值了,要用Unity內(nèi)置函數(shù)
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
fixed3 albedo = tex2D(_MainTex, i.uvMainTex);
// 獲得環(huán)境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 計(jì)算漫反射
// 蘭伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光顏色息尺;
// Kd:漫反射顏色携兵;
// N:單位法向量;
// L:單位光向量搂誉;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentLightDir, tangentNormal));
// 使用了 smoothstep 函數(shù)來計(jì)算混合系數(shù)徐紧。當(dāng)t值為1時(shí),表明該像素位于消融的邊界處炭懊;
// 當(dāng)t值為0時(shí)浪汪,表明該像素為正常的模型顏色,而中間的插值則表示需要模擬一個(gè)燒焦效果凛虽。
fixed t = 1 - smoothstep(0, _LineWidth, burn.r - _BurnAmount);
fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
// 為了讓效果更接近燒焦的痕跡,還使用 pow 函數(shù)對結(jié)果進(jìn)行處理
burnColor = pow(burnColor, 5);
// 計(jì)算陰影值和光照衰減
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));
return fixed4(finalColor, 1);
}
ENDCG
}
// 自定義用于投射陰影的 Pass (如果仍然使用普通的陰影 Pass广恢,那么被剔除的區(qū)域仍然會向其他物體投射陰影凯旋,造成“穿幫”。)
Pass
{
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
fixed _BurnAmount;
sampler2D _BurnMap;
float4 _BurnMap_ST;
struct v2f
{
V2F_SHADOW_CASTER;
float2 uvBurnMap: TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
return o;
}
fixed4 frag(v2f i): SV_TARGET
{
// 已經(jīng)消融的面片,不再投影
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount);
// Unity內(nèi)置宏至非,把結(jié)果輸出到深度圖和陰影映射紋理中
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
Fallback "Diffuse"
}