之前玩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. leetcode70 爬楼梯 Python

    组合数学Fibonacci 例3.4.1   (上楼梯问题)某人欲登上n级楼梯,若每次只能跨一级或两级,问他从地面上到第n级楼梯,共有多少种不同的方法? (解)设上到第n级楼梯的方法数为an.分类统计 ...

  2. vim的基础操作

       

  3. [UE4]瞬移对象

    一.首先把Predict Projectile Path By TraceChannel的Draw Debug Type改成none,不显示射线,改成该选项并不会影响正常使用. 二.避免瞬移穿透底板 ...

  4. Google SketchUp Cookbook: (Chapter 2) Following Paths with Follow Me

    软件环境 SketchUp Pro 2018 参考书籍 Google SketchUp Cookbook Follow Me工具 Follow Me工具,将2D图形沿着一条路径挤出生成3D物体. 使用 ...

  5. 了解原型设计工具pencil project

    Pencil Pencil是一个功能强大的界面原型设计工具,可以设计网页和桌面程序界面,侧重点在于设计,支持自定义控件和导出模板,功能确实很强大. 内置形状集合 从2.0.2开始铅笔默认包含更多形状集 ...

  6. Spark RDD 操作

    1. Spark RDD 创建操作 1.1 数据集合   parallelize 可以创建一个能够并行操作的RDD.其函数定义如下: ) scala> sc.defaultParallelism ...

  7. idea springboot 父子工程 子工程maven不自动import

    父工程删除对spring boot启动项的引用,因为父工程 dependencyManagement,它不会自动加载包,只指定包的版本, 如果在父工程中引用了包,但是没有指定包版本,子工程将不会识别到 ...

  8. 爬虫之scrapy

    一.项目简单流程 1.创建项目 scrapy startproject 项目名 2.创建Spider cd 项目名 scrapy genspider 爬虫名 域名 class YokaSpider(s ...

  9. python大法好——ython GUI编程(Tkinter)

    Python GUI编程(Tkinter) Python 提供了多个图形开发界面的库,几个常用 Python GUI 库如下: Tkinter: Tkinter 模块(Tk 接口)是 Python 的 ...

  10. junit,面向切面开发(动态代理),工厂设计模式,数据库连接池

    1.junit junit又叫单元测试,好处是能进行批量测试,而且如果方法出现了问题能立刻定位出出现问题的方法,还有一个好处是感官效果很好,如果方法都通过了则显示绿条,否则显示红条 TestCase. ...