Article directory
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)
-
hsv2rgb: Convert hsv to RGB color
-
hsv: hue, saturation, brightness
-
d: Use pow to adjust the color scale range
-
getDepth(noise): Separate the black, white and gray gradient colors into several color levels to show the cartoon effect
2. _CUTOFF: Triangle used to cut the noise range
- You can see that this is the rough outline of our cartoon fire
Both sides of the triangle are 1 (white). When compared, the parts less than 1 show black.
3. noise = getNoise(uv, t); : noise function
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)
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
- First noise effect
- Second noise effect
- The superposition effect of two noises
- noise = (noise / 2. + 0.5); normalize the noise (0,1):
3. _CUTOFF: Triangle used to cut the noise range
-
_CUTOFF = uv.y;
-
uv.x * 0.5 - 1: The value range changes (0,4)-> (0,2) -> (-1,-1)
-
abs(uv.x * 0.5 - 1.); : Make the value range symmetrical (-1,-1) -> (1,0), (0,1)
-
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
-
uv.y + pow(abs(uv.x * 0.5 - 1.), 1.0): achieve triangle shape
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
-
float d = (n - _CUTOFF) / (1. - _CUTOFF); : edge blur, after normalization
-
Posterization: d = floor(d * _Steps) / _Steps;
把上面计算的结果,进行乘以一个较大的数,使用向下取整后,再除以这个较大的数归一化。以达到卡通效果中,一块一块的色调效果
-
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);
- 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