Shader學習筆記 01 - 溶解

通常3D溶解

邊緣溶解

使用噪聲貼圖或者程序噪聲,經過範圍0-1的float材質參數在片元着色器進行 clip 操做。算法

float _DissolveDegree;  // 材質參數:溶解程度
// fbm 程序噪聲生成
float test = fbm(i.uv) - _DissolveDegree;
clip(test);
邊緣顏色

使用hdr、漸變紋理(RampTexture)、bloom提升效果。app

image

程序噪聲參考 link動畫

示例源碼:


3d

Shader "DissolvePack/unlit/DissolveSh" { Properties { _MainTex ("Texture", 2D) = "white" {} _RampTexture ("Ramp Texture", 2D) = "white" {} _Color("Tint", Color) = (1,1,1,1) _Color2("Burn Color", Color) = (1,1,1,1) _DissolveDegree("Dissolve Degree", Range(0,1)) = 0 _DissolveOffset("Dissolve Offset", Range(0.0, 0.5)) = 0.15 } SubShader { Tags { "RenderType"="Opaque" } LOD 100
    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // make fog work
        #pragma multi_compile_fog

        #include "UnityCG.cginc"
        // 程序噪聲 https://thebookofshaders.com/13/?lan=ch
        #include "../Noise.cginc"



        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;
            UNITY_FOG_COORDS(1)
            float4 vertex : SV_POSITION;
        };

        sampler2D _MainTex,_RampTexture;
        float4 _MainTex_ST;
        float _DissolveDegree,_DissolveOffset;
        float4 _Color,_Color2;

        v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            UNITY_TRANSFER_FOG(o,o.vertex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            // sample the texture
            fixed4 color = tex2D(_MainTex, i.uv);
            fixed4 col = saturate(color*_Color);
            fixed4 burnColor = saturate(color*_Color2);
            float test = fbm(i.uv) - _DissolveDegree;
            clip(test);
            if(test < _DissolveOffset && _DissolveDegree > 0) {
                //col = burnColor;
                col = tex2D(_RampTexture, float2(test/_DissolveOffset,0));
            }

            // apply fog
            UNITY_APPLY_FOG(i.fogCoord, col);
            return col;
        }
        ENDCG
    }
}
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" // 程序噪聲 https://thebookofshaders.com/13/?lan=ch #include "../Noise.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex,_RampTexture; float4 _MainTex_ST; float _DissolveDegree,_DissolveOffset; float4 _Color,_Color2; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 color = tex2D(_MainTex, i.uv); fixed4 col = saturate(color*_Color); fixed4 burnColor = saturate(color*_Color2); float test = fbm(i.uv) - _DissolveDegree; clip(test); if(test < _DissolveOffset && _DissolveDegree > 0) { //col = burnColor; col = tex2D(_RampTexture, float2(test/_DissolveOffset,0)); } // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } }}

帶狀邊緣

有些遊戲的類型(eg:卡通)可能不須要平滑的溶解邊緣,讓邊緣的顏色不是平滑漸進而是以相似{0,0.2,0.4,0.6...}階梯式的漸進。code

float _BandingSize; // 材質參數:帶狀大小,漸進值爲 1 / _BandingSize,eg:_BandingSize = 5,則爲{0,0.2,0.4,0.6...}
round(fbm(uv)*_BandingSize)/_BandingSize

image

像素溶解

使用uv生成噪聲時,讓小數位的精度丟失。orm

image

float pixelSize = 10;
float noise = fbm(floor(uv*pixelSize));
方向溶解

模型座標與方向(vector材質參數,歸一化)進行點乘,越偏離方向值越小。blog

half test = (dot(objectPosition, normalize(_DissolveDir))+ 1) / 2 - _DissolveDegree;
clip(test);

image

反面

溶解3d模型時反面會被剔除(cull),關閉剔除時效果:遊戲

image

方面溶解使用VFACE自定義剔除面:圖片

frag(fixed facing:VFACE){
    if(facing < 0) 顏色賦值.
}

image

旋轉,縮放的影響

使用模型座標旋轉時方向會同時跟隨着變化,縮放會致使offset變化。ip

image

改用忽略掉位移的世界座標(即以模型座標原點和世界座標原點重合)即可以讓方向不受模型旋轉,縮放的影響。

vert {
    // 矩陣變換的第四列是位移變化,強轉爲float3x3便會忽略位移變換。
    float3 worldPos = mul((float3x3)unity_ObjectToWorld, vertex.xyz);
}

image

示例源碼:


Shader "Unlit/DirectionalDissolveSh" { Properties { _MainTex ("Texture", 2D) = "white" {} _RampTexture ("Ramp Texture", 2D) = "white" {} _DissolveDir("Dissolve Direction", vector) = (0,0,0,0) _DissolveDegree("Dissolve Degree", Range(0,1)) = 0 _DissolveOffset("Dissolve Offset", Range(0.0, 0.5)) = 0.15 _NoiseST("Noise Scale & Offset", vector) = (1,1,0,0) [HDR]_Tint("Tint", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100
    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // make fog work
        #pragma multi_compile_fog

        #include "UnityCG.cginc"
        // 程序噪聲 https://thebookofshaders.com/13/?lan=ch
        #include "../Noise.cginc"

        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;
            float3 worldPosAdj : TEXCOORD1;
            UNITY_FOG_COORDS(1)
            float4 vertex : SV_POSITION;
        };

        sampler2D _MainTex,_RampTexture;
        float4 _MainTex_ST;
        float4 _DissolveDir;
        float4 _Tint;
        float4 _NoiseST;
        float _DissolveDegree, _DissolveOffset;

        v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            o.worldPosAdj = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz);
            //o.worldPosAdj = v.vertex;
            UNITY_TRANSFER_FOG(o,o.vertex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            // sample the texture
            fixed4 col = tex2D(_MainTex, i.uv);
            // [-1,1] => [0,1]
            half test = (dot(i.worldPosAdj, normalize(_DissolveDir))+ 1) / 2 - _DissolveDegree;
            clip(test);

            float noise = fbm(i.uv*_NoiseST.xy+_NoiseST.zw);
            test = test*noise - 0.01;
            clip (test);
            float offset = _DissolveOffset*0.1;

            if(test < offset && _DissolveDegree > 0) {
                col = tex2D(_RampTexture, float2(test/offset,0)) * _Tint;
            }

            // apply fog
            UNITY_APPLY_FOG(i.fogCoord, col);
            return col;
        }
        ENDCG
    }
}
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" // 程序噪聲 https://thebookofshaders.com/13/?lan=ch #include "../Noise.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float3 worldPosAdj : TEXCOORD1; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex,_RampTexture; float4 _MainTex_ST; float4 _DissolveDir; float4 _Tint; float4 _NoiseST; float _DissolveDegree, _DissolveOffset; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldPosAdj = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz); //o.worldPosAdj = v.vertex; UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // [-1,1] => [0,1] half test = (dot(i.worldPosAdj, normalize(_DissolveDir))+ 1) / 2 - _DissolveDegree; clip(test); float noise = fbm(i.uv*_NoiseST.xy+_NoiseST.zw); test = test*noise - 0.01; clip (test); float offset = _DissolveOffset*0.1; if(test < offset && _DissolveDegree > 0) { col = tex2D(_RampTexture, float2(test/offset,0)) * _Tint; } // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } }}

