Tell you the truth, I was moved to tears this effect

The original article first appeared in the public micro-channel number: Byte flow

Beating heart

Beating heart

When browsing blog, accidentally discovered the "beating heart" effect, instantly moved, and that this effect is achieved when using pure code (GLSL realize), and indeed they were scared to.

The original source of the retroactive effects, eventually see the site in SahderToy its original implementation, the other in the SahderToy also found countless similar amazing special effects, and the effects of the implementation code completely open.

ShaderToy is a cross-browser online community, and is a tool for creating and sharing through WebGL shaders used for learning in a Web browser and professors 3D computer graphics.
ShaderToy

SahderToy browse a lot on the site, it feels as if discovered the New World, the site supports online GLSL write and run scripts , called Github GL community.

We put on the site, "beating heart" of special effects script is converted to the corresponding OpenGLES GLSL script to run on the phone, and one by one the entire script parses, complete code is as follows:

#version 300 es
precision highp float;
layout(location = 0) out vec4 outColor;//输出
uniform float u_time;//时间偏移量
uniform vec2 u_screenSize;//屏幕尺寸
const float PI = 3.141592653;
void main()
{
// move to center
vec2 fragCoord = gl_FragCoord.xy;
vec2 p = (2.0*fragCoord-u_screenSize.xy)/min(u_screenSize.y,u_screenSize.x);

// background color
vec3 bcol = vec3(1.0,0.8,0.8)*(1.0-0.38*length(p));

// animate
float tt = u_time;
float ss = pow(tt,.2)*0.5 + 0.5;
ss = 1.0 + ss*0.5*sin(tt*6.2831*3.0 + p.y*0.5)*exp(-tt*4.0);
p *= vec2(0.5,1.5) + ss*vec2(0.5,-0.5);

// shape
p.y -= 0.25;
float a = atan(p.x,p.y) / PI;
float r = length(p);
float h = abs(a);
float d = (13.0*h - 22.0*h*h + 10.0*h*h*h)/(6.0-5.0*h);

// color
float s = 0.75 + 0.75*p.x;
s *= 1.0-0.4*r;
s = 0.3 + 0.7*s;
s *= 0.5+0.5*pow( 1.0-clamp(r/d, 0.0, 1.0 ), 0.1 );
vec3 hcol = vec3(1.0,0.5*r,0.3)*s;

vec3 col = mix( bcol, hcol, smoothstep( -0.06, 0.06, d-r) );

outColor = vec4(col,1.0);
}

About built-in variables gl_FragCoord, from the old paper we know: the screen space coordinates of the associated viewing zone is provided by a viewport function glViewport given function, and can be accessed by the fragment shader built gl_FragCoord variables, gl_FragCoordx and y represent the segment screen space coordinates ((0,0) in the lower left corner), which range depends on the glViewport function, the spatial coordinate origin at the lower left corner of the screen.

Section of code below the main role is to adjust the coordinate system, the origin of the screen coordinate system to move from the center of the lower left corner, so that all of the vector fragment are gl_FragCoord.xy center of the screen as a starting point, the vector p is the screen center and screen coordinates of the pixel between the direction vector.

// move to center
vec2 fragCoord = gl_FragCoord.xy;
vec2 p = (2.0 * fragCoord - u_screenSize.xy) / min(u_screenSize.y,u_screenSize.x);

Direction vector between the center of the screen and the screen coordinates of the pixel (FIG networks, invasion deleted)

接下来计算背景颜色,length(p) 表示计算当前片元(像素)与屏幕中心点的距离,背景颜色以 vec3(1.0,0.8,0.8) 该颜色为基础,距离屏幕越远颜色越暗。

// background color
vec3 bcol = vec3(1.0,0.8,0.8)*(1.0-0.38*length(p));

这时,我们把背景颜色渲染出来看看:
Background Color Rendering

接着绘制心形,主要利用反正切函数值和当前片元(像素)与屏幕中心点的距离相比较,来确定心形状的边界。GLES 中的反正切函数 atan(p.x,p.y) 取值范围是[-π, π],然后除以 PI 后,取值范围变成了 [-1, 1] 。

