![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNiZpdmL3EzM2MTNzEjM4EDOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.gif)
這是大緻的效果圖,圖檔壓得有點糊。我參考了本篇部落格 Unity shader護盾特效.
這是原部落格展示的圖檔:
本例采用了特殊的模型與貼圖,原部落格裡有視訊連結的教程,從模型到貼圖。
以下是代碼
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
Shader "Summer/Shield"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScanTex ("Scan Texture", 2D) = "white" {}
_Color("Color",Color)=(0,0,0,1)
_ScanColor("Scan Color",Color)=(0,0,0,1)
_RimColor("rim color",Color) = (1,1,1,1)//邊緣顔色
_RimPower ("rim power",range(1,10)) = 2//邊緣強度
_FresnelScale("ScanFresnelScale",range(0,1))=0.5
_ScanSpeed("ScanSpeed",range(0,2))=0.5
_ScanScale("ScanScale",range(0,1))=0.1
}
SubShader
{
Tags { "Queue"="Transparent"
"IgnoreProjector" = "True" //我們不希望任何投影類型材質或者貼圖,影響我們的物體或者着色器
"RenderType" = "Transparent" }
LOD 100
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;//涉及到了兩個UV通道
float2 scanUV:TEXCOORD1;
fixed4 color:COLOR;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
fixed4 vertColor:TEXCOORD1;
float2 scanUV:TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
sampler2D _ScanTex;
float4 _ScanTex_ST;
fixed4 _ScanColor;
float _ScanSpeed;
fixed4 _RimColor;
float _RimPower;
fixed _FresnelScale;
fixed _ScanScale;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.scanUV=TRANSFORM_TEX(v.scanUV,_ScanTex);
float3 V = WorldSpaceViewDir(v.vertex);
V = mul(unity_WorldToObject,V);//視方向從世界到模型坐标系的轉換
o.vertColor=v.color;
o.vertColor.r = dot(v.normal,normalize(V));
o.vertColor.r*=sign(o.vertColor.r);//取正,sign傳回其符号,使其計算的漫反射系數為正
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
i.scanUV.y+=_Time.y*_ScanSpeed;
fixed scanF=tex2D(_ScanTex,i.scanUV).a;
clip(-scanF);
col=col*i.vertColor.a*_Color;
fixed fresnel=_FresnelScale+(1-_FresnelScale)*pow((1-i.vertColor.r),_RimPower);
fixed fresnleAlpha=lerp(0,1,fresnel);
fixed4 backCol=fixed4(_RimColor.rgb,fresnleAlpha);
fixed4 fianlColor=(col+backCol)+_ScanColor*scanF;
return fianlColor;
}
v2f vert_Convex(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.scanUV=TRANSFORM_TEX(v.scanUV,_ScanTex);
float3 V = WorldSpaceViewDir(v.vertex);
V = mul(unity_WorldToObject,V);//視方向從世界到模型坐标系的轉換
o.vertColor=v.color;
o.vertColor.r = dot(v.normal,normalize(V));
o.vertColor.r*=sign(o.vertColor.r);
float4 pos=mul(UNITY_MATRIX_MV,v.vertex);
float3 normal=mul((float3x3)UNITY_MATRIX_IT_MV,v.normal);
normal.z-=0.2;
pos+=float4(normalize(normal),0)*(_ScanScale);
o.vertex=mul(UNITY_MATRIX_P,pos);
return o;
}
fixed4 frag_Convex (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
i.scanUV.y+=_Time.y*_ScanSpeed;
fixed scanF=tex2D(_ScanTex,i.scanUV).a;
if(scanF==0)
{
discard;
}
col=col*i.vertColor.a*_Color*(1-i.vertColor.a);
fixed fresnel=_FresnelScale+(1-_FresnelScale)*pow((1-i.vertColor.r),_RimPower);
fixed fresnleAlpha=lerp(0,1,fresnel);
fixed4 backCol=fixed4(_ScanColor.rgb,fresnleAlpha);
fixed4 fianlColor=col+backCol+fresnleAlpha*_ScanColor;
return fianlColor;
}
ENDCG
Pass
{
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex vert_Convex
#pragma fragment frag_Convex
#pragma multi_compile_fwdbase
ENDCG
}
}
}
主要思路是兩個Pass,一個渲染主體,該部分要剔除掉掃描到的部分;第二個Pass渲染掃描部分,即頂點經過偏移(向法線方向突出),該部分剔除未掃描的部分,進而實作一個動态掃描頂點偏移的效果。一開始我想的是一個Pass,在vert裡進行掃描圖檔(掃描圖檔:一張模拟掃描效果的貼圖,類似流光貼圖)的采樣,然後将該頂點偏移,但後來發現tex2D擷取的值不能在vert裡使用(我也不知道為什麼,一用就報錯),最後隻能用這種最簡單的方式。
第一個Pass部分
float3 V = WorldSpaceViewDir(v.vertex);
V = mul(unity_WorldToObject,V);//視方向從世界到模型坐标系的轉換
o.vertColor=v.color;
o.vertColor.r = dot(v.normal,normalize(V));
o.vertColor.r*=sign(o.vertColor.r);//取正,sign傳回其符号,使其計算的漫反射系數為正
不用saturate的原因是saturate将小于0的取為,而我關了剔除,這樣看到背面就是純色而沒有透明效果
i.scanUV.y+=_Time.y*_ScanSpeed;
fixed scanF=tex2D(_ScanTex,i.scanUV).a;//該圖是PNG,是以通過a通道便可區分
clip(-scanF);//剔除alpha大于0的像素
//菲涅爾效果
fixed fresnel=_FresnelScale+(1-_FresnelScale)*pow((1-i.vertColor.r),_RimPower);
fixed fresnleAlpha=lerp(0,1,fresnel);
fixed4 backCol=fixed4(_RimColor.rgb,fresnleAlpha);
第二個Pass部分
進行頂點變換,在視角空間下進行沿法線方向的頂點偏移
float4 pos=mul(UNITY_MATRIX_MV,v.vertex);
float3 normal=mul((float3x3)UNITY_MATRIX_IT_MV,v.normal);
normal.z-=0.2;
pos+=float4(normalize(normal),0)*(_ScanScale);
o.vertex=mul(UNITY_MATRIX_P,pos);
剔除Alpha值為零,即未掃描到的像素
i.scanUV.y+=_Time.y*_ScanSpeed;
fixed scanF=tex2D(_ScanTex,i.scanUV).a;
if(scanF==0)
{
discard;
}
好了,這大概就是全部了,感謝你的閱讀,如有錯誤,歡迎舉正。