之前玩Tencent的仙剑4手游时,杀死boss会看到boss有“消融”的效果,就是身体上有多个洞洞然后往四周扩散直至尸体完全消失,但效果是没有关闭背面剔除的“穿帮”效果,可能也是考虑性能因素。 emmmm,那下面开始详细讲解消融等等噪声相关应用的实现~

  · 消融效果

  噪音,就是“沙沙渣渣渣”的杂乱无章的声音,在图形学中使用噪声是为了把一些随机变量来引入到程序中,如火焰、地形、云朵的模拟等等都要使用随机变量。有人可能会问,直接使用random这种函数不就好了吗?为什么要引入这么多算法生成的特定噪声呢?原因在于生成的随机值太“随机”了,在图形学中称这种噪声为“白噪声”(功率谱密度在整个频域内均匀分布的噪声),通俗理解就是类似小时候电视机上面的雪花噪声,

   (二维的白噪声纹理)

  噪声的基础来自于随机数,若屏幕上的每个像素点给一个0~1之间的随机数来表示象素点的亮度,就能得到上面的白噪声纹理,很像老式电视机故障时的黑白雪花。这种随机图像并没有太大的作用,因为每个点的随机数都是离散的,相互完全没有关系,能看到这幅图像中的世界是完全没有逻辑,没有能量的。  既然基本随机噪音因离散性而没意义,那就人为让噪音连续起来,这种平滑的连续化处理就是插值,在离散数据中间用函数插值的方法把空隙填满空间就自然连续了。

  简单的线性插值得到的棱角分明的结构,指数/三角函数等插值组合能形成各种插值算法,从而对应多种不同噪声。如Perlin噪声被大量用于云朵、火焰和地形等自然环境的模拟;Simplex噪声在其基础上进行改进,提高效率和效果;而Worley噪声被提出用于模拟一些多孔结构,例如纸张、木纹等。有关噪声算法的详解,可参考 【图形学】谈谈噪声

  不管怎样,对简单随机噪音插值后都能得到一种像霉菌表面的图像,本节的噪声纹理如下图:

  

  开始编码,表现效果就是从不同的区域开始,并向随机方向扩张,最后整个物体都将消失不见。

  原理:使用噪声纹理进行取样,将取样的结果和某个控制消融程度的阈值比较,若小于阈值就用clip函数将对应像素裁剪掉(透明度测试处理),不裁剪掉的像素就和另外两个颜色来进行混合插值处理从而达到烧焦的效果,并且将光照阴影和衰减也按照同样的处理,避免错误的投射阴影。

  完整代码如下:

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Shaders/Dissolve"
{
    Properties
    {
        _BurnAmount ("Burn Amount",Range(0.0, 1.0)) = 0.0     //消融程度,0为正常显示,1为物体会完全消融
        _LineWidth("Burn Line Width", Range(0.0, 0.2)) = 0.1 //模拟烧焦效果的线宽,值越大表示火焰边缘蔓延范围越广
        _MainTex ("Base (RGB)", 2D) = "white" {}             //物体本身的漫反射纹理
        _BumpMap ("Normal Map", 2D) = "bump" {}                 //法线纹理
        _BurnMap("Burn Map", 2D) = "white"{}                 //噪声纹理
        //火焰边缘的两种颜色
        _BurnFirstColor(, , , )
        _BurnSecondColor(, , , )
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            //关闭面片剔除, 则模型正面/背面都被渲染,因为消融会裸露模型内部的构造
            Cull Off

            CGPROGRAM

            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag

            fixed _BurnAmount;
            fixed _LineWidth;
            sampler2D _MainTex;
            sampler2D _BumpMap;
            sampler2D _BurnMap;
            fixed4 _BurnFirstColor;
            fixed4 _BurnSecondColor;

            float4 _MainTex_ST;
            float4 _BumpMap_ST;
            float4 _BurnMap_ST;

            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uvMainTex : TEXCOORD0;
                float2 uvBumpMap : TEXCOORD1;
                float2 uvBurnMap : TEXCOORD2;
                float3 lightDir : TEXCOORD3;
                float3 worldPos : TEXCOORD4;
                SHADOW_COORDS()
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                //使用 TRANSFORM_TEX内置宏计算三张纹理的纹理坐标
                o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
                o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
                //将光源从模型空间变换到切线空间
                TANGENT_SPACE_ROTATION;
                  o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
                  //阴影
                  o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                  TRANSFER_SHADOW(o);

                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                //对噪声纹理采样
                fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
                //返回值范围是0~1,小于消融阈值的像素被剔除
                clip(burn.r - _BurnAmount);

                float3 tangentLightDir = normalize(i.lightDir);
                fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));

                fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

                fixed3 diffuse = _LightColor0.rgb * albedo * max(, dot(tangentNormal, tangentLightDir));

                /* smoothstep( float _Min, float _Max, float _X )  实现平滑过渡,让烧焦区域颜色混合渐变
                若_X 比 _Min,小于返回 0; 若_X 比 _Max 大则返回 1; 在范围 [_Min, _Max]内返回介于 0 和 1 之间的值
                smoothstep 函数用于在一段时间范围内逐渐但非线性地增加属性,如“不透明度”(Opacity)从 0 增加到 1。*/
                //混合系数t,值为0不需要混合,像素为正常模型颜色;值为1表示像素位于消融边界处
                 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
                /*插值函数lerp实现颜色渐变过渡  lerp(a, b, w):a与b为floatX或fixedX等同种类型,返回值是对应同种类型
                当w为0时返回a,为1时返回b,0~1之间,以比重w将a b进行线性插值计算。*/
                fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
                burnColor = pow(burnColor, );

                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                //Mark! step(a, x):Returns (x >= a) ? 1 : 0
                fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));

                );
            }
            ENDCG
        }

        //透明度处理的物体的阴影做同样处理,以免被剔除的区域仍会向其他物体投射阴影从而“穿帮”
        Pass
        {
            Tags { "LightMode" = "ShadowCaster" }

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #pragma multi_compile_shadowcaster

            #include "UnityCG.cginc"

            fixed _BurnAmount;
            sampler2D _BurnMap;
            float4 _BurnMap_ST;

            struct v2f {
                V2F_SHADOW_CASTER;
                float2 uvBurnMap : TEXCOORD1;
            };

            v2f vert(appdata_base v) {
                v2f o;

                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)

                o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);

                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;

                clip(burn.r - _BurnAmount);

                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

  注意,最后通过step判断函数再对混合系数t做条件处理,因一开始_BurnAmount为0时,t可能为1或不为0,lerp函数运算导致显示了_BurnColor,实际上刚开始_BurnAmount为0时不显示任何消融效果。

  运行游戏,显然有了!

  (_BurnAmount  = 0.3)

  (_BurnAmount = 0.5)

  明显看到关闭了剔除之后,背后的消融也不会穿帮。如果把剔除打开,个人觉得效果差不多且减小性能消耗,

   (_BurnAmount  = 0.5)

  

  资料链接:

  《Unity Shader 消融效果原理与变体

  《Simplex噪声及其生成原理

  Unity Shader-死亡溶解效果    [UnityShader]溶解与重现效果(差不多)

  一种基于边缘Bloom的溶解shader的实现  (效果挺酷)

  《Trifox》中的遮挡处理和溶解着色器技术

