日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

【ShaderLab实例笔记】Overwatch Shield - 守望先锋护盾特效制作笔记

發布時間:2024/1/8 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【ShaderLab实例笔记】Overwatch Shield - 守望先锋护盾特效制作笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

教程鏈接:Overwatch Shield
項目鏈接:OverwatchShieldTemplate
Pipeline & Shader:Built-in,Unlit

本文是對 Overwatch Shield 學習過程的記錄和總結,不是完全的翻譯,更多的細節和圖文建議跳轉原博

效果分析

使用守望先鋒中Reinhardt的護盾作為參考

整體觀察

可以看出護盾是半透明的,帶有一層基色,可以微微照亮周邊區域
繞著護盾走可以發現,護盾的背面也可以顯示(關閉Cull),并且有明顯的厚度

蜂窩紋理

蜂窩會從中心沿x軸方向搏動,仔細觀察還可以發現蜂窩并不是同時搏動的

蜂窩狀電流

電流沿著蜂窩狀邊緣向外擴散,不能難發現擴散的形狀一個菱形
所以這個效果其實是一個從護盾中心,從點變大的菱形框點亮蜂窩邊緣的過程

固定邊緣

仔細看就會發現邊緣效果由兩部分組成:固定邊緣、相交邊緣

相交邊緣

相交邊緣的效果基本和固定邊緣一樣

