Unity中实现ShaderToy卡通火(原理实现篇)

文章目录


前言

在上一篇文章中,我们实现卡通火ShaderToy到ShaderLab的移植。在这篇文章中,我们来解析一下其原理。

我们在分析时,需要从整体到局部。
依次从输出结果倒着逐步分解,这样对于复杂的效果就不会迷失目标。


一、我们在片元着色器中,实现卡通火的大体框架

  • 在片元着色器中,我们可以看出主要实现的几个功能

    复制代码
      		fixed4 frag(v2f_img i) : SV_Target
              {
                  float2 uv = i.uv;
                  uv.x *= 4.0;
                  float t = _Time.y * 3.0;
                  float3 col = 0;
    
                  float noise = getNoise(uv, t);
                  //shape _CUTOFF to get higher further up the screen
                  _CUTOFF = uv.y;
                  //and at horiz edges
                  _CUTOFF += pow(abs(uv.x * 0.5 - 1.), 1.0);
    
                  //debugview _CUTOFF field
                  //fragColor = float4(float3(_CUTOFF),1.0);   
    
                  if (noise < _CUTOFF)
                  {
                      //black
                      col = 0;
                  }
                  else
                  {
                      //fire
                      float d = pow(getDepth(noise), 0.7);
                      float3 hsv = float3(d * 0.17, 0.8 - d / 4., d + 0.8);
                      col = hsv2rgb(hsv);
                  }
                  
                  return float4(col, 1.0);
              }

1、使用 noise 和 _CUTOFF 判断作为显示火焰的区域

复制代码
if (noise < _CUTOFF)
{
	//black
	col = 0;
}
else
{
	//fire
	float d = pow(getDepth(noise), 0.7);
	float3 hsv = float3(d * 0.17, 0.8 - d / 4., d + 0.8);
	col = hsv2rgb(hsv);
}
  • black部分(当在遮罩范围外时,返回黑色)

  • fire部分(显示 火的形状 和 火的着色)

  1. hsv2rgb : 由 hsv 转化为 RGB 色

  2. hsv : 色相、饱和度、亮度

  3. d :使用pow调节色阶范围

  4. getDepth(noise) : 将黑白灰渐变色分离成几个色阶,以表现卡通效果

2、_CUTOFF : 用于裁剪噪波范围的三角形

  • 可以看出这就是我们卡通火的大概轮廓

    三角形两侧都是 1(白色),在比较时,小于1的部分都显示黑色。

3、noise = getNoise(uv, t); : 噪波函数


二、顺着大体框架依次解析具体实现的功能

复制代码
fixed4 frag(v2f_img i) : SV_Target
{
	float2 uv = i.uv;
	uv.x *= 4.0;
	float t = _Time.y * 3.0;
	float3 col = 0;
	
	float noise = getNoise(uv, t);
	//shape : 模拟出火焰大体形态(三角形)
	 _CUTOFF = uv.y;
	//and at horiz edges
	_CUTOFF += pow(abs(uv.x * 0.5 - 1.), 1.0);
	
	if (noise < _CUTOFF)
	{
		 //black
		col = 0;
	}
	else
	{
		//fire
		//计算得到 火的轮廓
		float d = pow(getDepth(noise), 0.7);
		//计算得到 色相、明度、饱和度
		loat3 hsv = float3(d * 0.17, 0.8 - d / 4., d + 0.8);
		//将 HSV 转化为 RGB
		col = hsv2rgb(hsv);
	}
	                
	return float4(col, 1.0);
}

1、 uv.x *= 4.0; : 使用 uv 在 x 轴上的值,从(0,1)扩大到(0,4)

2、noise = getNoise(uv, t); : 噪波函数

复制代码
//获取整屏的噪波效果
float getNoise(float2 uv, float t)
{
	//given a uv coord and time - return a noise val in range 0 - 1
	//using ashima noise
	
	//add time to y position to make noise field move upwards
	
	float TRAVEL_SPEED = 1.5;
	
	//octave 1
	float SCALE = 2.0;
	float noise = snoise(float3(uv.x * SCALE, uv.y * SCALE - t * TRAVEL_SPEED, 0));
	
	//octave 2 - more detail
	SCALE = 6.0;
	noise += snoise(float3(uv.x * SCALE + t, uv.y * SCALE, 0)) * 0.2;
	
	//move noise into 0 - 1 range    
	noise = (noise / 2. + 0.5);
	
	return noise;
}
  • 使用 TRAVEL_SPEED 、 SCALE 和 t 作为噪波因子,作为影响生成噪波的主要参数

    TRAVEL_SPEED : 流速

    SCALE :缩放

    t :时间

  • snoise(float3(x,y,z)); : 噪波生成算法(这里使用了 ashima noise 算法)

  • Perlin噪声与Simplex噪声笔记

    这里引用一篇别人的笔记,作为该噪音函数的讲解

  1. 第一次噪波效果
  2. 第二次噪波效果
  3. 两次噪波叠加效果
  4. noise = (noise / 2. + 0.5);将噪波归一化(0,1):

