Updated: September 2, 2021.
Github source code: [Click me to get the source code]
shiny
Shiny implements LOGO streamer in another way , which is almost the same in terms of performance. The difference is that Shiny does not need to specify streamer masks and flowing image textures. It directly calculates an area through UV, and then superimposes colors , in order to achieve a shiny effect, the final rendering:
Idea analysis
First of all, according to the idea of LOGO streamer, we only need to select a uv area, superimpose color for the area, and make 区域的中心越亮
, more and more 区域的两边扩散越暗
, without considering the animation, this is the area shining effect we want.
Because we want to give people a feeling of passing by, so we 闪亮区域
must be up and down (moving left and right) or left and right (moving up and down), as in the final rendering above, the shiny area is up and down, Move left and right to achieve a flashing effect.
shiny area
Let's first consider the way of connecting up and down. In our vision, our shiny area is as follows:
We will 闪亮的值
imagine that 0-1
the center of the area is 1 (the brightest), and the more it spreads to the two sides, the closer it is to 0 (the darkest): According to experience, it is
obvious that we can use the abscissa of uv to convert to 闪亮的值
, known uv The range of abscissa values is as follows:
Then our algorithm is clear, that is, to [0,1](也即是uv.x的取值范围)
map intervals to intervals [0,1,0]
!
First, intervals [0,1]
can be further abstracted into intervals [0,0.5,1]
, and then, first [0,0.5,1]
map intervals to intervals [-1,0,1]
:
//将区间[0,0.5,1],映射到区间[-1,0,1]
half value1 = uv.x * 2 - 1;
Then [-1,0,1]
map the interval to the interval by taking the absolute value [1,0,1]
:
//将区间[0,0.5,1],映射到区间[-1,0,1]
half value1 = uv.x * 2 - 1;
//将区间[-1,0,1],映射到区间[1,0,1]
half value2 = abs(value1);
Finally, [1,0,1]
invert the interval to get our desired result, the interval [0,1,0]
:
//将区间[0,0.5,1],映射到区间[-1,0,1]
half value1 = uv.x * 2 - 1;
//将区间[-1,0,1],映射到区间[1,0,1]
half value2 = abs(value1);
//倒置区间[1,0,1],得到区间[0,1,0]
half value3 = 1 - value2;
At this point, the obtained value value3
is returned according to the current input uv.x value. 闪亮的值
The range of this value is, [0,1,0]
the middle is bright, and the two sides are dark.
softness
value3 [0,1,0]
We can smooth the values as follows to get power
our final shiny intensity:
//通过smoothstep将区间[0,1,0]平滑,得到闪亮强度power
half power = smoothstep(0, softness, value3);
Here softness
is the value of the external input, we named it 柔和度
, why can the softness achieve a smooth effect? Let's look at the picture and analyze it.
First, when the softness is the lowest (0.01 here), the effect is 完全不柔和
:
Let's analyze the formula and bring the softness 0.01 into:
//通过smoothstep将区间[0,1,0]平滑,得到闪亮强度power
half power = smoothstep(0, 0.01, value3);
According to smoothstep
the characteristics of the method, as long as the input value3
value is greater than 0.01, it will return 1, given power
, that is to say, after a round of smoothing operations, our original value3
interval:
//扩大区间细节
[0,0.1,0.2,...0.9,1,0.9...0.2,0.1,0]
is converted to power
an interval:
//扩大区间细节
[0,1,1,...1,1,1...1,1,0]
That is to say, the extremely low softness makes all 稍微有一点亮度
places become soft 亮度为1
, which is the embodiment of softness.
Conversely, when the softness is the highest (1 here), the smoothing operation will return to value3
the original value, which itself is an interval with a smoothing effect:
Gloss
From the above, we have obtained the shiny brightness value power
. We multiply the brightness value by one 颜色
to get our final shiny color. Here, we introduce a control parameter again 光泽度
, and use it to control our output color value :
//通过光泽度插值得到闪光颜色shinyColor
half3 shinyColor = lerp(fixed3(1, 1, 1), color.rgb, gloss);
//输出最终颜色(原始颜色 + 闪亮颜色)
color.rgb += shinyColor * power;
The glossiness here is interpolated gloss
between 纯白色
and 本身颜色
, when the glossiness is 0, the shiny color is white (the values of the three channels are equal, power
and some values will be reduced after multiplication), RGB
after the three channels of any color are accumulated with the same value, and It will not change it 色相
, that is to say, whether it should be red or red , should it be blue or blue , it just looks a little brighter overall, without gloss:
when the glossiness is 1, the shiny color is itself Color, its own color is added to its own color, which makes the red place redder , and the blue place bluer , very shiny:
to rotate
According to the above calculation, what we get is only a vertical one that runs through up and down 闪亮区域
. We want to rotate it arbitrarily, for example, as follows: this
:
this:
how to achieve such an effect?
Let's recall the algorithm for obtaining the shiny area at the beginning: input a uv coordinate, and obtain the coordinate point according to the x value of the uv coordinate 闪亮颜色强度
.
We want to achieve the rotation effect, and then input the uv coordinates directly 旋转
, the output is not the real coordinate point of the uv coordinates 闪亮颜色强度
!
So the algorithm is very simple, just do it, just turn uv:
//uv旋转
uv = RotatePoint2(uv, float2(0.5, 0.5), radians(_Rotation));
The function of RotatePoint2 is to rotate the input two-dimensional point along a specified arc along a circle center:
//将二维顶点point2,沿着圆心center,顺时针旋转radian弧度
float2 RotatePoint2(float2 point2, float2 center, half radian)
{
half radius = distance(point2, center);
half angle = atan((point2.y - center.y) / (point2.x - center.x)) - radian;
point2.x = cos(angle) * radius + center.x;
point2.y = sin(angle) * radius + center.y;
return point2;
}
The algorithm is also a trigonometric function, so I won't explain it in detail here.
shiny animation
To achieve shiny animation, we only need to change _Position
the value of the input, without using _Time
parameters, directly add an animation player:
At this point, this simple shiny effect is basically over. If there is still something you don’t understand, you can refer to the source code. Of course, the code in the blog may not be exactly the same as the source code. The original intention here is just to introduce ideas. In the specific implementation process There may be some statement optimization, of course the core algorithm is the same.