当前位置:网站首页>Unity中SmoothStep介绍和应用: 溶解特效优化
Unity中SmoothStep介绍和应用: 溶解特效优化
2022-07-07 09:59:00 【拂面清风三点水】
Unity中SmoothStep介绍和应用: 溶解特效优化
上一篇文章使用RampTex来给溶解特效附加了一个差强人意的边缘颜色变化, 这一篇文章我们来做进一步优化, 并对相应的原理做一些简单的分析.
今天主要的内容如下:
- 差值函数介绍:
smoothstep
- 使用差值函数来改善溶解特效
- 实现方案的进一步优化
老规矩, 先来看看最终效果:
差值函数介绍: smoothstep
smoothstep(edge_low, edge_up, x)
函数:
[edge_low, edge_up]
是指定的一个差值范围x
是任意实数- 函数结果是:
if x < edge_low; return 0
.if x > edge_up; return 1
.- 如果
x
处于edge_low
和edge_up
之间, 则返回x
在[0, 1]
范围内的映射值- 比如指定范围是
[0, 10]
,x=5
, 我们我们将其映射到[0, 1]
之后, 对应的映射值为0.5
- 比如指定范围是
[0, 100]
,x=5
, 我们我们将其映射到[0, 1]
之后, 对应的映射值为0.05
- 实质上是将
[edge_low, edge_up]
映射到[0, 1]
, 然后找到x
在[0, 1]
中的映射值 - 注意这里说明使用的是线性映射, 而
smoothstep
使用的不是线性映射, 而是线性映射之后使用曲线来光滑过后的结果, 这个结果和线性映射差别不大, 我们可以简单的使用线性映射来理解
- 比如指定范围是
x
处于范围之内的函数的映射为:- 线性映射函数为: k 1 = ( x − a ) ( b − a ) { a < = x < = b } k_{1}=\dfrac{(x-a)}{(b-a)}\{a<=x<=b\} k1=(b−a)(x−a){ a<=x<=b}
- 先进行线性映射, 获得
x
的映射值 k 1 k_1 k1 - 然后使用曲线来光滑这个值: k = 3 k 1 2 − 2 k 1 3 { a < x < b } k=3k_{1}^{2}-2k_{1}^{3}\{a<x<b\} k=3k12−2k13{ a<x<b}
代码实现如下:
float smoothstep (float edge0, float edge1, float x)
{
if (x < edge0)
return 0;
if (x > edge1)
return 1;
// 线性映射
x = (x - edge0) / (edge1 - edge0);
// 光滑
return x * x * (3 - 2 * x);
}
上图展示的就是使用smoothstep
函数作的图, 其中指定的范围是[a=1, b=a+1=2]
, 可以看到:
- 当
x <= 1
时, 函数值为0
- 当
x >= 2
时, 函数值为1
- 当
1 < x < 2
时, 函数值为被缩放到了[0, 1]
之间- 蓝色的线条是线性映射
- 红色的线条是线性映射之后使用曲线作平滑后的结果
- 总体上来说两者差别不大
使用差值来改善方案
我们这次不设定有多少圈层, 完全根据RampTex的渐变层级来, RampTex有多少层, 我们的边缘就有多少层.
也就是说, 我们需要将RampTex映射到溶解边缘那一小块区域, 就像我们上篇文章指定的0~0.12
范围.
再简单说, 就是要将RampTex画到这个0~0.12
范围内.
这里的实现我们使用0~0.1
.
使用数学的说法, 就是要将处于[0, 1]
这个范围内的噪声纹理值, 映射到边缘的[0, 0.1]
之内, 结合上面介绍的差值函数, 我们的目标是:
// x=dissolveCol.r, {0<=x<=1}
smoothsetp(0, 0.1, x)
当然, 这里的[0, 0.1]
并不是溶解边缘.
在之前的文章中, 我们介绍过, dissolveCol.r < _DissolveThreshold
就是溶解像素, 那么从dissolveCol.r == _DissolveThreshold
开始的像素就是溶解边缘的像素, 至于说到底要多大的范围, 就需要我们自己指定, 比如这里的值为0.1
.
所以我们的调用需要优化为:
// x=dissolveCol.r, 采样自噪声纹理, {0<=x<=1}
// a=_DissolveThreshold, b=_DissolveThreshold+0.1
// 其中a为发生溶解的下界, b为溶解像素边缘宽度
y = smoothsetp(a, b, x)
上面调用的意思是:
我们需要将溶解值x
, 通过差值函数处理过后得到新的采样坐标y
, 通过y
在RampTex上采样颜色后附加到对象的原色上:
x < a
: 此像素发生溶解, 抛弃, 不用理会x > b
: 此时函数返回1
, 在RampTex上采样到黑色, 因为黑色值为0, 相当于最后这个像素保持原色a < x < b
: 此时函数返回经过线性映射和光滑处理后的新的处于[0, 1]
之间的映射值- 这里要注意的是: 由于
x
本身就处于[0, 1]
之间, 经过处理的结果y
依然处理[0, 1]
之间, 所以及其容易造成误解, 需要好好体会 - 这里的条件都没有涉及到
=
的部分, 因为在shader中处理边界, 一个像素的差异不大, 而且可能会有性能差异
- 这里要注意的是: 由于
经过这个小小的处理后, 我们就可以在边缘位置的指定宽度像素内, 完整的画出RampTex代表的颜色.
实现方案的进一步优化
你以为这样就完了? 天真…
因为RampTex实质上是一个一维纹理, 所以我们的第一个优化就是, 更换其采样器, 这样可以提高一定的性能:
sampler _RampTex;
fixed4 rampColor = tex1D(_RampTex, smoothstep(_DissolveThreshold, _DissolveThreshold + 0.1, dissolveCol.r));
第二个优化其实在上面已经提到了, 我们在差值时, 使用了smoothstep
函数, 这个函数会先进行线性映射, 然后再进行曲线圆滑, 而曲线圆滑之后的结果和线性映射的结果其实差别不大, 但是却多了一个曲线计算, 所以我们在要求不是特别高的情况下, 只使用线性映射的结果即可.
首先我们要介绍一下函数: saturate
.
saturate(x)
函数的作用是:
- 当
x <= 0
时, 函数值为0
- 当
x >= 1
时, 函数值为1
- 当
0 < x < 1
时, 返回x
然后实现线性映射和使用saturate
:
// k = (x - a) / (b - a);
k = (dissolveCol.r - _DissolveThreshold) / (_DissolveThreshold + 0.1 - _DissolveThreshold);
fixed4 rampColor = tex1D(_RampTex, saturate(k));
好啦, 至此我们的溶解特效算是介绍完了.
下面是完整的代码:
Shader "Dissolve"
{
Properties
{
[NoScaleOffset]_MainTex ("Texture", 2D) = "white" {}
_DissolveTex("DissolveTex", 2D) = "white" {}
_DissolveThreshold("DissolveThreshold", Range(0, 1)) = 0
_RampTex("RampTex", 2D) = "" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _DissolveTex;
float4 _DissolveTex_ST;
fixed _DissolveThreshold;
sampler _RampTex;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
o.uv.xy = v.uv;
o.uv.zw = TRANSFORM_TEX(v.uv, _DissolveTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 dissolveCol = tex2D(_DissolveTex, i.uv.zw);
// 从噪声纹理采样颜色, 如果该值[小于阈值]则丢弃本片段
// 比如阈值为0.1, 则在噪声纹理上采样的所有像素r值小于0.1的片段都会被丢弃
// 即噪声纹理上偏黑的颜色(->0)首先开始溶解, 偏白的颜色(->1)最后溶解
clip(dissolveCol.r - _DissolveThreshold);
// fixed4 rampColor = tex1D(_RampTex, smoothstep(_DissolveThreshold, _DissolveThreshold + 0.1, dissolveCol.r));
// k = (x-a)/(b-a);
// k = (dissolveCol.r - _DissolveThreshold) / (_DissolveThreshold + 0.1 - _DissolveThreshold)
fixed4 rampColor = tex1D(_RampTex, saturate((dissolveCol.r - _DissolveThreshold) * 10));
fixed4 col = tex2D(_MainTex, i.uv.xy);
col += rampColor;
return col;
}
ENDCG
}
}
}
总结
经过三篇文章的介绍, 我们算是完全认识了Unity中的溶解特效是如何制作和优化的过程.
在整个过程中, 我们不止了解到了溶解特效本身的原理, 还通过这个特效, 接触到了在Shader中比较常见的噪声纹理和渐变纹理, 并使用这两个工具来对溶解特效进行了比较好的优化.
同时我们还分析了Shader中常用的smoothstep
函数和saturate
函数, 总之就是收获满满.
好了, 今天的内容就是这些, 希望对大家有所帮助.
边栏推荐
- Talk about SOC startup (VII) uboot startup process III
- 【最短路】Acwing1128信使:floyd最短路
- 【数据聚类】基于多元宇宙优化DBSCAN实现数据聚类分析附matlab代码
- 正在运行的Kubernetes集群想要调整Pod的网段地址
- [extraction des caractéristiques de texture] extraction des caractéristiques de texture de l'image LBP basée sur le mode binaire local de Matlab [y compris le code source de Matlab 1931]
- 《论文阅读》Neural Approaches to Conversational AI(1)
- 110.网络安全渗透测试—[权限提升篇8]—[Windows SqlServer xp_cmdshell存储过程提权]
- Various uses of vim are very practical. I learned and summarized them in my work
- . Net Maui performance improvement
- Blog moved to Zhihu
猜你喜欢
The annual salary of general test is 15W, and the annual salary of test and development is 30w+. What is the difference between the two?
Time bomb inside the software: 0-day log4shell is just the tip of the iceberg
Talk about SOC startup (x) kernel startup pilot knowledge
聊聊SOC启动(七) uboot启动流程三
Test the foundation of development, and teach you to prepare for a fully functional web platform environment
In SQL, I want to set foreign keys. Why is this problem
【滤波跟踪】基于matlab捷联惯导仿真【含Matlab源码 1935期】
OneDNS助力高校行业网络安全
一起探索云服务之云数据库
Rationaldmis2022 array workpiece measurement
随机推荐
Automated testing framework
[encapsulation of time format tool functions]
Some opinions and code implementation of Siou loss: more powerful learning for bounding box regression zhora gevorgyan
Nuclear boat (I): when "male mothers" come into reality, can the biotechnology revolution liberate women?
聊聊SOC启动(七) uboot启动流程三
Stm32f1 and stm32subeide programming example -max7219 drives 8-bit 7-segment nixie tube (based on SPI)
Enclosed please find. Net Maui's latest learning resources
清华姚班程序员,网上征婚被骂?
SwiftUI Swift 内功之 Swift 中使用不透明类型的 5 个技巧
Software design - "high cohesion and low coupling"
Camera calibration (2): summary of monocular camera calibration
powershell cs-UTF-16LE编码上线
【滤波跟踪】基于matlab捷联惯导仿真【含Matlab源码 1935期】
聊聊SOC启动(九) 为uboot 添加新的board
超标量处理器设计 姚永斌 第9章 指令执行 摘录
Flet教程之 18 Divider 分隔符组件 基础入门(教程含源码)
Talk about SOC startup (VI) uboot startup process II
When sink is consumed in mysql, the self incrementing primary key has been set in the database table. How to operate in Flink?
.NET MAUI 性能提升
sql里,我想设置外键,为什么出现这个问题