制作一个状态栏中跑马灯效果_利用噪音图制作一个纸张燃烧的效果

ce52a3a3-f020-eb11-8da9-e4434bdf6706.png

写在前面

前几天有人在群里问怎么制作一个纸张被火烧掉的效果,刚好那天没那么忙,就尝试实现了一个,效果如下:

d052a3a3-f020-eb11-8da9-e4434bdf6706.png

这个shader并不复杂,主要是对噪音图的使用,趁这次机会和大家分享一下

基础材质

纸张本身不是这次效果的重点,所以简单的采样一张贴图即可,下面是随便找了张图贴上去

fixed4 col = tex2D(_MainTex, i.uv.xy);

d152a3a3-f020-eb11-8da9-e4434bdf6706.png

我们还需要一张燃烧过后的图,把上面那张图扔PS里去个色,然后加点污渍:

fixed4 colAsh = tex2D(_AshTex, i.uv.xy);

d452a3a3-f020-eb11-8da9-e4434bdf6706.png

余烬效果

燃烧过会还会留下一下余烬,我们可以利用噪音图来实现,通过采样两次噪音图进行叠加来让噪音更随机一些,让其中一次稍微放大一些防止完全重叠的情况。

这里用到一张网上随便找的噪音图(注意商用的话可能会有侵权风险)

d652a3a3-f020-eb11-8da9-e4434bdf6706.png

vertex:

o.uv2 = TRANSFORM_TEX(v.uv, _NoiseMap).xyxy * half4(1,1,1.3,1.3) + _FlowVector * _Time.x;

fragment:

fixed noise =(tex2D(_NoiseMap2 , i.uv2.xy) + tex2D(_NoiseMap2, i.uv2.zw)) * 0.5;


效果如下

db52a3a3-f020-eb11-8da9-e4434bdf6706.png

当然你也可以试试别的叠加算法,比如这样:

fixed noise =abs( (tex2D(_NoiseMap2 , i.uv2.xy) + tex2D(_NoiseMap2, i.uv2.zw)) - 1);

我们会得到一个这样的效果

dd52a3a3-f020-eb11-8da9-e4434bdf6706.png

提取其中一部分区域作为遮罩,叠加上火焰的颜色:

fixed3 spark =(smoothstep(0.8,1, noise)) * _SparkColor;

de52a3a3-f020-eb11-8da9-e4434bdf6706.png

就能得到这样的效果了。

材质混合

这里图省事我们就直接使用uv的x方向来进行混合,因为这里的过渡范围限定为0到1,所以需要把混合因子映射到-1到2才能保证完全覆盖,效果如下:

_Blend = _Blend * 3 - 1;
half blendValue = smoothstep(_Blend-_Range, _Blend+_Range, i.uv.x) ;
col.rgb = lerp(colAsh + spark, col ,  blendValue) + burnRange;

e052a3a3-f020-eb11-8da9-e4434bdf6706.png

现在的混合边缘太生硬,使用上面的噪音图对混合的边缘进行扰动,稍微调整下代码:

half blendValue = smoothstep(_Blend-_Range, _Blend+_Range, i.uv.x + noise * _Range) ;

e152a3a3-f020-eb11-8da9-e4434bdf6706.png

火焰

从上面的的混合权重里把边缘提取出来,因为噪音的影响,我们就可以得到一个近似火焰的遮罩区域:

 float3 burnRange = blendValue * (1-blendValue) * _RangeColor;
col.rgb = lerp(colAsh + spark, col ,  blendValue) + burnRange;

e252a3a3-f020-eb11-8da9-e4434bdf6706.png

现在火焰占满了整个过渡区域,我觉得有点太过了,所以调整了下,把火焰限定在更小的范围,并且加入了偏移值和火焰宽度值进行微调:

float3 burnRange = max(0 , 1 - abs(blendValue - (_FireOffset *(1-_FireRange*2)+ _FireRange)) /_FireRange) * _RangeColor;

e452a3a3-f020-eb11-8da9-e4434bdf6706.png

写这类效果的时候我一般喜欢画图来辅助计算,比较推荐一个网站:

Graphing Calculator - GeoGebra​www.geogebra.org

上面的那个算法图像是这样子的:

e552a3a3-f020-eb11-8da9-e4434bdf6706.png
https://www.zhihu.com/video/1225579900545224704

消散效果

消散效果应该很多人都写过,一个简单的clip即可,因为我们需要按照燃烧>灰烬>消散这个过程渐变,所以消散效果要在前面的混合权重基础上加一点偏移:

clip(col.a * (i.uv.x+ noise * _Range)  -  (_Blend  -  _Range -_EmberRange)) ;

e652a3a3-f020-eb11-8da9-e4434bdf6706.png

顶点扰动

纸张燃烧时候还会因为热量产生一点轻微的变形,我们利用顶点动画来模拟这个效果,注意如果要做顶点动画的话,顶点数量不能太少,这里的模型使用的是一个30*30的网格

float4 noiseuv = float4(v.uv * _NoiseMap2_ST.xy + _NoiseMap2_ST.zw * _Time.x, 0,0) ;
fixed noise = tex2Dlod(_NoiseMap2 , noiseuv) ;
_Blend = _Blend * 4 - 1;
half vertOffset =  noise *  _VertOffset * saturate (1 - (o.uv.x*4  -_Blend));

o.vertex = UnityObjectToClipPos(v.vertex + half4(0,0,vertOffset ,0));

e752a3a3-f020-eb11-8da9-e4434bdf6706.png

这样我们的效果就全部完成了,看上去还不错, 再加一点粒子应该会很不错。