3、_CUTOFF : 用于裁剪噪波范围的三角形

  • _CUTOFF = uv.y;

  • uv.x * 0.5 - 1 :值域范围改变(0,4)-> (0,2) -> (-1,-1)

  • abs(uv.x * 0.5 - 1.); : 使值域变成对称的样子 (-1,-1) -> (1,0)、(0,1)

  • pow(abs(uv.x * 0.5 - 1.), 2.0);可以用指数函数来控制三角形状的边扭曲程度

  • uv.y + pow(abs(uv.x * 0.5 - 1.), 1.0) :实现三角形状

4、卡通火形状部分

  • 将黑白灰渐变色分离成几个色阶,以表现卡通效果

//计算得到 火的轮廓

float d = pow(getDepth(noise), 0.7);

复制代码
// 将黑白灰渐变色分离成几个色阶,以表现卡通效果
float getDepth(float n)
{
	//given a 0-1 value return a depth,
	
	//remap remaining non-_CUTOFF region to 0 - 1
	//实现边缘虚化的作用,把原本尖锐的边缘 ,变得虚化柔和
	float d = (n - _CUTOFF) / (1. - _CUTOFF);
	return d;
	//色调分离
	d = floor(d * _Steps) / _Steps;
	return d;
}
  • n - _CUTOFF 的效果

  • float d = (n - _CUTOFF) / (1. - _CUTOFF); :边缘虚化,归一化后

  • 色调分离 : d = floor(d * _Steps) / _Steps;
    把上面计算的结果,进行乘以一个较大的数,使用向下取整后,再除以这个较大的数归一化。以达到卡通效果中,一块一块的色调效果

  • pow 用来调节亮度

5、卡通火颜色部分

  • hsv 是一种符合 人眼 对颜色感性认识 的特征值

计算得到 色相、饱和度、亮度。这里的特征值是 计算出符合 火的颜色区间范围。
其他效果的颜色,需要自己调整寻找颜色算法

float3 hsv = float3(d * 0.17, 0.8 - d / 4., d + 0.8);

  • rgb 是一种符合 计算机硬件设计 的 颜色设计。使用算法把 hsv 转化到 rgb

float3 hsv2rgb(float3 c)

{

float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);

float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);

return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);

}

这里给出taecg老师总结的 HSV 2 RGB 算法链接

相关推荐
叶帆2 天前
【YFIOs】用C#开发硬件之设备上云
开发语言·unity·c#
久数君2 天前
AI三维建模工具“造形家”:地理场景三维化的高效解决方案
unity·glb·ai算法·ai三维建模工具·地图框选·造形家·城市建筑模型
会思考的猴子3 天前
Unity VFX 属性 Postion 和 TargetPostion
unity
hai3152475433 天前
九章编程法 · 猜数字游戏 (GW-BASIC 重构版) *
人工智能·microsoft·游戏引擎·游戏程序
心前阳光3 天前
Unity资源导入之自动化资源导入
unity·自动化·游戏引擎
心前阳光3 天前
Unity之2021.3.45f2c1发布安卓程序遇到的问题
android·unity·游戏引擎
纪纯3 天前
PicoVR Unity Integration SDK 3.4 常用交互API
unity·游戏引擎·vr·pico
龙智DevSecOps解决方案3 天前
3A 游戏优化技术栈:如何打通引擎级分析工具与 DevOps 持续集成管线?
unity·性能优化·游戏开发·技术美术·perforce·unrealengine
葛兰岱尔3 天前
从 SolidWorks 到 Three.js,从 Inventor 到 Unity——制造业CAD模型“几何-语义一体化“转换,不再是天方夜谭!
开发语言·javascript·unity
鼎艺创新科技3 天前
三维电子沙盘中OSGB倾斜摄影数据的加载与渲染
游戏引擎·cocos2d