8

このトンネルのようなアニメーションを WebGL に実装するにはどうすればよいですか?

ここに画像の説明を入力

ソース: http://dvdp.tumblr.com/

参照: WebGL でこの回転スパイラルを実装するには?

4

1 に答える 1

28

さて、これは楽しかったです。:)

WebGLデモはここから入手できます:http://boblycat.org/~knute/webgl/tunnel/

編集:使用できなくなりましたが、ShaderToyバージョンを作成しました:https ://www.shadertoy.com/view/XdKfRD )

主なアルゴリズムはフラグメントシェーダーにあります。基本的な考え方は、黒いリング/円を大きいものから小さいものへと繰り返し、中心をオフセットしてトンネルのような効果を生み出すforループです。

任意のピクセルが与えられると、そのピクセルがリングに十分近く、黒いピクセルの候補になるかどうかを確認できます。リングの外側にある場合は、ループを壊して、大きなリングを通して小さなリングが見えないようにします。

前の(外側の)円からの距離は、リングが閉じているときにパターンを一緒に「絞る」ために使用されます。これは、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;
}
于 2011-03-30T22:15:51.133 に答える