How to implement this tunnel like animation in WebGL? [closed]

最后都变了- 提交于 2019-12-03 09:50:14

问题


How to implement this tunnel like animation in WebGL?

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

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


回答1:


Well, this was fun. :)

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

(EDIT: no longer available, but I created a ShaderToy version: https://www.shadertoy.com/view/XdKfRD)

The main algorithm is in the fragment shader. The basic idea is a for loop iterating over the black rings/circles, from large to small, also offsetting the center to produce a tunnel-like effect.

Given any pixel, we can check if the pixel is close enough to the ring to be a candidate for a black pixel or not. If it is outside the ring, break the loop to avoid seeing smaller rings through the larger ones.

The distance from the previous (outer) circle is used to "squeeze" the pattern together when rings are close, this helps create the illusion of a 3D surface.

The wavy pattern of each ring is of course a sine curve. The angle of the pixel (compared to the circle center) is combined with a uniform time parameter to animate the wavy pattern for each ring.

And finally, there was lots of experimentation with different parameters and transformation functions like pow() to get the result close to the target animation. It's not perfect, but pretty close.

The fragment shader 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;
}


来源:https://stackoverflow.com/questions/5451376/how-to-implement-this-tunnel-like-animation-in-webgl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!