Help us understand the problem. What is going on with this article?

フラグメントシェーダーだけでタイムラインアニメーションを作る考え方

More than 1 year has passed since last update.

以前、フラグメントシェーダーのみで、タイムラインのようなアニメーションをさせる方法を、The Book of ShaderのGalleryの中から学んだのでまとめました。

ちなみに、私が作ったのはこんな感じです。

背景で動いている四角や、三角がそれです。
これは、Unityで作りましたがHLSLでもGLSLでも考え方は一緒です。
今回はGLSLで説明します。

0~1で動くようなアニメーション関数を作る

まずは矩形や円などが動くアニメーション関数を作ります。

float animation1(vec2 _st, float t) {
    float x = t;
    float y = 0.5;
    float d = rect(_st, vec2(x, y));
    return d;
}

tには0〜1が入る想定です。その数値をうまく使ってアニメーションさせます。
上記のは四角が0〜1に向かって移動するイメージです。

さらにEaseing関数で味を出す

ですが、ただ時間でアニメーションをさせると、一定の動きしかせず、単調になるので、Easeing関数を使っていい感じにしましょう。

Easeing関数とはざっくり説明すると、数値の変化率を変える関数です。
それを使うことで、だんだん速くとか、だんだんゆっくりにとか変化させることができます。

Easingがよくわからないという人はこちらをチェックです。
Easing Function 早見表

シェーダー内でのEasing関数はこちらが参考になります。
The Book of Shaders:Gallery - Easing Function

float easeInQuad(float t) {
    return t * t;
}

float easeOutQuad(float t) {
    return -1.0 * t * (t - 2.0);
}

float animation1(vec2 _st, float t) {
    float x = easeOutQuad(t);
    float y = 0.5;
    float d = rect(_st, vec2(x, y));
    return d;
}

Easing関数には0〜1の値を渡します。
そのままtの値を渡してあげれば、0〜1の変化率を変えてくれます。
今回はEaseOut(だんだん遅く)をかけてみます。

トータルの時間を決める

次にアニメーションのトータルの時間を決めます。
mod()の第一引数に時間、第二引数にトータルの時間を入れます。

uniform vec2 resolution;
uniform float time;

void main () {
    vec2 st = gl_FragCoord.xy / resolution.xy;
    //トータルで10秒のアニメーション
    float t = mod(time, 10.0);
    vec3 color = vec3(0.0);
    gl_FragColor = vec4(color, 1.0);
}

mod()は余りを返してくれるので 常に0〜10を返してくれるようになります。

各アニメーションに時間を配分する

それぞれ作ったアニメーション関数に、時間を渡していきます。
それぞれ時間を渡す際はlinearstep()を使います。

uniform vec2 resolution;
uniform float time;

float animation1(vec2 _st, float time) { ... }
float animation2(vec2 _st, float time) { ... }
float animation3(vec2 _st, float time) { ... }

void main () {
    //トータルで10秒のアニメーション
    vec2 st = gl_FragCoord.xy / resolution.xy;
    float t = mod(time, 10.0);
    vec3 color = vec3(0.0);

    //各アニメーションに時間を設定して渡す
    float a1 = animation1(st, linearstep(0.0, 3.0, t));
    float a2 = animation2(st, linearstep(3.0, 7.0, t));
    float a3 = animation3(st, linearstep(7.0, 10.0, t));

    //全てをまとめる
    color = vec3(a1) + vec3(a2) + vec3(a3);

    gl_FragColor = vec4(color, 1.0);
}

linearstep()の第一引数に開始時間、第二引数に終了時間を渡します。

//例えば以下の場合は0〜3を0〜1に変換してくれます
//0より小さい数字は0、3より大きい数字は1が返ってきます。
float a1 = animation1(st, linearstep(0.0, 3.0, t));

これで、それぞれのアニメーションをどのタイミングで行うかを調整します。

最後に全てのアニメーションをまとめて完成です。

さらに

linearstep()をアニメーション関数の中で使えば、矩形が入ってきて、出ていくみたいなタイミングも調整できます。

float easeInQuad(float t) { ... }
float easeOutQuad(float t) { ... }

float animation1(vec2 _st, float t) {
    float x = easeInQuad(linearstep(0.0, 0.5, t)) * 0.5
              + easeOutQuad(linearstep(0.5, 1.0, t)) * 0.5;
    float y = 0.5;
    float d = rect(_st, vec2(x, y));
    return d;
}

こんな感じで、0 ~ 0.5まではEaseIn(だんだん速く)、0.5 ~ 1.0まではEaseOut(だんだん遅く)、みたいなこともできます。

これらをうまく組み合わせるとよりいい感じのアニメーションになります。

参考文献

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした