有序的无序:unity shader噪声图以及消融效果的实现

先上一张效果图:


消融效果

这篇文章我准备写两部分:

  • 使用噪声图在unity shader里实现物体消融的效果
  • 消融是随机的,但每次过程又是固定的,这一种“有序的无序”模式的理解,这一点是我更想说的,消融效果是帮助解释这个概念。

有规律的随机

玩游戏的时候,有时会有火焰特效,有次我盯住一个火焰看,发现火焰是循环播放的,比如下面这张图。类似火焰这种东西,是变化无常的,一般就是随机来做,比如用unity自带的粒子系统来实现,那么火焰的形态是完全随机无序的,是我们无法控制的。但又是怎么让规定的样式进行晃动呢?这里就有一个冲突,就是火焰的形态的无序随机的,否则就会不像;然后火焰这种无序又是按照一种既定的规律重复的进行。

简单说,随机是13、313、54、114、521、14、41、4521、421这样任意的,足够时间,都是随机的;而有规律的随机是13、313、54、114、521、14、41、4521、421这样随机了一轮后,下一轮跟这个是相同的,这样重复。可以理解为套了两层,第一层重复,是有规律的,第二层随机。

这就是我的困惑,怎么构造这种“有序的无序” ?

重复的火焰特效

消融效果

先上代码,如果你不了解unity shader,也可以继续看看,我会把里面的逻辑用常人理解的方式理一遍。

Shader "Unlit/Dissolve"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _BurnMap ("Fire Map", 2D) = "white" {}
        _BurnSpeed ("Burn Speed", float) = 1.0
        _Specular ("Specular", range(0, 1)) = 0.5
        _Gloss ("Gloss", range(8, 256)) = 20
        //_BurnAmount ("BurnAmount", range(0,1)) = 0

        _BurnFirstColor ("Burn First Color", Color) = (1,0,0,1)
        _BurnSecondColor ("Burn Second Color", Color) = (0,0,0,1)
        _BurnRange ("Burn Range", float) = 0.1
    }
    SubShader
    {
        Tags { "LightMode"="ForwardBase" }


        Pass
        {
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"


            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _BurnMap;
            fixed _BurnSpeed;
            fixed _Gloss;
            float _Specular;

            fixed _BurnAmount;
            fixed4 _BurnFirstColor;
            fixed4 _BurnSecondColor;
            float _BurnRange;

            struct a2v{
                float4 vertex : POSITION;
                float4 normal : NORMAL;
                float4 uv : TEXCOORD0;
            };

            struct v2f{
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 worldPos : TEXCOORD1;
                float3 worldNormal : TEXCOORD2;
            };

            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));

                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                //burn map
                fixed3 burn = tex2D(_BurnMap, i.uv).rgb;
                _BurnAmount += _Time.y * _BurnSpeed;
                clip(burn.r - _BurnAmount);


                // sample the texture
                fixed3 albedo = tex2D(_MainTex, i.uv).rgb;

                //diffuse
                fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed3 diffuse = _LightColor0.rgb * saturate(dot(lightDir, i.worldNormal)) * albedo;
            
                //specular
                fixed3 reflectDir = normalize(reflect(-lightDir, i.worldNormal));
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 specular = _LightColor0.rgb * _Specular * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                fixed3 finalColor = specular + diffuse + ambient;

                //在消融的边缘位置,添加红色和黑色,模拟烧焦的效果。当前正在烧的边缘就是那些r - _BurnAmount刚好为0的位置。
                //float burnRate = 1 - saturate((burn.r - _BurnAmount) / _BurnRange);
                float burnRate = 1 - smoothstep(0, _BurnRange, burn.r - _BurnAmount);
                fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, burnRate);
                //burnColor = pow(burnColor, 5);
                finalColor = lerp(finalColor, burnColor, burnRate);

                return fixed4(finalColor, 1);
            }
            ENDCG
        }
    }
}
  • shader里有个函数clip可以丢弃像素,就是不渲染模型的这个点了。什么是物体的消融?不就是你本来看到的是物体A,然后变成了看到物体A后面的背景了。使用丢弃像素的操作,本来渲染物体A的,变成显示后面的东西了,消融效果就出来了。

  • 消融还有个特性,就是它像水滴在纸上面那样逐渐扩撒开来,而不是无规律的随意消融。那么问题就转移为:按照一个逐渐扩散的模式丢弃模型的像素。

  • 如果要像现实里一样,那么消融扩散的方向、大小、快慢都该是无规律任意的,这是这个问题里的无序。但是如果要用程序随机计算哪些地方消融,那么就是不断出现一个个小洞那样的效果,要模拟扩散消融,个人觉得那就太难了。而现在的做法就是使用纹理图

  • 比如我用相机给现实的火拍一张照,把这个作为模板。物体的表面有个uv坐标系,简单说就是3维物体表面跟2维的平面建立一个对应关系,那么物体表面任意一点,我都可以找到之前拍的火焰纹理图上一个对应点,然后根据这个点的颜色里的红色多少决定是否丢弃。

火焰纹理

主要代码就是这一段,其他只是实现光照和边缘烧焦效果。

fixed3 burn = tex2D(_BurnMap, i.uv).rgb;
_BurnAmount += _Time.y * _BurnSpeed;
clip(burn.r - _BurnAmount);

_BurnMap是火焰纹理图,tex2D(_BurnMap, i.uv)这个函数就是根据点的uv左边取到对应纹理图上那一点的颜色信息,clip(burn.r - _BurnAmount);如果这个一点的r(红色)通道值小于_BurnAmount,就剔除这个像素。因为_BurnAmount += _Time.y * _BurnSpeed;``_BurnAmount不断增大,那么就会有越来越多的像素被丢弃,最后整个物体都被丢弃,彻底消融。

因为纹理图本身就是扩散模式的,所以物体被剔除也是这个模式。

如果把物体表面的纹理也换成火焰纹理,就可以很明显的看到,从最红的位置开始消融,消融的位置跟纹理的颜色分布是完全一样的。

火焰纹理消融

纹理的力量

回到上面

怎么构造这种“有序的无序” ?

使用纹理。纹理上面的内容本身是混乱无序的,但是纹理图已经是一张固定的图,我们参照纹理来决定哪里消融,那纹理图就是有规律的了,是有序的。

在unity里,shader里是使用纹理,那么在其他地方呢?更抽象的说,就是使用一个模板,把无序记录下来,然后按照这个已经记录下来的模板来操作,这样就可以实现“有序的无序”

最后一个问题,这样做有什么好处?简便直接效率高。

资源

消融效果的材质包放在github这个库里,DissoveShader.unitypackage就是。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容