// shape
p.y -= 0.25;//向屏幕下方偏移 0.25 个单位
float a = atan(p.x,p.y) / PI;
float r = length(p);
float h = abs(a);//取绝对值
//float d = (13.0*h - 22.0*h*h + 10.0*h*h*h)/(6.0-5.0*h);//这个函数主要使心的形状更加扁平化,暂时先忽略

Drawing Heart

我们通过上图来理解心形的绘制过程,每条直线上像素点得到的 a 值都是相同的,我们用黄点表示距离屏幕中心的远近,然后通过 d-r 的值来确定心形的边界。

vec3 col = mix(bcol, hcol, smoothstep( -0.06, 0.06, d-r) );

以上是绘制心形的关键函数,hcol 是心的颜色,bcol 是背景色。

smoothstep

smoothstep 是一个很常用的平滑过渡函数,当第三个参数比 -0.06 小时,返回 0,比0.06 大时返回 1 ,如果在 -0.06 和 0.06 之间,则返回 0 到 1 之间的值,其作用是用于平缓 d-r 的值在正负交界处的突变。

opengl mix

mix函数用于加权混合心的颜色和背景色,根据 smoothstep 函数特性,在心形内用心的颜色,在心形外用背景色,而边界则是两种颜色之间的模糊过渡。

再说说心形扁平化函数的作用,当我们不使用扁平化函数,而是直接用 h-r 来控制心的形状,得到的图像是一个又胖又肥的心形,这样你大概可以得知这个函数的作用。

//float d = (13.0*h - 22.0*h*h + 10.0*h*h*h)/(6.0-5.0*h);//这个函数主要使心的形状更加扁平化,暂时先忽略
vec3 col = mix(bcol, hcol, smoothstep( -0.06, 0.06, h-r) );

Obtained without using the heart-shaped flattened function

然后看看心的颜色生成,由表达式 vec3(1.0,0.5*r,0.3) 可以看出心的颜色是红色,且由屏幕中心向四周红色逐渐减弱,然后产生一系列渐变,最后分出心形内外的区域颜色。

// color
float s = 0.75 + 0.75*p.x;//在 x 轴方向有一个渐变
s *= 1.0-0.4*r;//根据距离产生渐变
s = 0.3 + 0.7*s;//增亮了左侧暗部区域
s *= 0.5+0.5*pow( 1.0-clamp(r/d, 0.0, 1.0 ), 0.1 );//借助变量 r/d 分出了心形内外的区域颜色
vec3 hcol = vec3(1.0,0.5*r,0.3)*s;

我们直接输出心的颜色 hcol ,看看是什么效果:

Color output hcol heart

最后是跳动效果的实现,其原理就是对屏幕像素在 x、y 方向进行周期性偏移,偏移幅度由特殊的函数来控制。

// animate
float tt = u_time;//u_time 为周期性输入的时间
float ss = pow(tt,.2)*0.5 + 0.5;
ss = 1.0 + ss*0.5*sin(tt*6.2831*3.0 + p.y*0.5)*exp(-tt*4.0);//控制幅度的函数
p *= vec2(0.5,1.5) + ss*vec2(0.5,-0.5);

我们通过下面的代码控制输入时间周期为 2000ms 。

float time = static_cast<float>(fmod(GetSysCurrentTime(), 2000) / 2000);
glUniform1f(m_TimeLoc, time);

振幅控制函数的模拟曲线如下图所示,大致可以看出心跳动时幅度变化情况。
Analog amplitude control function curve (FIG networks, invasion deleted)

The last thing to note is that the script GLSL accuracy of the statement, the text of the code we use is highpaccuracy, but when mediumpthe time precision, burr occurs due to the lack of precision caused, as shown below:

Due to lack of precision burr

Path implementation code:
NDK_OpenGLES_3_0

reference

https://www.shadertoy.com/view/XsfGRn
https://blog.csdn.net/candycat1992/article/details/44040273

Contacts and exchanges

My public number

Published 56 original articles · won praise 20 · views 210 000 +

Guess you like

Origin blog.csdn.net/Kennethdroid/article/details/104536532