效果總結

  • 基礎效果:整體透明,帶有基色,Cull Off
  • 蜂窩紋理:蜂窩紋理從護盾中心沿x軸搏動,帶有一定先后順序
  • 電流脈沖:蜂窩電流從護盾中心按菱形擴散
  • 固定邊緣:越邊緣顏色越實
  • 相交邊緣:與其他物體交叉處有高亮
  • 自發光:護盾可以微微照亮周邊
  • 項目設置

    場景方面,模擬了視頻(圖片)中的物體:
    護盾與地面相交,有物體穿過護盾,方便測試效果

    另外項目的色彩空間使用了線性空間,因為線性空間的精度更高

    更多關于顏色空間的內容,可以查看GAMMA AND LINEAR SPACE - WHAT THEY ARE AND HOW THEY DIFFER和Unity User Manual Color space

    Shader編寫

    基礎效果

    目標效果:整體透明,帶有基色,Cull Off
    沒啥東西,改 Tag,關 Cull,設置 Blend,完事

    Pass {Tags {"RenderType" = "Transparent" "Queue" = "Transparent"}Cull offBlend SrcAlpha oneHLSLPROGRAM// ...fixed4 frag (v2f i) : SV_Target{return _Color;}ENDHLSL }

    基礎效果圖

    ?

    蜂窩搏動

    目標效果:蜂窩紋理從護盾中心沿x軸搏動,帶有一定先后順序

    效果拆分:

  • 蜂窩紋理 + 整體呼吸效果
  • 打亂呼吸順序
  • 呼吸效果沿x軸擴散
  • 1.蜂窩紋理 + 整體呼吸效果

    fixed4 frag (v2f i) : SV_Target {// 蜂窩圖案fixed4 pulseTex = tex2D(_PulseTex, i.uv);fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;// 呼吸效果pulseTerm *= abs(sin(_Time.y * _PulseTimeScale));return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a); }


    ?

    2.打亂呼吸順序

    打亂順序的方法有很多,因為這里使用的蜂窩紋理剛好有深淺變化,可以直接用來作為決定呼吸順序的因子

    這里的代碼會讓 r 通道越大的地方亮的越早

    fixed4 frag (v2f i) : SV_Target {// 蜂窩圖案fixed4 pulseTex = tex2D(_PulseTex, i.uv);fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;float breath = _Time.y * _PulseTimeScale;float pulseOffset = pulseTex.r * _PulseTexOffsetScale;// 隨機呼吸效果pulseTerm *= abs(sin(breath + pulseOffset));return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a); }

    為了防止 sin() 的負值導致奇怪的結果出現,需要濾去負值
    同時也不希望負值直接被處理為常數導致圖案無變化,使用了 abs()
    ?

    3.呼吸效果沿x軸擴散

    由于制作模型時,將模型的中心點設置在了正中心(默認是底部中心),可以直接取模型空間的x坐標來確定頂點與中心軸的距離

    fixed4 frag (v2f i) : SV_Target {// 蜂窩圖案fixed4 pulseTex = tex2D(_PulseTex, i.uv);fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;float breath = _Time.y * _PulseTimeScale;float pulseOffset = pulseTex.r * _PulseTexOffsetScale;float horizontalDist = abs(i.posOS.x);float xOffset = horizontalDist * _PulsePosScale;// 沿x軸擴散的隨機呼吸效果pulseTerm *= abs(sin(breath + pulseOffset - xOffset));return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a); }

    ?

    電流脈沖

    目標效果:蜂窩電流從護盾中心按菱形擴散
    大體上思路和蜂窩搏動效果有很多相似的地方

    效果拆分:

  • 蜂窩電流 + 整體呼吸效果
  • 蜂窩電流以菱形從護盾中心擴散
  • 1.蜂窩電流 + 整體呼吸效果

    fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動// ...// 蜂窩電流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;hexEdgeTerm *= saturate(sin(edgeBreath));// 只看電流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a); }

    1.5呼吸周期調整

    現在的呼吸周期使用的是 saturate(sin(x)) 函數,呼吸效果如下圖:

    圖中當曲線位于 x 軸上方時,電流才會出現,到達 1 時達到最亮,隨后變暗直到消失,開始下一輪循環

    由于現在的曲線是一個單純的 sin 曲線,電流亮和不亮的時間是相同的

    將曲線向下平移,就會讓亮的時間變短,暗的時間變長

    可以將這個下移量設置為變量 _HexEdgeWidthModifier,方便調試

    fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動// ...// 蜂窩電流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;hexEdgeTerm *= saturate(sin(edgeBreath) - _HexEdgeWidthModifier);// 只看電流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a); }

    但是根據實現效果看,電流只是淡淡的閃一下就滅了,效果非常不明顯

    這是因為在 x 軸上方的部分因為被下移,而無法到達 1 值,導致了電流顏色一直無法到達飽和的效果

    解決方法是把它拉長到 1:
    (1 - _HexEdgeWidthModifier)/ edgeNormalizer = 1,
    edgeNormalizer = 1 - _HexEdgeWidthModifier

    fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動// ...// 蜂窩電流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;// 周期調整float edgeNormalizer = 1 - _HexEdgeWidthModifier;hexEdgeTerm *= saturate(sin(edgeBreath) - _HexEdgeWidthModifier) / edgeNormalizer;// 只看電流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a); }

    ?

    2.菱形光從護盾中心擴散

    在蜂窩搏動效果中,使用的是 sin 沿 x 軸擴散
    這里要使用一個菱形擴散(“ 菱形 ” 雖然不準確,方便起見先這么叫)

    由于 x 對應的 y 不唯一,這個菱形無法使用函數表示

    但是,它可以表示為這個式子:abs(y) + abs(x) = 1,等式右邊的數字越大,菱形就越大

    這意味著如果計算 sin(abs(x)+abs(y)),就可以構造一個邊長不斷在一定區間變化的菱形的圖案,只要加入時間參數就可以讓它動起來

    補充
    上面的加粗字是這篇筆記全文唯一一個完完全全的翻譯,沒有一點自己的理解
    因為坦白說,這句話壓根就沒理解…
    想了好久也想不明白這到底是什么從 0 突變到 2048 的因果聯系啊…???
    ?
    Desmos 沒辦法表示二維圖像,好在還有Gooogle計算器:

    有了圖果然就好理解多了,這波啊,絕對是先有圖,后有粗體字

    fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動// ...// 蜂窩電流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;float verticalDist = abs(i.posOS.z);// 周期調整float edgeNormalizer = 1 - _HexEdgeWidthModifier;// 菱形float diamondPattern = (horizontalDist + verticalDist) * _HexEdgePosScale;hexEdgeTerm *= saturate(sin(diamondPattern - edgeBreath) - _HexEdgeWidthModifier) / edgeNormalizer;// 只看電流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a); }

    這里的verticalDist取的是z方向的值,因為在這個模型的坐標系中,上方向是z

    把最后的return修改一下,電流這里也算是完成了
    ?

    輪廓線

    目標效果:越邊緣顏色越實
    這個效果相對于前面的兩個很好實現,只要使用漸變邊緣的遮罩紋理,再進行一些調整就可以了

    fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動、蜂窩電流// ...fixed4 edgeTex = tex2D(_EdgeTex, i.uv);fixed4 edgeTerm = edgeTex.a * _Color * _EdgeIntensity;// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb + edgeTerm, _Color.a);return fixed4(edgeTerm.rgb, _Color.a); }


    大粗邊緣太傻太悶了,要想辦法把邊緣變薄
    如果不想改貼圖的話,就需要進行一些數學計算,比如pow()

    fixed4 edgeTerm = pow(edgeTex.a, _EdgeExponent) * _Color * _EdgeIntensity;


    需要注意的是,因為顏色的值域是[0, 1],_EdgeExponent越大意味著邊緣遮罩將更快的達到1并保持恒定,導致邊緣看起來缺乏過渡
    ?

    相交邊緣

    目標效果:與其他物體相交處有高亮

    雖然把固定邊緣和相交邊緣分成了不同的效果制作,為了讓最終的效果不突兀,還是需要保證固定邊緣和相交邊緣看起來就像是護盾的連續邊界一樣

    把它們分成兩部制作,只是因為兩個邊緣的形成原因不一樣,需要使用不同的方法來判定邊緣:
    一個是護盾原有的邊緣,固定存在(邊緣紋理);一個是護盾與其他物體相交形成的邊緣,需要根據相交情況進行判斷(深度紋理
    ?

    原理分析

    為什么判斷相交情況需要深度紋理呢?

    首先,當兩個物體相交時,它們之間的距離會變成0
    而深度值指的是點到相機(屏幕)的距離
    那么只要護盾上某點的深度值和環境中其他物體上的點的深度值差值足夠小,就會表明這兩個點足夠接近,足以產生相交線

    護盾的深度值可以在相機空間計算獲得,環境的深度值可以通過采樣相機渲染的深度紋理獲得

    補充
    根據之前學過的陰影章節可以知道,如果 Shader (和它的 FallBack)中沒有 ShadowCaster Pass,它所在的物體就不會出現在深度紋理中
    本例的 Shader 中不包含 默認的 ShadowCaster Pass 和 FallBack,所以深度紋理中沒有存儲護盾的深度值

    ?

    攝像機深度紋理獲取以及采樣

    想要獲得相機的深度紋理,需要在相機上掛一個腳本,確保它渲染深度紋理:

    void OnEnable() {GetComponent<Camera>().depthTextureMode = DepthTextureMode.DepthNormals; }

    如果設置正確,在相機組件的Inspector面板上,會出現下面的提示:

    然后就可以在Shader中使用這個紋理了

    sampler2D _CameraDepthNormalsTexture; // 必須使用這個名字

    這里需要注意的是,此時相機渲染出來的深度紋理是在屏幕空間中得到的,因此也需要在屏幕空間進行采樣

    o.posSS = ComputeScreenPos(o.pos);// UnityCG.cginc中的ComputeScreenPos大意 inline float4 ComputeScreenPos (float4 pos) {float4 o = pos * 0.5f;o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;o.zw = pos.zw;return o; }

    其中 _ProjectionParams.x 取值為 1.0 或 -1.0,取決于當前平臺的屏幕坐標系的 y 軸是向上還是向下,其他的具體原理可以見之前的計算機圖形學筆記,齊次裁剪空間到屏幕空間的變換
    ?

    護盾深度值計算

    想要計算深度值,必須先計算相機空間的頂點位置,它的 z 值就是該點的深度

    因為在片元著色器中進行比較時,兩個深度值都為正更方便,所以把這個深度值乘以 -1(相機空間中 z 的反方向指向相機的朝向,在相機面前的點的 z 都為負數)

    o.depth = -mul(UNITY_MATRIX_MV, v.vertex).z;

    現在,這個深度值是頂點到相機的實際距離
    但是深度紋理存儲的值是顏色,值域是 [0 ,1](在近平面上為 0,在遠平面上為 1)
    所以為了能正確比較兩個深度值,將頂點深度值重映射到 [0,1],需要除以相機到遠平面的距離(_ProjectionParams.w = 1 / FarPlane)

    o.depth = -mul(UNITY_MATRIX_MV, v.vertex).z * _ProjectionParams.w;

    補充
    其實這里數學上并不嚴謹,頂點到相機的實際距離值域應該是 [near,far],重映射的結果應該是 (z - near) / (far - near),而不是簡單的 z / far
    但是由于本例中相機的近平面為 0.1,所以在這里沒有太大影響,不如簡化計算直接取 z / far

    ?

    相交效果制作

    將上面兩步得到的屏幕空間坐標、護盾深度值傳遞到 Fragment Shader,然后就開始制作效果

    首先是求深度紋理中環境深度值,和護盾深度值的差值

    float diff = tex2D(_CameraDepthNormalsTexture, i.posSS.xy).r - i.depth;

    然而事情并沒有這么簡單,還有兩個問題要處理:

  • 因為使用了透視投影,需要做一個透視除法來減小透視帶來的歪斜效果
  • 深度值并不是簡單的存儲在_CameraDepthNormalsTexture紋理的一個通道中,而是存儲在 zw 兩個通道中(可以提升精度)
  • 解決方法倒是很簡單:

    // DecodeFloatRG為UnityCG.cginc中自帶的輔助函數 float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) - i.depth;

    補充
    注意 diff 的值是非負的
    雖然兩個距離做差應該有三種情況(>、<、= 0),但被減項是 “ 場景中不透明物體的深度值 ”,意味著當 diff < 0 時(即護盾深度值較大),護盾本身被不透明物體遮擋,不會被渲染;只有在 diff > 0 時(即護盾深度值較小),護盾才會被渲染。因此,從渲染結果的角度看,diff ≥ 0,是非負的
    ?
    關于透視除法,可以復習之前的計算機圖形學筆記,或者畫個側面的視錐體切一切

    理論上講,現在已經得到了環境深度值和護盾深度值的非負差值(值域 [0,1]),只要取 1 - diff,就可以得到一個相交邊緣的遮罩

    fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動、電流脈沖、固定邊緣// ...float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) - i.depth;float intersectGradient = 1 - diff;return intersectGradient; }

    但是事情好像沒有這么簡單:

    這是因為 diff 的值太小了,沒能將非相交邊緣的部分減到 0,可以通過乘上一個常量來增大這個值,比如 20

    diff *= 20; float intersectGradient = 1 - min(diff, 1.0);

    這里將 intersectGradient 的值改成了 1 - min(diff, 1.0);,是因為1 - diff; 無法保證 diff 乘了 20 后 intersectGradient 依舊 > 0

    雖然負值在普通 Shader 中會顯示為黑色,但是在修改過 BlendMode 的 Shader 中,負值顏色依然可以顯示,因此必須防止負值出現

    修改后問題看起來解決了

    但是這個方法并不通用,因為當改變相機的遠平面時,這個遮罩也會跟著改變

    這其實是因為在一開始計算護盾深度值的時候,做了一個 “ 除以相機到遠平面的距離 ” 的操作

    當時這么做的理由是,z 值是距離,紋理采樣是顏色,值域不同
    既然現在發現,統一成顏色值域的話會出問題,那干脆就都整成距離吧…

    所以解決方法是給差值 “ 乘以相機到遠平面的距離 ”

    fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動、電流脈沖、固定邊緣// ...float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) - i.depth;diff *= _ProjectionParams.z;float intersectGradient = 1 - min(diff, 1.0);return intersectGradient; }

    這下就完全不受相機設置的影響了,因為它就是一個絕對的距離,不是一個基于遠平面參數的 Range(0, 1)

    推薦直接從前面就改成使用距離計算,可以節省一點計算量…
    前面的筆記就不改了,反正栽坑爬坑也是思考的一部分…

    v2f vert (appdata v) {// ...// 距離o.depth = - mul(UNITY_MATRIX_MV, v.vertex).z;return o; }fixed4 frag (v2f i) : SV_Target {// 蜂窩搏動、蜂窩電流、固定邊緣// ...// 距離差float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) * _ProjectionParams.z - i.depth;float intersectGradient = 1 - min(diff, 1.0f);fixed4 intersectTerm = _Color * pow(intersectGradient, _IntersectExponent) * _IntersectIntensity;return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb + edgeTerm + intersectTerm, _Color.a); }

    雖然想要兩種邊緣無縫連接,計算相交線的一些參數(指數、強度)還是設置成了與固定邊緣不一樣的變量參數

    圖中護盾材質的固定邊緣和相交邊緣的強度、指數使用的是同樣的值
    ?

    自發光

    目標效果:護盾可以微微照亮周邊

    因為護盾的自發光是整體效果,可以把它交給后處理

    給相機添加 Post Process Layer 和 Post Process Volume
    在 Post Process Layer 中,選擇 Default Layer,反走樣選擇 FXAA

    補充
    正常情況下應該對需要進行不同后處理的物體在 Inspector 設置 Layer 進行分層,但是這里只是為了實現一個效果,而且還是整體的效果,所以可以忽略

    在 Post Process Volume 中,勾選 is Global,創建一個新的 Profile,添加Bloom效果,調節 Bloom 強度

    ?

    優化

    將使用同一套UV的多張灰度紋理存儲進一張紋理,通過.r、.g、b、.a使用對應通道的紋理
    一張紋理有RGBA四個通道,所以一張紋理最多可以存4張灰度圖
    R:蜂窩邊緣,G:蜂窩紋理,B:護盾邊緣

    這是一個很常見的優化手段,有很多優點:

  • 節省項目空間:多張圖存成了1張
  • 節省運行內存,提升加載速度:只需要讀取并存儲1張紋理在內存中
  • 對于多張圖只需要進行一次紋理采樣、一次 TRANSFORM_TEX 操作,減小時間空間消耗,簡化代碼
  • 簡單來說就是節省空間,提升速度,簡化操作
    ?

    總結

    完整 Shader

    Shader "Lexdev/CaseStudies/OverwatchShield" {Properties{_HexTex("R:HexEdge G:HexPulse B:Edge", 2D) = "white" {}_Color ("Color", Color) = (1,1,1,1)[Header(Hex Pulse)] _PulseIntensity ("Hex Pulse Intensity", float) = 3.0_PulseTimeScale("Hex Pulse Time Scale", float) = 2.0_PulsePosScale("Hex Pulse Position Scale", float) = 50.0_PulseTexOffsetScale("Hex Pulse Texture Offset Scale", float) = 1.5[Header(Electronic Pulse)]_HexEdgeColor("Hex Edge Color", COLOR) = (0,0,0,0)_HexEdgeIntensity("Hex Edge Intensity", float) = 2.0_HexEdgeTimeScale("Hex Edge Time Scale", float) = 2.0_HexEdgeWidthModifier("Hex Edge Width Modifier", Range(0,1)) = 0.8_HexEdgePosScale("Hex Edge Position Scale", float) = 80.0[Header(Edge)]_EdgeIntensity("Edge Intensity", float) = 10.0_EdgeExponent("Edge Falloff Exponent", float) = 6.0_IntersectIntensity("Intersection Intensity", float) = 10.0_IntersectExponent("Intersection Falloff Exponent", float) = 6.0}SubShader{Pass{Tags {"RenderType" = "Transparent" "Queue" = "Transparent"}Cull offBlend SrcAlpha oneHLSLPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _HexTex; float4 _HexTex_ST;float4 _Color;float _PulseIntensity;float _PulseTimeScale;float _PulsePosScale;float _PulseTexOffsetScale;float4 _HexEdgeColor;float _HexEdgeIntensity;float _HexEdgeTimeScale;float _HexEdgeWidthModifier;float _HexEdgePosScale;float _EdgeIntensity;float _EdgeExponent;// Camera rendered depth texture, must use the namesampler2D _CameraDepthNormalsTexture;float _IntersectIntensity;float _IntersectExponent;struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float4 posOS : TEXCOORD1;float4 posSS : TEXCOORD2;float depth : TEXCOORD3;};v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _HexTex);o.posOS = v.vertex;o.posSS = ComputeScreenPos(o.pos);// Z-Distanceo.depth = - mul(UNITY_MATRIX_MV, v.vertex).z;return o;}fixed4 frag (v2f i) : SV_Target{fixed4 hexTex = tex2D(_HexTex, i.uv);fixed pulseTex = hexTex.g;fixed hexEdgeTex = hexTex.r;fixed edgeTex = hexTex.b;// Hex pulsefixed3 pulseTerm = pulseTex * _Color * _PulseIntensity;float breath = _Time.y * _PulseTimeScale;float pulseOffset = pulseTex * _PulseTexOffsetScale;float horizontalDist = abs(i.posOS.x);float xOffset = horizontalDist * _PulsePosScale;pulseTerm *= abs(sin(breath + pulseOffset - xOffset));// Electronic pulsefixed3 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;float edgeBreath = _Time.y * _HexEdgeTimeScale;float edgeNormalizer = 1 - _HexEdgeWidthModifier;float verticalDist = abs(i.posOS.z); // Z-Up in Object-Spacefloat diamondPattern = (horizontalDist + verticalDist) * _HexEdgePosScale;hexEdgeTerm *= saturate(sin(diamondPattern - edgeBreath) - _HexEdgeWidthModifier) / edgeNormalizer;// Z-Distancefloat diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) * _ProjectionParams.z - i.depth;float intersectGradient = 1 - min(diff, 1.0f);// Edge & Intersectionfixed3 edgeTerm = pow(edgeTex, _EdgeExponent) * _Color * _EdgeIntensity;fixed3 intersectTerm = pow(intersectGradient, _IntersectExponent) * _Color * _IntersectIntensity;return fixed4(_Color.rgb + pulseTerm + hexEdgeTerm + edgeTerm + intersectTerm, _Color.a);}ENDHLSL}} }

    基本思路總結
    總的來說效果可以分為兩大部分:圖案和邊緣

    圖案部分可以拆解為沿 x 軸擴散的蜂窩圖案,和以菱形向外擴大的電流
    邊緣部分可以拆分為護盾的固有邊緣,和護盾與其他物體相交產生的邊緣

    圖案部分的計算是先用顏色紋理強度因子得到基礎圖形,再通過對時間進行數學運算獲得圖形遮罩,二者相乘得到動畫
    邊緣部分的計算是采樣紋理獲得遮罩,再通過對顏色進行數學計算,二者相乘得到邊緣

    ?
    雜七雜八總結
    平移 sin 函數可以打算 > 0 和 < 0 的長度平衡;可能需要對平移后的值做矯正
    abs(x) + abs(y) = n(常)是中心在原點的斜放正方形
    sin(abs(x) + abs(y)) 是s&fja@inc*ud^ishd(#svd%abaabaaba
    有奇奇怪怪的非二維函數可以扔到Google里看圖像
    采樣相機渲染的深度圖時,需要先做一個透視除法矯正透視導致的歪斜

    ?

    一點題外話
    感覺撿到寶了,這個博主真的好良心啊
    博客里一共有5個教程,目前算是跟著學完了其中兩個最感興趣的效果(Gears Hammer of Dawn、Overwatch Shield),受益良多,慢慢的開始明白拆解思路、參數設置思路了

    雖然依然是數學渣一個,但是還是很開心終于開始慢慢體會到數學的神奇了

    哎…失去了 gif 感覺放圖的樂趣都沒了QWQ

    總結

    以上是生活随笔為你收集整理的【ShaderLab实例笔记】Overwatch Shield - 守望先锋护盾特效制作笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。