小强学渲染之Unity Shader噪声应用的更多相关文章

  1. 小强学渲染之Unity Shader编程HelloWorld

    第一个简单的顶点vert/片元frag着色器   1)打开Unity 5.6编辑器,新建一个场景后ctrl+s保存命名为Scene_5.默认创建的场景是包含了一摄像机,一平行光,且场景背景是一天空盒而 ...

  2. 小强学渲染之Unity Shader边缘描边加强

    项目开发遇到一个需求,就是当坦克的准心瞄准敌方(enemy tank 或 item box)时,要让选中的对象的轮廓高亮起来,这实际上是接下来要讲解的实时渲染中轮廓线的渲染应用.实现方式有多种,下面逐 ...

  3. 小强学渲染之OpenGL的CPU管线

    读到这里,应该对OpenGL渲染管线有了初步简单了解.下面着重分析CPU管线,即逻辑控制中心做了什么,这部分还是容易理解的.如下图: 一,将数据加载到显存中. 这是由GPU是访问显存中的数据决定的.因 ...

  4. 小强学渲染之OpenGL渲染管线详析

    什么是OpenGL? OpenGL是一套图形硬件的软件API接口库,它直接和GPU交互,将3D场景渲染绘制到2D屏幕上.总结说,OpenGL的功能是将程序中定义的各种2D或3D模型绘制到帧缓存中,或者 ...

  5. 小强学渲染之OpenGL的GPU管线

    GPU渲染流水线,是硬件真正体现渲染概念的操作过程,也是最终将图元画到2D屏幕上的阶段.GPU管线涵盖了渲染流程的 几何阶段 和 光栅化阶段,但对开发者而言,只有对顶点和片段着色器有可编程控制权,其他 ...

  6. 小强学渲染之OpenGL状态机理解

    状态机是理论上的一种机器,呃这个说法非常非常的抽象.通俗一点理解,状态机描述了一个对象在其生命周期内所经历的各种状态,状态间的转变,发生转变的动因,条件及转变中所执行的活动.或者说,状态机是一种行为, ...

  7. Unity Shader入门精要学习笔记 - 第15章 使用噪声

    转载自 冯乐乐的 <Unity Shader 入门精要> 消融效果 消融效果常见于游戏中的角色死亡.地图烧毁等效果.这这些效果中,消融往往从不同的区域开始,并向看似随机的方向扩张,最后整个 ...

  8. 【Unity Shader】新书封面 — Low Polygon风格的渲染

    写在前面 最近又开心又担心,因为我的书马上就要上市了,开心当然是因为等了这么久终于可以如愿了,担心是因为不少人对它的期待都很大,我第一次写书,能力也有限,不知道能不能让大家满意,让大家也都喜欢上它.不 ...

  9. 【Unity Shader】(九) ------ 高级纹理之渲染纹理及镜子与玻璃效果的实现

    笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Shader](三) ----- ...