傳送溶解 Teleport Dissolve

在溶解的過程加上形變即頂點移動。

vert {
    float3 worldPosAdj = mul(unity_ObjectToWorld, vertex.xyz);
    float posRatio = (dot(worldPosAdj, dir) + 1 ) / 2;
    vertex.xyz += dir * fbm(uv) * _DissolveDegree * posRatio * _ChangeSize;
}

image

2D Sprite 溶解 (無光照)

主要操做都在片元着色器裏;溶解圖片使用噪聲也可使用貼圖,貼圖能夠多種多樣,甚至是幀動畫圖片;因爲不參與光照計算,sprite的顏色變化須要手動處理,處理方法能夠是相似於數字圖像處理之類的方法,也能夠與擾動貼圖相關聯;Sprite的溶解一樣可使用適用於3d的溶解;因爲sprite有部分透明區域,渲染模式也是透明的,全部裁剪能夠經過a通道。

邊緣溶解
// 材質參數
float _DissolveDegree;  // 溶解程度
float _DissolveOffset; // 溶解邊緣寬度
float4 _NoiseST; // 噪聲縮放和平移
float4 _TargetColor; // 總體變化顏色

// 修改透明度,且讓邊緣顏色有漸進效果
float4 c = smoothstep(_DissolveDegree, _DissolveDegree + _DissolveOffset, fbm(uv * _NoiseST.xy + _NoiseST.zw));
color *= c;

// 總體顏色變化
color.rgb = lerp(color.rgb, color.rgb * (1 - color.a) + _TargetColor.rgb, _DissolveDegree);
// 相乘效果也還行
color.rgb = lerp(color.rgb, color.rgb * (1 - color.a) * _TargetColor.rgb * 20, _DissolveDegree);

image

溶解方向

使用uv參數,可配方向一樣使用dot。

float2 dir = normalize(float2(_DissolveDir.x,_DissolveDir.y));
float test = (dot(dir,uv - float2(0.5,0.5))+1)/2 - _DissolveDegree;

因爲uv是固定的,不像座標那麼不可控,能夠經過程序算法生成一些特殊的邊緣變化。

// eg
float offset = _DissolveDegree*sin(uv.x*5*3.14);
float test = (1 + offset)*(1-uv.y)/(_DissolveDegree + 0.01);

image

噪聲貼圖

一種是直接使用噪聲貼圖圖的像素值影響uv(distortion),讓主帖圖具備噪聲貼圖的形狀。而後sprite逐漸溶解(透明值、或使用噪聲、或者使用方向溶解都行)。

eg: 2dxfx上的blood的簡化版,只保留了扭曲(distortion)和方向溶解(directional dissolve)。
image

另外一種是使用噪聲貼圖的各個通道,每一個通道是不一樣的形狀,在不一樣時段影響主貼圖,而後合併在一塊兒。2dxfx上的那幾個teleportion基本上都是使用這種方法,再加上閃光(shiny)之類的效果合併而成。最終效果依賴於貼圖的選擇製做。

相關文章
相關標籤/搜索