Esfog_UnityShader教程_溶解效果Dissolve

  溶解效果在游戏中是很常见的,比如在一些神话或者魔法世界中,一些NPC角色在剧情需要时候会身体会渐渐的消失掉.甚至有一些更炫的,比如用火焰喷射器把目标燃尽。这些都可以用到溶解效果。这篇文章主要是讲解一下比较基础的溶解效果如何实现,实现的方法并不唯一,本篇只是其中一种思路。



原理



  既然想让角色的身体一块块渐渐消失,不妨就让角色身体上相应的部位不进行渲染(或者改成透明,我们这里选择前者)。那根据什么来判断身体的哪一部分需要被溶解呢,这时候就需要一张额外的贴图或者利用角色纹理贴图的Alpha通道(本篇选择前者)。这个贴图和纹理贴图一样,对应着玩家身体的每一个位置,这样我们就可以根据贴图上某个指定通道的颜色值来控制角色各个身体部位是否溶解了。

  另外我们还会涉及到的一个命令叫discard,是由CG提供的,若出现在fragmentShader中表示立即放弃当前处理的片元。也就是说当我们判定当前片远需要溶解的时候我们就使用discard命令。

  上面这张图就是用来控制溶解程度的纹理,我们这里比较简单,只使用了R通道,如果你想做的很复杂也可以利用上其它的通道。在本篇中我们将根据时间的推进不断溶解掉贴图上R通道颜色值较小的区域。美术可以利用这张贴图控制角色的任意溶解顺序和效果。由于我的素材都是随便找的,自己又不会画画,所以这个图和角色纹理可能并不搭配,请不要在意。

  再看下面这个图,我先来提前说一下本篇Shader在Properties中提供的各个可调节的参数

1.Base(RGB)是角色纹理贴图,不用解释了.

2.NoiseTex(R)是很重重要的,我们用来控制角色溶解的样式贴图,我们只利用了R通道。

3.DissolveSpeed(Second),整个溶解过程需要的时间,单位是秒

4.EdgeWidth,这个就是额外加的一个边缘效果,比如你观察纸在化作灰烬时他的周围会先变成黑褐色。我们这里的EdgeWidth就是定义这个周围区域的大小,注意这个width并不是指的长度,而是定义一个透明度的间隔区间,也就是与基准值相差多少可以算作边缘处理。

5.EdgeColor,和4一样,边缘效果的颜色。



实现


 1 Shader "Esfog/Dissolve"
 2 {
 3     Properties
 4     {
 5         _MainTex ("Base (RGB)", 2D) = "white" {}
 6         _NoiseTex ("NoiseTex (R)",2D) = "white"{}
 7         _DissolveSpeed ("DissolveSpeed (Second)",Float) = 1
 8         _EdgeWidth("EdgeWidth",Range(0,0.5)) = 0.1
 9         _EdgeColor("EdgeColor",Color) =  (1,1,1,1)
10     }
11     SubShader
12     {
13         Tags { "RenderType"="Opaque" }
14
15         Pass
16         {
17             CGPROGRAM
18             #pragma vertex vert_img
19             #pragma fragment frag
20             #include "UnityCG.cginc"
21
22             uniform sampler2D _MainTex;
23             uniform sampler2D _NoiseTex;
24             uniform float _DissolveSpeed;
25             uniform float _EdgeWidth;
26             uniform float4 _EdgeColor;
27
28             float4 frag(v2f_img i):COLOR
29             {
30                 float DissolveFactor = saturate(_Time.y / _DissolveSpeed);
31                 float noiseValue = tex2D(_NoiseTex,i.uv).r;
32                 if(noiseValue <= DissolveFactor)
33                 {
34                     discard;
35                 }
36
37                 float4 texColor = tex2D(_MainTex,i.uv);
38                 float EdgeFactor = saturate((noiseValue - DissolveFactor)/(_EdgeWidth*DissolveFactor));
39                 float4 BlendColor = texColor * _EdgeColor;
40
41                 return lerp(texColor,BlendColor,1 - EdgeFactor);
42             }
43
44             ENDCG
45         }
46     }
47
48     FallBack Off
49 }