完整代码

/*******************************************************
*   2020-03-18 16:38:42
*   @Mya
*   模拟纸张燃烧的shader
********************************************************/
Shader "Mya/Effect/BurningPaper"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _AshTex ("Ash Texture", 2D) = "white" {}
        _NoiseMap("Noise" , 2D) = "black"{}
        
        
        _Blend("Blend" , Range(0,1)) = 0
         [hdr]_RangeColor("Range Color" , Color) = (1,0,0,1)
        _Range("Range" , Range(0.01,0.5)) = 0.1
        _FireRange("Fire Range" , Range(0,0.5)) = 0.2
        _FireOffset("Fire Offset" , Range(0,1)) = 0
        [hdr]_SparkColor("spark Color" , Color) = (1,0,0,1)
        _AshRange("EmberRange" , Range(0,1)) = 0.1
        _FlowVector("Flow Vector" , vector) = (0,0,0,0)

        _NoiseMap2("Noise2" , 2D) = "black"{}
        _VertOffset("Vert Offset" , Range(0,1))    = 0.1
        
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha
        Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv       : TEXCOORD0;
                float4 uv2      : TEXCOORD1;
                float4 vertex   : SV_POSITION;
            };

            sampler2D   _MainTex;
            float4      _MainTex_ST;
            sampler2D   _AshTex;
            
            sampler2D   _NoiseMap; 
            float4      _NoiseMap_ST ;
            sampler2D   _NoiseMap2;
            float4      _NoiseMap2_ST ;

            half        _Blend;
            half        _Range;
            fixed4      _RangeColor;
            fixed4      _SparkColor;
            half        _AshRange;

            half        _FireRange;
            half        _FireOffset;

            half4       _FlowVector;
            half        _VertOffset;
            v2f vert (appdata v)
            {
                v2f o;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                //通过采样两次噪音图进行叠加来让噪音更随机一些,让其中一次稍微放大一些防止完全重叠的情况
                o.uv2 = TRANSFORM_TEX(v.uv, _NoiseMap).xyxy * half4(1,1,1.3,1.3) + _FlowVector * _Time.x;

                //顶点里采样贴图需要使用tex2Dlod
                float4 noiseuv = float4(v.uv * _NoiseMap2_ST.xy + _NoiseMap2_ST.zw * _Time.x, 0,0) ;
                fixed noise = tex2Dlod(_NoiseMap2 , noiseuv) ;

                _Blend = _Blend * 4 - 1;
                //对燃烧区域的顶点做一点偏移,模拟飘动的效果
                half vertOffset =  noise *  _VertOffset * saturate (1 - (o.uv.x*4  -_Blend));

                o.vertex = UnityObjectToClipPos(v.vertex + half4(0,0,vertOffset ,0));

                return o;
            }


            fixed4 frag (v2f i) : SV_Target
            {
                fixed noise = (tex2D(_NoiseMap , i.uv2.xy) + tex2D(_NoiseMap2, i.uv2.zw))*0.5;
                
                //基于uv的x方向计算混合的权重,边缘使用噪音进行扰动
                //要保证四个状态(原始,燃烧,灰烬,消散)都能完整显示,需要把混合因子映射到-1~3
                _Blend = _Blend * 4 - 1;
                half blendValue = smoothstep(_Blend-_Range, _Blend+_Range, i.uv.x + noise * _Range) ;
                
                //原始的颜色
                fixed4 col = tex2D(_MainTex, i.uv.xy);
                //燃烧后的颜色
                fixed4 colAsh = tex2D(_AshTex, i.uv.xy);
   
                //余烬
                fixed3 spark =(smoothstep(0.8,1, noise)) * _SparkColor;

                //火焰
                float3 burnRange = max(0 , 1 - abs(blendValue - (_FireOffset *(1-_FireRange*2)+ _FireRange)) /_FireRange) * _RangeColor;

                //消散
                clip(col.a * (i.uv.x+ noise * _Range)  -  (_Blend  -  _Range -_AshRange)) ;

                //混合
                col.rgb = lerp(colAsh + spark, col ,  blendValue) + burnRange;

                return col;
            }
            ENDCG
        }
    }
}

GitHub:

https://github.com/myacat/PaperBurn​github.com

写在最后

简单修改下还可以做出类似这样的效果,有兴趣的小伙伴可以自己尝试继续优化下:

e952a3a3-f020-eb11-8da9-e4434bdf6706.png

=====4.18更新====================

今天稍微调整了一下这个曲线的算法,让曲线更平滑一些

ea52a3a3-f020-eb11-8da9-e4434bdf6706.png
https://www.zhihu.com/video/1234725242905935872

eb52a3a3-f020-eb11-8da9-e4434bdf6706.png
左边是新的算法

拆成了独立的函数

            half Pow2(half x)
            {
                return x*x;
            }
            half ReMap(half range, half offset , half val)
            {
            #if SMOOTHRANGE
                //Range(10-0)
                range = 10*(1 -range);
                return Pow2(max(0, 1 - Pow2(range*val + 2*val - range*offset - 1)));
            #else
                //Range(0.5-0.1)
                range = 0.1 + range * 0.4;
                return max(0 , 1 - abs(val - (offset *(1-range*2)+ range)) /range) ;
            #endif
            }

因为两个算法range参数的值域是不一样的,所以重映射了一次。

ec52a3a3-f020-eb11-8da9-e4434bdf6706.png
左边是新的算法

然而改完后发现效果也并没有变好多少.....emmm算是提供另一个思路吧...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值