Implementing ShaderToy cartoon fire in Unity (principle implementation)


Preface

In the previous article, we implemented the transplantation of Cartoon Fire ShaderToy to ShaderLab. In this article, we will analyze how it works.

When we analyze, we need to move from the whole to the part.
Decompose it step by step from the output results backwards, so that you will not lose sight of complex effects.


1. We implement the general framework of cartoon fire in the fragment shader

  • In the fragment shader, we can see several main functions implemented
			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. Use noise and _CUTOFF to determine the area where the flame is displayed.

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 part (when outside the mask range, return black)

  • fire section (shows fire shape and fire coloring)

  1. hsv2rgb: Convert hsv to RGB color
    Please add image description

  2. hsv: hue, saturation, brightness
    Please add image description

  3. d: Use pow to adjust the color scale range

  4. getDepth(noise): Separate the black, white and gray gradient colors into several color levels to show the cartoon effect
    Please add image description

2. _CUTOFF: Triangle used to cut the noise range

  • You can see that this is the rough outline of our cartoon fire
    Insert image description here
    Both sides of the triangle are 1 (white). When compared, the parts less than 1 show black.

3. noise = getNoise(uv, t); : noise function

Please add image description


2. Analyze the specific functions implemented along the general framework.

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; : Use the value of uv on the x-axis to expand from (0,1) to (0,4)

Insert image description here

Insert image description here

2. noise = getNoise(uv, t); : noise function

//获取整屏的噪波效果
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;
}
  • Use TRAVEL_SPEED, SCALE and t as noise factors as the main parameters affecting the generated noise
    TRAVEL_SPEED: flow rate
    SCALE: scaling a>
    t: time

  • snoise(float3(x,y,z)); : Noise generation algorithm (ashima noise algorithm is used here)

  • Perlin noise and Simplex noise notes
    Here is a quote from someone else’s notes as an explanation of the noise function

  1. First noise effect
    Please add image description
  2. Second noise effect
    Please add image description
  3. The superposition effect of two noises
    Please add image description
  4. noise = (noise / 2. + 0.5); normalize the noise (0,1):
    Please add image description

3. _CUTOFF: Triangle used to cut the noise range

  • _CUTOFF = uv.y;
    Insert image description here

  • uv.x * 0.5 - 1: The value range changes (0,4)-> (0,2) -> (-1,-1)
    Insert image description here

  • abs(uv.x * 0.5 - 1.); : Make the value range symmetrical (-1,-1) -> (1,0), (0,1)
    Insert image description here

  • pow(abs(uv.x * 0.5 - 1.), 2.0); You can use an exponential function to control the degree of side distortion of the triangle shape
    Insert image description here

  • uv.y + pow(abs(uv.x * 0.5 - 1.), 1.0): achieve triangle shape
    Insert image description here

4. Cartoon fire shape part

  • Separate the black, white and gray gradient colors into several color levels to express a cartoon effect

//Calculate the outline of the fire
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 - the effect of _CUTOFF
    Insert image description here

  • float d = (n - _CUTOFF) / (1. - _CUTOFF); : edge blur, after normalization
    Insert image description here

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

  • pow is used to adjust brightness

5. Cartoon fire color part

  • hsv is a characteristic value that conforms to the human eye's perceptual understanding of color.

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

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

Please add image description

  • RGB is a color design that conforms to computer hardware design. Convert hsv to rgb using algorithm

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);
}

Here is the HSV 2 RGB algorithm link summarized by taecg teacher

Guess you like

Origin blog.csdn.net/qq_51603875/article/details/134935341