5~9行,前面解释过了,这里就不再说明了。

   18行,这里我使用了unity提供的vert_img顶点着色器,因为我们的需求很简单,只需要进行顶点坐标变换以及把uv传给后面就行了,既然有现成的就不自己写了,它会自动把结果返回到一个v2f_img结构体中.

   30行,这里的_Time.y就是从物体一开始渲染到现在所过去的时间unity给我们提供了几个不同的时间参数都保存在_Time中,其中_Time.y是标准的时间,单位(s)。用它去除以_DissolveSpeed.就能得到当前时间已经占用了多少总溶解时间.由于我们要保证比值范围限定在(0,1)的范围内,所以最后调用一个saturate()函数,这个函数式CG提供的就是来完成这个目的的。

  31行,从溶解贴图里面取出r通道的值.

  32~35行,我们利用刚才算出比值来对溶解贴图里面的R通道颜色值进行判断,如果R通道颜色值小于等于这个比值那么我们就通过discard抛弃当前片远,当前片远的处理会立刻结束。

  38行,又是计算一个比值,分子是noiseValue - DissolveFactor,这个值表示溶解贴图上的R通道值和目前的溶解基准值相差多少,而(_EdgeWidth*DissolveFactor)可以这样理解,_EdgeWidth表示可以多大的差值可以算作是边缘,而乘以一个DissolveFactor,就表示边缘最大宽度会随着时间变化时间越长,宽度越宽。最后这两个值相除就代表当前片元的边缘程度,值越小表示离他被溶解掉的时间越长,反之表示他很快就要被溶解掉了.

  39行,我们计算一下当前纹理颜色与边缘颜色相乘的值,后面需要用。

  41行,这里我们用lerp函数来利用上一步计算出来的(1-EdgeFactor)对原始纹理颜色和上一步中的混合颜色进行一个插值,在解释第38行时候我们已经说了,EdgeFactor越小表示离溶解时间越近,也就是说它边缘化的颜色成分越重,那么由于lerp(x,y,a) = x*(1-a) + y*a;为了让边缘化的颜色重一些我们就使用1-EdgeFactor作为因子了,反之亦然。

  好了看一下效果,使用不同的溶解贴图会产生完全不同的效果,下面展示一下我从网上随便找到的两个贴图,由于和原模型纹理并不是一套的,所以看上去会有点怪,不要在意。

使用这个贴图1:

效果如下

使用贴图2:

效果如下:

  好了基本效果就是如此,总体来说这个Shader的效果看起来好与不好,主要还是取决于美术提供的溶解贴图是否合适,本节素材并不是一套的,看起来会有点怪。

  另外一点就是大家在学习Shader的过程中你总会发现别人打源代码中会出现很多pow,lerp,或者对各个参数的加减乘除,希望大家不要太纠结这些地方的意义,有时候确实是有一定的数学原理,但很多时候都是开发者自己写的一个经验公式,为了调效果而写的。随着你的经验慢慢增多,渐渐的你也会开始使用一些经验公式。效果对了就好,只要不影响性能。毕竟给玩家看的又不是源代码。

  尊重他人智慧成果,欢迎转载,请注明作者esfog,原文地址http://www.cnblogs.com/Esfog/p/4469025.html 

  

时间: 2024-11-05 06:28:31

Esfog_UnityShader教程_溶解效果Dissolve的相关文章

Esfog_UnityShader教程_逐帧动画

有段日子没出这个系列的新文章了,今天就拿一个比较常见也比较基础的利用改变Shader来改变不断调整UV实现播放逐帧动画的小功能.很久没写了就当练练手了.在新版本的Unity中早就已经集成了Sprite2D的功能,而且可以编辑不规则的图形,不过了解一下它的原理,也是蛮好的! 逐帧动画 帧动画大家应该都不陌生,经常会看到把一个动画几帧的的状态按一定顺序整合在同一张图片上,如下图: 从上图中我们可以看出,这个图片动画一共有20帧,从左到右,从上到下依次排布(基本上都是这个规律).为了展示效果我们需要一

Esfog_UnityShader教程_遮挡描边(原理篇)

