Shader学习笔记 01 - 溶解

一般3D溶解

边缘溶解

使用噪声贴图或者程序噪声,通过范围0-1的float材质参数在片元着色器进行 clip 操作。

float _DissolveDegree;  // 材质参数:溶解程度
// fbm 程序噪声生成
float test = fbm(i.uv) - _DissolveDegree;
clip(test);
边缘颜色

使用hdr、渐变纹理(RampTexture)、bloom提高效果。

image

程序噪声参考 link

示例源码:

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
    }
}

}

带状边缘

有些游戏的类型(eg:卡通)可能不需要平滑的溶解边缘,让边缘的颜色不是平滑渐进而是以类似{0,0.2,0.4,0.6...}阶梯式的渐进。

float _BandingSize; // 材质参数:带状大小,渐进值为 1 / _BandingSize,eg:_BandingSize = 5,则为{0,0.2,0.4,0.6...}
round(fbm(uv)*_BandingSize)/_BandingSize

image

像素溶解

使用uv生成噪声时,让小数位的精度丢失。

image

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

模型坐标与方向(vector材质参数,归一化)进行点乘,越偏离方向值越小。

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变化。

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
    }
}

}

传送溶解 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)之类的效果合并而成。最终效果依赖于贴图的选择制作。

posted @ 2020-05-25 09:55  Lain_vv  阅读(496)  评论(0编辑  收藏  举报