问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Unity Shader实现消融效果

创作时间:
作者:
@小白创作中心

Unity Shader实现消融效果

引用
CSDN
1.
https://m.blog.csdn.net/Go_Accepted/article/details/145358557

消融效果是一种常见的视觉特效,常用于游戏开发中,如角色死亡、传送场景、魔法消失等。本文将详细介绍如何使用Unity Shader实现这种效果,包括基本原理和具体实现方法。

消融效果是模拟物体逐渐从屏幕上消失或溶解的过程,它通常利用噪声纹理实现,使物体按照某种规则逐渐透明或完全不可见。这种效果常用于: 角色死亡、传送场景、 魔法消失,比如燃烧、消失等

1、基本原理

通过对比噪声纹理值与消融进度参数,剔除低于阈值的像素,同时在边缘添加渐变颜色实现动态溶解效果。

关键点:

  • 如何剔除像素

在片元着色器中对噪声纹理进行采样,由于噪声纹理是灰度图,只需取出其中的RGB中的任意一通道的颜色来使用。再**自定义一个用于控制消融进度的参数(0~1),**最后利用该片元的 噪声纹理值 – 进度参数,若小于0则不渲染该片元,通过控制进度参数,便可以控制消融程度了

  • 如何处理边缘

在处理边缘渐变颜色效果时,将使用Unity中内置的三个Shader函数:

smoothstep(a, b, x)

a起始值;b结束值;x输入值(用于在a和b之间平滑插值)

当x<a时,返回0;当x>b时,返回1;

a < x < b时,返回0~1之间的值

lerp(a,b,t)

a起始值;b结束值;t插值因子

当t=0时,返回a;当t=1时,返回b;当 0 < t < 1时;返回a和b之间的值

step(value, x)

value阈值,x输入值;两值用于比较

x < value,返回0;x>=value,返回1

首先我们利用smoothstep函数决定边缘颜色,利用噪声颜色值 – 消融进度值 得到一个剔除阈值value,然后自定义一个边缘范围值_Range,然后用smoothstep函数来得到一个值

接着在原本的颜色和渐变颜色之间进行插值,决定使用哪个颜色。

利用smoothstep结合消融阈值来决定在渐变纹理中采用的渐变颜色,利用lerp来决定在原始颜色和边缘渐变颜色中使用哪个颜色,用自定义参数来决定边缘范围,从而实现消融边缘渐变色

2、实现

Shader "ShaderProj/21/Dissolve"
{
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
        _MainColor ("MainColor", Color) = (1,1,1,1)
        _BumpMap("BumpMap", 2D) = ""{}
        _BumpScale("BumpScale", Range(0, 1)) = 1
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularNum("SpecularNum", Range(8, 256)) = 18
        _Noise ("Noise", 2D) = ""{}
        _Gradient ("Gradient", 2D) = ""{}
        _Dissolve ("Dissolve", Range(0, 1)) = 0
        _EdgeRange ("EdgeRange", Range(0, 1)) = 0
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            struct v2f
            {
                float4 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
                float3 lightDir : TEXCOORD1;
                float3 viewDir : TEXCOORD2;
                float2 uvNoise : TEXCOORD3;
                float3 worldPos : TEXCOORD4;
                SHADOW_COORDS(5)
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _MainColor;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
            float _BumpScale;
            fixed4 _SpecularColor;
            fixed _SpecularNum;
            sampler2D _Noise;
            float4 _Noise_ST;
            sampler2D _Gradient;
            fixed _Dissolve;
            fixed _EdgeRange;
            v2f vert (appdata_full v)
            {
                v2f data;
                data.pos = UnityObjectToClipPos(v.vertex);
                data.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
                data.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
                data.uvNoise = TRANSFORM_TEX(v.texcoord, _Noise);
                float3 binormal = cross(normalize(v.tangent), normalize(v.normal)) * v.tangent.w;
                float3x3 rotation = float3x3(v.tangent.xyz,
                                             binormal,
                                             v.normal);
                data.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                data.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
                data.worldPos = mul(unity_ObjectToWorld, v.vertex);
                TRANSFER_SHADOW(data);
                return data;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                // 剔除
                fixed3 noiseColor = tex2D(_Noise, i.uvNoise);
                clip(_Dissolve == 1 ? -1 : noiseColor - _Dissolve);
                float4 packedNormal = tex2D(_BumpMap, i.uv.zw);
                float3 tangentNormal = UnpackNormal(packedNormal);
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
                fixed3 albedo = tex2D(_MainTex, i.uv.xy) * _MainColor.rgb;
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(tangentNormal, normalize(i.lightDir)));
                float3 halfA = normalize(normalize(i.viewDir) + normalize(i.lightDir));
                fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tangentNormal, normalize(i.viewDir))), _SpecularNum);
                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor * atten + specularColor;
                // 渐变颜色
                fixed t = 1 - smoothstep(0, _EdgeRange, noiseColor - _Dissolve);
                fixed3 gradient = tex2D(_Gradient, fixed2(t, 0));
                fixed3 finalColor = lerp(color, gradient, t * step(0.00001, _Dissolve));
                return fixed4(finalColor, 1);
            }
            ENDCG
        }
        Pass{
            Tags{"LightMode" = "ShadowCaster"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"
            struct v2f
            {
                V2F_SHADOW_CASTER;
                fixed2 uvNoise : TEXCOORD0;
            };
            sampler2D _Noise;
            float4 _Noise_ST;
            float _Dissolve;
            v2f vert(appdata_base v)
            {
                v2f data;
                data.uvNoise = TRANSFORM_TEX(v.texcoord, _Noise); 
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(data);
                return data;
            }
            fixed4 frag(v2f i) : SV_Target
            {
                // 剔除影子
                fixed3 noiseColor = tex2D(_Noise, i.uvNoise);
                clip(_Dissolve == 1 ? -1 : noiseColor - _Dissolve);
                SHADOW_CASTER_FRAGMENT(i);
            }
            ENDCG
        }
    }
}

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号