咳咳,有段时间没有更新了,最近有点懒!把不少精力都放在C++身上了.闲言少叙,今天要讲的可和之前的几篇有所不同了,这次是一个次综合应用.这篇内容中与之前不同主要体现在下面几点上. 1.之前我们写的都是只用一个Shader来实现某些效果,而这次我们要使用多个Shader结合起来发挥作用. 2.之前我们只是写的都是纯Shader代码,没有涉及到客户端的C#脚本(你爱用JS也可).而这次也要使用到. 3.这篇教程涉及到的代码量也是之前是之前的几倍了. 4.总的来说之前的都是比较简单的,而这篇就有了些难

python基础教程_学习笔记23:图形用户界面

图形用户界面 丰富的平台 在编写Python GUI程序前,需要决定使用哪个GUI平台. 简单来说,平台是图形组件的一个特定集合,可以通过叫做GUI工具包的给定Python模块进行访问. 工具包 描述 Tkinter 使用Tk平台.很容易得到.半标准. wxpython 基于wxWindows.跨平台越来越流行. PythonWin 只能在Windows上使用.使用了本机的Windows GUI功能. JavaSwing 只能用于Jython.使用本机的Java GUI. PyGTK 使用GTK

Spring_MVC_教程_快速入门_深入分析

Spring MVC 教程,快速入门,深入分析 博客分类: SPRING Spring MVC 教程快速入门 资源下载: Spring_MVC_教程_快速入门_深入分析V1.1.pdf SpringMVC核心配置文件示例.rar 作者:赵磊 博客:http://elf8848.iteye.com 目录 一.前言 二.spring mvc 核心类与接口 三.spring mvc 核心流程图 四.spring mvc DispatcherServlet说明 五.spring mvc 父子上下文的说明

目前见到的最傻瓜全面的STRUTS入门教程^_^

目前见到的最傻瓜全面的STRUTS入门教程^_^ 一  Jakarta Struts学习之新手上路 Web 应用开发早期曾经是那么的“简单”,那个时候还只是纯HTML页面和浏览器特效而已.由于还涉及不到动态数据操作和商业应用,也就省去了很多麻烦.但是这 样的“简单”只是过眼云烟,如今我们不得不为复杂的基于Web的商业应用开发采用诸多技术. 本文将介绍如何利用Struts进行应用开发的前台整合的开发过程.Struts是一个为开发基于模型(Model)-视图(View)-控制器 (Controlle

Esfog_UnityShader教程_NormalMap法线贴图

咳咳,好久没有更新了,一来是这段时间很忙很忙,再来就是自己有些懒了,这个要不得啊,赶紧补上.在前面我们已经介绍过了漫反射和镜面反射,这两个是基本的光照类型,仅仅依靠它们就想制作出精美的效果是远远不够的,这一篇我们就来了解一下如何利用一种叫做法线贴图的技术并结合我们前面讲过的知识来制作出更精细的效果. 法线贴图NormalMap 首先要提到的是,什么是法线贴图,如果大家想看更专业的解释可以自行求助搜索引擎,这里我说一下我的个人理解:在游戏中,如果角色或物体模型做的越精细(面数越多),那么渲染后效果

python基础教程_学习笔记21:文件和素材

文件和素材 打开文件 open函数用来打开文件,语法如下: open([name[,mode[,buffering]]) open函数使用一个文件名作为唯一的强制参数,然后返回一个文件对象.模式(mode)和缓冲(buffering)参数都是可选的. >>> f=open(r'D:\software(x86)\Python27\README.txt') 如果文件不存在,则出现错误: >>> f=open(r'D:\software(x86)\Python27\READM

python基础教程_学习笔记15:标准库:一些最爱——fileinput

标准库:一些最爱 fileinput 重要的函数 函数 描述 input([files[,inplace[,backup]]) 便于遍历多个输入流中的行 filename() 返回当前文件的名称 lineno() 返回当前(累计)的名称 filelineno() 返回当前文件的行数 isfirstline() 检查当前行是否是文件的第一行 isstdin() 检查最后一行是否来自sys.stdin nextfile() 关闭当前文件,移动到下一个文件 close() 关闭序列 fileinput

python基础教程_学习笔记12:充电时刻——模块

充电时刻--模块 python的标准安装包括一组模块,称为标准库. 模块 >>> import math >>> math.sin(0) 0.0 模块是程序 任何python程序都可以作为模块导入. $ cat hello.py #!/usr/bin/python print "Hello,signjing!" $ ./hello.py Hello,signjing! 假设将python程序保存在/home/ggz2/magiccube/mysh/p