当前位置:网站首页>Unity3d shader achieves ablation effect
Unity3d shader achieves ablation effect
2022-07-28 17:05:00 【Morita Rinko】
Realization principle : Noise texture + Transparency test
Noise texture :
In my understanding, it is , A texture image storing random values , Because generating a large number of random numbers is very time-consuming and troublesome , If you use a graph to store random values , In this way, it will be very simple and efficient , Using noise texture, we can achieve many special effects , Such as ablation effect .
Realization effect :
Ablation effect
Implementation method :
1、shader file : First of all, we need to use noise texture to write a method to realize random elimination of rendering content shader.
Main functions : Get the random number in the noise texture , Compare with the preset ablation threshold , If the random value is less than the threshold, the current slice will not be rendered , On the contrary, the slice is rendered normally .
Code :
(1) Declaration of each attribute
Properties
{
_MainTex ("Base (RGB)", 2D) = "while" {
}
_BurnAmount("Burn Amount",Range(0.0,1.0))=0.0// Ablation degree , When it comes to 0 Is melting into 0, It means no ablation , If it melts into 1, It means complete ablation
_LineWidth("Burn Line Width",Range(0.0,0.2))=0.1// The line width of the Scorch effect , That is, the effect width of the scorched edge
_BumpMap("Normal Map",2D)="bump"{
}// Normal texture
// Two colors on the edge of the flame
_BurnFirstColor("Burn First Color",Color)=(1,0,0,1)// Color near the non ablative part
_BurnSecondColor("Burn Second Color",Color)=(1,0,0,1)// Melt the color of the edge
// Noise texture
_BurnMap("Burn Map",2D)="while"{
}
}
(2) Set the overall Tags, Because the rendered object does not have transparent objects or non transparent textures , So the settings are as follows
Tags {
"RenderType"="Opaque" "Queue"="Geometry"}
(3) Set the pass Of Tags And more
Tags{
"LightMode"="ForwardBase"}
// Eliminate and close because ablation will see the interior of the object , If you turn on culling, the interior will not be rendered to
Cull Off
CGPROGRAM
#include "Lighting.cginc"
#include "AutoLight.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
fixed4 _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) Set up two structures
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) Vertex shader
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);
// Transform to get the ray vector in tangent space ,objspacelightdir Is to get the coordinates of light in object space according to the coordinates , Then transform the ray coordinates to get the ray vector in tangent space
// Get the transformation matrix rotation, From model space to tangent space
TANGENT_SPACE_ROTATION;
o.lightDir =mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;
// From object coordinates to world coordinates
o.worldPos =mul(unity_ObjectToWorld,v.vertex).xyz;
// Calculate the sampling coordinates of shadow texture
TRANSFER_SHADOW(o);
return o;
}
(6) Chip shader
fixed4 frag(v2f i):SV_Target{
fixed3 burn =tex2D(_BurnMap,i.uvBurnTex).rgb;
// According to the sampling results from the noise texture and the ablation degree, some pieces are removed and not rendered ,cilp The function is for less than 0 The piece element of , So if the random value obtained by sampling is less than burnAmount Will be eliminated ,burnAmount The larger the setting, the more parts will be eliminated , The smaller the setting, the less it will be rejected
clip(burn.r-_BurnAmount);
// Get the ray vector and normal in tangent space , Used to calculate diffuse reflection
float3 tangentLightDir =normalize(i.lightDir);
float3 tangentNormal =UnpackNormal(tex2D(_BumpMap,i.uvBumpTex));
// Reflectivity , Reflectance is obtained by sampling from diffuse texture
fixed3 albedo =tex2D(_MainTex,i.uvMainTex).rgb;
// The reflected light is obtained by multiplying the ambient light by the reflectivity
fixed3 ambient =UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
// Diffuse reflection
fixed3 diffuse =_LightColor0.rgb*albedo*max(0,dot(tangentNormal,tangentLightDir));
// According to the degree of ablation 0 The difference between the widest distance to the line , Then calculate the burnt color according to the difference
// utilize smoothstep Get [0,1] Value , The more obvious the difference is, the closer it is to the edge of ablation , And because of the need to finally mix colors , need t The smaller the value. , The closer to the non ablation area , So we need to use 1-.
fixed t =1-smoothstep(0.0,_LineWidth,burn.r-_BurnAmount);
//t The higher the value, the closer it is to the ablation area .
fixed3 burnColor =lerp(_BurnFirstColor,_BurnSecondColor,t);
//pow The following is more prominent
burnColor =pow(burnColor,5);
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
// Mix the light color and the burnt color according to the difference
// utilize step function , Guarantee _BurnAmount by 0 When rendering graphics completely , When _BurnAmount Greater than 0.0001 When , Mix light colors and burnt colors , When t The larger, the closer to the ablation area .
fixed3 finalColor =lerp(ambient+diffuse*atten,burnColor,t*step(0.0001,_BurnAmount));
return fixed4(finalColor,1.0);
}
smoothstep Used to generate 0 To 1 Smooth transition value , It is also called smooth step function .
float smoothstep(float a, float b, float x)
{x = clamp((x - a) / (b- a), 0.0, 1.0);
return x * x * (3 - 2 * x);
}`
Effect is smoothstep(a, b, x)
step Usually used to replace if-else Code for
step (a, x)
{
if (x < a)
{
return 0;
}
else
{
return 1;
}
}
That is to say step (a, x), If a>x, The result is 0, On the contrary, the result is 1
(7) Control the shadow effect Pass
Because the removed part should have no shadow, so , We need to customize a shadow pass, Eliminate parts that do not need to be cast before shadow casting , Use the same idea as the ablation effect , Use the noise texture to obtain the random value and compare with the threshold .
Pass{
Tags{
"LightMode"="ShadowCaster"}
CGPROGRAM
#include "unityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
struct a2v{
float4 vertex:POSITION;
float2 texcoord:TEXCOORD0;
};
struct v2f{
// Use macros to define the variables needed for shadow casting
V2F_SHADOW_CASTER;
float2 uvBurnMap:TEXCOORD1;
};
fixed _BurnAmount;
sampler2D _BurnMap;
float4 _BurnMap_ST;
v2f vert(appdata_base v){
v2f o;
// Calculate the variables we need to cast shadows
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;
// Remove it
clip(burn.r-_BurnAmount);
// Complete the shadow casting of the undeleted part
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
2、c# Script : Write scripts to control the ablation threshold at [0,1] Change between , So as to realize the object from complete ablation to none .
Realize the idea : Mount the script on the object , Get the material of the object , Update ablation threshold over time , And keep the ablation threshold constant [0,1] Between .
Mathf.Repeat:
Repeat(float t,float length);
Cyclic value t, So that the output will not be greater than or equal to length, Not less than 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 update
void 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 frame
void Update()
{
burnamount = Mathf.Repeat(Time.time * burnSpeed, 1.0f);
material.SetFloat("_BurnAmount", burnamount);
}
}
shader Complete code :
Shader "Custom/Chapter15-Dissolve"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "while" {
}
_BurnAmount("Burn Amount",Range(0.0,1.0))=0.0// Ablation degree , When it comes to 0 Is melting into 0, It means no ablation , If it melts into 1, It means complete ablation
_LineWidth("Burn Line Width",Range(0.0,0.2))=0.1// The line width of the Scorch effect , That is, the effect width of the scorched edge
_BumpMap("Normal Map",2D)="bump"{
}// Normal texture
// Two colors on the edge of the flame
_BurnFirstColor("Burn First Color",Color)=(1,0,0,1)
_BurnSecondColor("Burn Second Color",Color)=(1,0,0,1)
// Noise texture
_BurnMap("Burn Map",2D)="while"{
}
}
SubShader
{
Tags {
"RenderType"="Opaque" "Queue"="Geometry"}
// Ablation pass
Pass{
Tags{
"LightMode"="ForwardBase"}
// Eliminate and close because ablation will see the interior of the object , If you turn on culling, the interior will not be rendered to
Cull Off
CGPROGRAM
#include "Lighting.cginc"
#include "AutoLight.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
fixed4 _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);
// Transform to get the ray vector in tangent space ,objspacelightdir Is to get the coordinates of light in object space according to the coordinates , Then transform the ray coordinates to get the ray vector in tangent space
TANGENT_SPACE_ROTATION;
o.lightDir =mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;
// From object coordinates to world coordinates
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;
// According to the sampling results from the noise texture and the ablation degree, some pieces are removed and not rendered ,cilp The function is for less than 0 The piece element of , So if the value obtained by sampling is less than burnAmount Will be eliminated ,burnAmount The larger the setting, the more parts will be eliminated , The smaller the setting, the less it will be rejected
clip(burn.r-_BurnAmount);
float3 tangentLightDir =normalize(i.lightDir);
float3 tangentNormal =UnpackNormal(tex2D(_BumpMap,i.uvBumpTex));
// Reflectivity , Reflectance is obtained by sampling from diffuse texture
fixed3 albedo =tex2D(_MainTex,i.uvMainTex).rgb;
// The reflected light is obtained by multiplying the ambient light by the reflectivity
fixed3 ambient =UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
// Diffuse reflection
fixed3 diffuse =_LightColor0.rgb*albedo*max(0,dot(tangentNormal,tangentLightDir));
// According to the degree of ablation 0 The difference between the widest distance to the line , Then calculate the burnt color according to the difference
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);
// Mix the light color and the burnt color according to the difference
fixed3 finalColor =lerp(ambient+diffuse*atten,burnColor,1-t*step(0.0001,_BurnAmount));
return fixed4(finalColor,1.0);
}
ENDCG
}
// The rejected objects are rendering shadows pass It is not eliminated, but only eliminated when rendering the flame color, and then it is not rendered , Therefore, when rendering shadows, shadows will also be rendered to the deleted pieces , This is not correct, so we need to rewrite the shadow pass
Pass{
Tags{
"LightMode"="ShadowCaster"}
CGPROGRAM
#include "unityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
struct 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"
}
边栏推荐
- 做题笔记2(两数相加)
- 飞马D200S无人机与机载激光雷达在大比例尺DEM建设中的应用
- leetcode70假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
- Mysql与Oracle的13点区别
- How to set ticdc synchronization data to only synchronize the specified library?
- 3D modeling tool Archicad 26 newly released
- 获取时间戳的三种方法的效率比较
- SUSE Storage6 环境搭建详细步骤 – Win10 + VMware WorkStation
- In 2020q2, shipments in the global tablet market soared by 26.1%: Huawei ranked third and Lenovo increased the most!
- [deep learning]: day 9 of pytorch introduction to project practice: dropout implementation (including source code)
猜你喜欢

阿里大哥教你如何正确认识关于标准IO缓冲区的问题

MySQL installation tutorial

综合设计一个OPPE主页--页面服务部分

Add differential pairs and connections in Ad

Comprehensively design an oppe homepage -- after sales service of the page

【深度学习】:《PyTorch入门到项目实战》第二天:从零实现线性回归(含详细代码)

阿里云 MSE 支持 Go 语言流量防护

Brother Ali teaches you how to correctly understand the problem of standard IO buffer
![[deep learning]: day 1 of pytorch introduction to project practice: data operation and automatic derivation](/img/4e/a41eee56fc0e8d3089f105bcb63155.png)
[deep learning]: day 1 of pytorch introduction to project practice: data operation and automatic derivation

技术分享 | 误删表以及表中数据,该如何恢复?
随机推荐
Re12:读论文 Se3 Semantic Self-segmentation for Abstractive Summarization of Long Legal Documents in Low
如何使用Fail2Ban保护WordPress登录页面
Is smart park the trend of future development?
SUSE CEPH add nodes, reduce nodes, delete OSD disks and other operations – storage6
Epoll horizontal departure, which edge triggers
Quickly master kotlin set functions
获取时间戳的三种方法的效率比较
Programmers from entry to roast!!!!
深入理解 DeepSea 和 Salt 部署工具 – Storage6
Summary of kubenertes 1.16 cluster deployment problems
【深度学习】:《PyTorch入门到项目实战》第一天:数据操作和自动求导
记录ceph两个rbd删除不了的处理过程
Exercise note 5 (square of ordered array)
Comprehensively design an oppe homepage -- page service part
leetcode9. 回文数
浏览器解码过程分析
Call DLL file without source code
阿里大哥教你如何正确认识关于标准IO缓冲区的问题
Ugui learning notes (II) Scrollview related
Huawei mate 40 series exposure: large curvature hyperboloid screen, 5nm kylin 1020 processor! There will also be a version of Tianji 1000+
