实现原理:噪声纹理+透明度测试

噪声纹理:

在我的理解下就是,存储着随机值的一张纹理图片,因为大量生成随机数非常的耗时而且麻烦,如果用一张图来存储随机值,这样获取起来会非常的简单高效,利用噪声纹理我们可以实现很多特殊的效果,比如消融效果。

实现效果:

消融效果

实现方法:

1、shader文件:首先需要利用噪声纹理编写一个实现随机剔除渲染内容的shader。
主要实现功能:获取到噪声纹理中的随机数,和预设的消融阈值做比较,如果随机值小于阈值表示当前片元将不会渲染,反之该片元就正常渲染。
代码:
(1)各个属性的声明

    Properties{_MainTex ("Base (RGB)", 2D) = "while" {}_BurnAmount("Burn Amount",Range(0.0,1.0))=0.0//消融程度,当为0是消融为0,意思是没有消融,如果消融为1,则表示完全消融_LineWidth("Burn Line Width",Range(0.0,0.2))=0.1//烧焦效果的线宽,也就是烧焦边缘的效果宽度_BumpMap("Normal Map",2D)="bump"{}//法线纹理//火焰边缘的两种颜色_BurnFirstColor("Burn First Color",Color)=(1,0,0,1)//靠近非消融部分的颜色_BurnSecondColor("Burn Second Color",Color)=(1,0,0,1)//消融边缘的颜色//噪声纹理_BurnMap("Burn Map",2D)="while"{}}

(2)设置总体的Tags,因为渲染的物体不存在透明物体或者是非透明纹理,所以设置如下

Tags { "RenderType"="Opaque" "Queue"="Geometry"}

(3)设置实现消融效果的pass的 Tags以及其他内容

    Tags{"LightMode"="ForwardBase"}//剔除关闭因为消融会看到物体内部,如果开启剔除会导致内部没有渲染到Cull OffCGPROGRAM#include"Lighting.cginc"#include"AutoLight.cginc"#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbasefixed4 _Color;fixed _BurnAmount;fixed _LineWidth;sampler2D _MainTex;float4 _MainTex_ST;sampler2D _BumpMap;float4 _BumpMap_ST;sampler2D _BurnMap;float4 _BurnMap_ST;fixed4 _BurnFirstColor;fixed4 _BurnSecondColor;

(4)设置两个结构体

    struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float4 tangent : TANGENT;float2 texcoord : TEXCOORD0;};struct v2f{float4 pos:SV_POSITION;float2 uvMainTex:TEXCOORD0;float2 uvBumpTex:TEXCOORD1;float2 uvBurnTex:TEXCOORD2;float3 lightDir:TEXCOORD3;float3 worldPos:texcoord4;SHADOW_COORDS(5)};

