如何在WebGL中实现类似隧道的动画?

8

2
(看了30秒后呕吐) - Blender
尝试使用投影仪将其显示在墙上 ;) - zproxy
2
虽然这个问题已经有了一个被接受的答案,但我们的社区不应该被用来为您个人方便提供代码工厂。 - Kev
1个回答

28

好的,这很有趣。 :)

WebGL演示可以在此处找到:http://boblycat.org/~knute/webgl/tunnel/

(编辑:不再可用,但我创建了一个ShaderToy版本:https://www.shadertoy.com/view/XdKfRD)

主要算法在片段着色器中。基本思想是从大到小迭代遍历黑色环/圆,并偏移中心以产生隧道效果。

对于任何像素,我们可以检查该像素是否接近环以成为黑色像素的候选项。如果它在环外部,则跳出循环以避免看到较大的环穿过较小的环。

从前一个(外部)圆的距离用于在环接近时“挤压”模式在一起,这有助于创建3D表面的幻觉。

每个环的波浪形式当然是正弦曲线。像素的角度(相对于圆心)与统一的时间参数结合使用,以为每个环动画波浪形式。

最后,进行了许多使用不同参数和变换函数(如pow())的试验,以使结果接近目标动画。它并不完美,但非常接近。

片段着色器代码:

#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;
}

1
也许可以将着色器拆分成多个着色器,以适应96个算术指令槽?除非>>不同的着色器对象不能附加到单个程序对象上。 - zproxy
我在一台旧的Eee netbook上自己遇到了第一个错误,但还没有进一步调查。但是在我的HTC Desire手机上,使用Firefox 4可以以每秒约半帧的速度运行。 :) - Soulman
可以说,更简单的方法是制作一个环面,应用波浪纹理,将相机放在内部并偏移纹理坐标的U分量。这样也可以在所有硬件上运行,并且不会对硬件造成压力。 - gman
@gman:好的,那你应该向我们展示如何做到这一点! :) - Sk8erPeter
可能需要让纹理具有多个波浪周期,而不仅仅是一个。http://greggman.github.io/doodles/wave-tunnel.html - gman
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接