How to implement this tunnel, for example, animation in WebGL?

How to implement this tunnel as animation in WebGL?

enter image description here

Source: http://dvdp.tumblr.com/

See also: How to implement this spinning spiral in WebGL?

+7
source share
1 answer

Well, that was fun. :)

WebGL demo is available here: http://boblycat.org/~knute/webgl/tunnel/

The main algorithm is in the fragment shader. The main idea is a loop cycle, repeating in black rings / circles, from large to small, also a compensating center for creating a tunnel effect.

For any pixel, we can check if the pixel is enough for the ring to be a candidate for a black pixel or not. If it is outside the ring, break the loop so as not to see the smaller rings through the larger ones.

The distance from the previous (outer) circle is used to “squeeze” the picture together, when the rings are close, this helps create the illusion of a three-dimensional surface.

The wavy structure of each ring is, of course, sinusoidal. The pixel angle (compared to the center of the circle) is combined with a single time parameter for animating the wave pattern for each ring.

And finally, there were many experiments with various parameters and conversion functions, such as pow (), to get a result close to the target animation. It is not perfect, but pretty close.

Shader Fragment Code:

#ifdef GL_ES precision highp float; #endif const float PI = 3.14159265358979323846264; const float TWOPI = PI*2.0; const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0); const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0); const vec2 CENTER = vec2(0.0, 0.0); const int MAX_RINGS = 30; const float RING_DISTANCE = 0.05; const float WAVE_COUNT = 60.0; const float WAVE_DEPTH = 0.04; uniform float uTime; varying vec2 vPosition; void main(void) { float rot = mod(uTime*0.0006, TWOPI); float x = vPosition.x; float y = vPosition.y; bool black = false; float prevRingDist = RING_DISTANCE; for (int i = 0; i < MAX_RINGS; i++) { vec2 center = vec2(0.0, 0.7 - RING_DISTANCE * float(i)*1.2); float radius = 0.5 + RING_DISTANCE / (pow(float(i+5), 1.1)*0.006); float dist = distance(center, vPosition); dist = pow(dist, 0.3); float ringDist = abs(dist-radius); if (ringDist < RING_DISTANCE*prevRingDist*7.0) { float angle = atan(y - center.y, x - center.x); float thickness = 1.1 * abs(dist - radius) / prevRingDist; float depthFactor = WAVE_DEPTH * sin((angle+rot*radius) * WAVE_COUNT); if (dist > radius) { black = (thickness < RING_DISTANCE * 5.0 - depthFactor * 2.0); } else { black = (thickness < RING_DISTANCE * 5.0 + depthFactor); } break; } if (dist > radius) break; prevRingDist = ringDist; } gl_FragColor = black ? BLACK : WHITE; } 
+26
source

All Articles