(5)顶点着色器

    v2f vert(a2v v){v2f o;o.pos =UnityObjectToClipPos(v.vertex);o.uvMainTex =TRANSFORM_TEX(v.texcoord,_MainTex);o.uvBumpTex =TRANSFORM_TEX(v.texcoord,_BumpMap);o.uvBurnTex =TRANSFORM_TEX(v.texcoord,_BurnMap);//变换得到切线空间下的光线向量,objspacelightdir是根据坐标得到物体空间下的光线坐标,再对光线坐标进行变换得到切线空间下的光线向量//获取到变换矩阵rotation,从模型空间转换到切线空间TANGENT_SPACE_ROTATION;o.lightDir =mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;//从物体坐标到世界坐标o.worldPos =mul(unity_ObjectToWorld,v.vertex).xyz;//计算阴影纹理的采样坐标TRANSFER_SHADOW(o);return o;}

(6)片元着色器

    fixed4 frag(v2f i):SV_Target{fixed3 burn =tex2D(_BurnMap,i.uvBurnTex).rgb;//根据从噪声纹理的采样结果来和消融程度做比较来剔除一些片元不渲染,cilp函数是对于小于0的片元进行剔除,所以如果采样得到的随机值小于burnAmount则会被剔除,burnAmount设置的越大被剔除的部分就越多,设置的越小被剔除的也就越少clip(burn.r-_BurnAmount);//获取到切线空间下的光线向量和法线,用于计算漫反射float3 tangentLightDir =normalize(i.lightDir);float3 tangentNormal =UnpackNormal(tex2D(_BumpMap,i.uvBumpTex));//反射率,从漫反射纹理上采样得到反射率fixed3 albedo =tex2D(_MainTex,i.uvMainTex).rgb;//环境光照乘以反射率得到反射光照fixed3 ambient =UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;//漫反射fixed3 diffuse =_LightColor0.rgb*albedo*max(0,dot(tangentNormal,tangentLightDir));//根据消融程度得到0到线最宽距离间的差值,再根据该差值计算得到烧焦的颜色//利用smoothstep根据差值获取到[0,1]的值,差值越小说明越靠近消融边缘,而因为最后混合颜色的需求,需要t值越小,越靠近非消融区域,所以需要用1-。fixed t =1-smoothstep(0.0,_LineWidth,burn.r-_BurnAmount);//t值越大表示越靠近消融区域。fixed3 burnColor =lerp(_BurnFirstColor,_BurnSecondColor,t);//pow一下更加突出burnColor =pow(burnColor,5);UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);//根据差值混合光照颜色和烧焦颜色//利用step函数,保证当_BurnAmount为0的时候完全渲染图形,当_BurnAmount大于0.0001的时候,混合光照颜色和烧焦的颜色,当t越大越靠近消融区域。fixed3 finalColor =lerp(ambient+diffuse*atten,burnColor,t*step(0.0001,_BurnAmount));return fixed4(finalColor,1.0);}

smoothstep 用来生成0到1的平滑过渡值,它也叫平滑阶梯函数。
float smoothstep(float a, float b, float x)
{

x = clamp((x - a) / (b- a), 0.0, 1.0);
return x * x * (3 - 2 * x);
}`
效果是smoothstep(a, b, x)

step 通常用来取代 if-else 的代码
step (a, x)
{
if (x < a)
{
return 0;
}
else
{
return 1;
}
}
也就是step (a, x),如果a>x,则结果为0,反之结果为1

(7)控制阴影效果的Pass
由于被剔除的部分应该是没有阴影的所以,我们需要自定义一个阴影的pass,在阴影投射之前将不需要投射的部分剔除,利用和消融效果相同的思路,利用噪声纹理获取随机值与阙值进行比较。

    Pass{Tags{"LightMode"="ShadowCaster"}CGPROGRAM#include"unityCG.cginc"#pragma vertex vert#pragma fragment frag#pragma multi_compile_shadowcasterstruct a2v{float4 vertex:POSITION;float2 texcoord:TEXCOORD0;};struct v2f{//利用宏定义阴影投射时需要的变量V2F_SHADOW_CASTER;float2 uvBurnMap:TEXCOORD1;};fixed _BurnAmount;sampler2D _BurnMap;float4 _BurnMap_ST;v2f vert(appdata_base v){v2f o;//计算我们投射阴影时候需要的变量TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);o.uvBurnMap =TRANSFORM_TEX(v.vertex,_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}

2、c#脚本:编写脚本控制消融阙值在[0,1]之间变化,从而实现物体从完整消融到无。
实现思路:将脚本挂载在物体上,获取到物体的材质,随时间更新消融阈值,并且使得消融阈值一直在[0,1]之间。

Mathf.Repeat:
Repeat(float t,float length);
循环值t,使输出不会大于等于length,也不会小于0。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BurnAmountChangeWithTime : MonoBehaviour
{public Material material;[Range(0.01f, 1.0f)]public float burnSpeed = 0.3f;private float burnamount = 0.0f;// Start is called before the first frame updatevoid Start(){if (material == null){Renderer renderer = gameObject.GetComponentInChildren<Renderer>();if (renderer != null){material = renderer.material;}}if (material == null){this.enabled = false;}else{material.SetFloat("_BurnAmount", 0.0f);}}// Update is called once per framevoid Update(){burnamount = Mathf.Repeat(Time.time * burnSpeed, 1.0f);material.SetFloat("_BurnAmount", burnamount);}
}

shader完整代码:

Shader "Custom/Chapter15-Dissolve"
{Properties{_MainTex ("Base (RGB)", 2D) = "while" {}_BurnAmount("Burn Amount",Range(0.0,1.0))=0.0//消融程度,当为0是消融为0,意思是没有消融,如果消融为1,则表示完全消融_LineWidth("Burn Line Width",Range(0.0,0.2))=0.1//烧焦效果的线宽,也就是烧焦边缘的效果宽度_BumpMap("Normal Map",2D)="bump"{}//法线纹理//火焰边缘的两种颜色_BurnFirstColor("Burn First Color",Color)=(1,0,0,1)_BurnSecondColor("Burn Second Color",Color)=(1,0,0,1)//噪声纹理_BurnMap("Burn Map",2D)="while"{}}SubShader{Tags { "RenderType"="Opaque" "Queue"="Geometry"}//消融passPass{Tags{"LightMode"="ForwardBase"}//剔除关闭因为消融会看到物体内部,如果开启剔除会导致内部没有渲染到Cull OffCGPROGRAM#include"Lighting.cginc"#include"AutoLight.cginc"#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbasefixed4 _Color;fixed _BurnAmount;fixed _LineWidth;sampler2D _MainTex;float4 _MainTex_ST;sampler2D _BumpMap;float4 _BumpMap_ST;sampler2D _BurnMap;float4 _BurnMap_ST;fixed4 _BurnFirstColor;fixed4 _BurnSecondColor;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float4 tangent : TANGENT;float2 texcoord : TEXCOORD0;};struct v2f{float4 pos:SV_POSITION;float2 uvMainTex:TEXCOORD0;float2 uvBumpTex:TEXCOORD1;float2 uvBurnTex:TEXCOORD2;float3 lightDir:TEXCOORD3;float3 worldPos:texcoord4;SHADOW_COORDS(5)};v2f vert(a2v v){v2f o;o.pos =UnityObjectToClipPos(v.vertex);o.uvMainTex =TRANSFORM_TEX(v.texcoord,_MainTex);o.uvBumpTex =TRANSFORM_TEX(v.texcoord,_BumpMap);o.uvBurnTex =TRANSFORM_TEX(v.texcoord,_BurnMap);//变换得到切线空间下的光线向量,objspacelightdir是根据坐标得到物体空间下的光线坐标,再对光线坐标进行变换得到切线空间下的光线向量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.uvBurnTex).rgb;//根据从噪声纹理的采样结果来和消融程度做比较来剔除一些片元不渲染,cilp函数是对于小于0的片元进行剔除,所以如果采样得到的值小于burnAmount则会被剔除,burnAmount设置的越大被剔除的部分就越多,设置的越小被剔除的也就越少clip(burn.r-_BurnAmount);float3 tangentLightDir =normalize(i.lightDir);float3 tangentNormal =UnpackNormal(tex2D(_BumpMap,i.uvBumpTex));//反射率,从漫反射纹理上采样得到反射率fixed3 albedo =tex2D(_MainTex,i.uvMainTex).rgb;//环境光照乘以反射率得到反射光照fixed3 ambient =UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;//漫反射fixed3 diffuse =_LightColor0.rgb*albedo*max(0,dot(tangentNormal,tangentLightDir));//根据消融程度得到0到线最宽距离间的差值,再根据该差值计算得到烧焦的颜色fixed t =smoothstep(0.0,_LineWidth,burn.r-_BurnAmount);fixed3 burnColor =lerp(_BurnSecondColor,_BurnFirstColor,t);burnColor =pow(burnColor,5);UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);//根据差值混合光照颜色和烧焦颜色fixed3 finalColor =lerp(ambient+diffuse*atten,burnColor,1-t*step(0.0001,_BurnAmount));return fixed4(finalColor,1.0);}ENDCG}//被剔除的物体在渲染阴影的pass里面没有被剔除而只是在渲染火焰颜色的时候被剔除了之后没有进行渲染,所以在渲染阴影的时候会给被剔除的片元也渲染阴影,这样就不正确了所以要重新编写阴影的passPass{Tags{"LightMode"="ShadowCaster"}CGPROGRAM#include"unityCG.cginc"#pragma vertex vert#pragma fragment frag#pragma multi_compile_shadowcasterstruct a2v{float4 vertex:POSITION;float2 texcoord:TEXCOORD0;};struct v2f{V2F_SHADOW_CASTER;float2 uvBurnMap:TEXCOORD1;};fixed _BurnAmount;sampler2D _BurnMap;float4 _BurnMap_ST;v2f vert(appdata_base v){v2f o;TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);o.uvBurnMap =TRANSFORM_TEX(v.vertex,_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"
}

Unity3d shader实现消融效果相关推荐

  1. Shader之消融效果

    简介 游戏中经常会看到特效给人眼前一亮的感觉,例如当怪物死亡时逐渐消逝的效果.人物由隐形状态逐渐显形的效果.本篇文章将从Shader的角度去思考如何通过代码去实现这些效果. 原理 噪声图:利用噪声图的 ...

  2. 【Unity3d Shader】景深效果

    摄影常用的一招就是背景虚化,背景虚化的相片可以突出拍摄主题,强调自己要展现的事物. 摄影技巧为:1,开大光圈:2,拉长焦距:3,主体离镜头近:4,背景离主体远 有了背景虚化,照片会变的有艺术感见下图( ...

  3. Unity Shader 燃烧消融效果(surface表面着色器)

    有些时候需要让角色死亡时逐渐燃烧消失,用表面着色器很容易实现. 可以用一张黑白图片控制各部位的消融顺序,比如让该图片的某通道和一个变量相比,小于该变量则舍弃片元.变量从0不断变大,则消融面积逐渐扩大. ...

  4. 【Unity3d Shader】眨眼效果

    眨眼效果常用于游戏中的剧情表现,先看效果图: 原理很简单: 椭圆公式: 当焦点在x轴时,椭圆的标准方程是:x^2/a^2+y^2/b^2=1,(a>b>0): 当焦点在y轴时,椭圆的标准方 ...

  5. Unity3D Shader :水滴效果

    Shader 代码 Shader "Unlit/Test" { Properties { _MainTex ("Texture", 2D) = "wh ...

  6. 【Unity Shader 消融效果_案例分享】

    1.实现逻辑 消融效果主要是利用了Shader中的clip()函数,也就是透明测试功能,在ASE中叫"Opacity Mask". 消融效果是基于一张"Noise&quo ...

  7. Unity Shader学习:Dissolve消融效果

    Unity Shader学习:Dissolve消融效果 消融效果在游戏里非常常用,这里简单的实现下,代码里用到了if分支在shader里可能会费一点,如果想直接用puppet_master大佬版本的话 ...

  8. Shader学习的基础知识( 三十)消融效果

    消融效果其实就是噪声纹理+透明度测试,取样阈值在小于0则裁剪掉,0到N显示中间色,N到1则正常显示. Shader "Unity Shaders Book/Chapter 15/Dissol ...

  9. Unity_Shader高级篇_15_Unity Shader入门精要_消融效果

    第15章 使用噪音 很多时候,向规则的事物里添加一些"杂乱无章"的效果往往会有意想不到的效果.而这些"杂乱无章"的效果来源就是噪音.在本章中,我们将会学习如何使 ...

最新文章

  1. Activex test contact failed to create control 未指定的错误 控件无法加载的原因
  2. 运行shell脚本时怎么知道jdk路径_Linux中如何查询运行文件的全路径的方法
  3. LeetCode-56-Merge Intervals
  4. 【EWSA无线路由密码破解工具 中文特别版下载】含教程及字典(弱口令生日特殊符号等)
  5. 基于boost asio实现的支持ssl的通用socket框架
  6. 大分区表高并发性能提升100倍?阿里云 RDS PostgreSQL 12 解读
  7. 求栈中元素个数算法_Algorithm 大家都会的去除有序数组中重复元素的三种算法...
  8. 最棒的java代码生成器
  9. 【无标题】曲线坐标张量分析
  10. 一款DYI动态桌面壁纸程序
  11. AutoCAD快速入门(二十九):视口
  12. 高效工作节省时间的一些小技巧
  13. 【存储知识】RAID(磁盘冗余阵列)与 LVM(逻辑卷管理器)
  14. 【IoT】创业:产品生产之老化测试
  15. 七种常见的电子邮件安全协议简析
  16. 5分钟通过Sails.js从零开始开发RESTful API
  17. Selenium+Request爬取某鱼直播音频数据(下)
  18. 伸缩式起重机的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  19. Windows电脑蓝牙打电话-预研总结
  20. 对创建的screen会话进行恢复时出现:There is no screen to be resumed matching XXX 解决办法

热门文章

  1. ALLyeSNO 优化版浩方 第二版 Ver:2007.06.15 清除广告 自动挤房间
  2. 长期稳定的搬砖项目——steam搬砖
  3. 蓝桥杯卡片换位(DFS)
  4. USB协议中的返回包含义
  5. 蛇形矩阵2021-06-13
  6. 普歌-腾讯云短信+使用node发送短信(3种方法API、SDK)、封装工具、搭建web服务、写接口、调用接口发送短信、时效性判断、验证验证码的正确性(下)
  7. js常用下载的几种方式
  8. 不管你学的是什么专业,你都应该多少懂些管理学的东西之【罗森塔尔效应】【虚假同感偏差】...
  9. 【Python开发】FastAPI 07:Depends 依赖注入
  10. PTA-成绩录入时的及格与不及格人数统计