随机推荐

  1. 自定义页面微信、微博、QQ分享效果

    几行简单的分享代码既可以实现,先看下效果: 第一步:页面因为结构代码 <div id="freebtn"> <ul> <li class=" ...

  2. 存储过程中调用webservice

    存储过程中调用webservice其实是在数据库中利用系统函数调用OLE. 1.查找webservice api 可得到MSSOAP.SoapClient. 2.查找API 接口可得到mssoapin ...

  3. 20164310Exp2后门原理与实践

    一.基础问题回答 1.例举你能想到的一个后门进入到你系统中的可能方式 答:在莫名其妙的网站下载某些莫名奇妙的播放器. 2.例举你知道的后门如何启动起来(win及linux)的方式? 答:对于windo ...

  4. Java tomcat Several ports (8005, 8080, 8009) required by Tomcat v9.0 Server at localhost

    关于 下面问题是因为(8005, 8080, 8009) 被原tomcat占用了. Several ports (8005, 8080, 8009) required by Tomcat v9.0 S ...

  5. English 翻译到Vyeshal的软件

    我或许可以做一个从英语到Vyeshal的翻译软件2333

  6. spring事务[转]

    https://www.cnblogs.com/cnmenglang/p/6410848.html 先了解事务的7种传播属性: PROPAGATION_REQUIRED -- 支持当前事务,如果当前没 ...

  7. 在Spring Boot中使用 @ConfigurationProperties 注解

    但 Spring Boot 提供了另一种方式 ,能够根据类型校验和管理application中的bean. 这里会介绍如何使用@ConfigurationProperties.继续使用mail做例子. ...

  8. eclipse连接mysql数据库

    我这里在eclipse新建一个maven 项目做测试 首先我们要在本地电脑安装了mysql数据库和mysql驱动包 我的mysql数据库是通过phpstudy自带的 这个是驱动包 window–> ...

  9. jQuery获取各种位置方法

    一.获取窗口的宽高 1.获取流览器显示区域的高度 : $(window).height(); 2.获取流览器显示区域的宽度 : $(window).width(); 3.获取文档流的高度 : $(do ...

  10. xpath 笔记

    from lxml import etree info = f.read()  # requests.get().text # print(info) selector=etree.HTML(info ...