1
2

p5.js のフィルター用シェーダー「createFilterShader()」で三角関数を使ったノイズ(WebGL 2.0/GLSL ES 3.0):seed・スクロールアニメーションを追加

Posted at

こちらの記事で書いていた内容の続きです。

●p5.js のフィルター用シェーダー「createFilterShader()」で三角関数を使ったノイズの WebGL 2.0/GLSL ES 3.0 バージョン(The Book of Shadersより) - Qiita
 https://qiita.com/youtoy/items/e11126452f8528813f3b

具体的には、上の記事で書いていた「見た目が 1種類限定の静止画的なノイズ画像」に関して、「見た目を変える」「アニメーションさせる」という 2つのことをやります。
(「見た目が異なる複数種類の、静止画的な出力のノイズ画像の出力」と「xy方向にスクロールするアニメーション」という内容をやります)

それらを試した内容は、以下のとおりです。

実装について

ノイズ画像をシェーダーで出力する話は、前の記事か、その元ネタになっている「The Book of Shaders: Noise のページ」をご覧ください。 

その処理を実装すると、以下のようなノイズ画像が得られます。

image.png

それでは、今回の追加実装の話を書いていきます。

seed を設定できるようにする

1つ目は、乱数生成でよくある seed のような設定をできるようにします。

前の記事に掲載していたプログラムで言うと、フラグメントシェーダーの以下の部分に手を加えます。

image.png

上記は sin() を使ってばらけた値を出力するということをやっていますが、その生成される値の系列が変化するようにします。

コード全体は以下のとおりです。

let s;

function setup() {
  createCanvas(500, 400, WEBGL);
  noStroke();

  s = createFilterShader(fragSrc);
}

function draw() {
  background(0);

  const seed = map(mouseX, 0, width, 100.0, 300.0)
  s.setUniform("seed", seed);
  filter(s);
}

const fragSrc = `#version 300 es
precision mediump float;

in vec2 vTexCoord;
out vec4 outColor;

uniform float seed;

// 2D Random
float random(in vec2 st) {
    return fract(sin(dot(st.xy + seed, vec2(12.9898, 78.233))) * 43758.5453123);

}

// 2D Noise
float noise(in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));
    vec2 u = f*f*(3.0-2.0*f);
    return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

void main() {
    vec2 st = vTexCoord;
    vec2 pos = st * 5.0;
    float n = noise(pos);
    outColor = vec4(vec3(n), 1.0);
}`;

dot() の中に seed の値を入れ、その値は draw() の中で mouseX をもとに用意する構成にしました。

スクロールアニメーション

次にスクロールアニメーションです。

main() の中で、座標に関わる処理を行っている部分の値を時間変化させます。

処理全体は以下のとおりです。

let s;

function setup() {
  createCanvas(500, 400, WEBGL);
  noStroke();

  s = createFilterShader(fragSrc);
}

function draw() {
  background(0);

  const u_time = millis() * 0.003;
  s.setUniform("u_time", u_time);
  filter(s);
}

const fragSrc = `#version 300 es
precision mediump float;

uniform float u_time;

in vec2 vTexCoord;
out vec4 outColor;

// 2D Random
float random(in vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

// 2D Noise
float noise(in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));
    vec2 u = f*f*(3.0-2.0*f);
    return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

void main() {
    vec2 st = vTexCoord + vec2(u_time * 0.1, u_time * 0.1);
    vec2 pos = st * 5.0;
    
    float n = noise(pos);    
    outColor = vec4(vec3(n), 1.0);
}`;

vec2 st = vTexCoord + vec2(u_time * 0.1, u_time * 0.1) という部分で、 u_time という時間変化する値を、x座標と y座標の両方に足しています。
(正規化された)座標 0 〜 1 からはみ出た部分は、座標 0 〜 1 をループしているような変化をするので、単純な加算のみ行っておけば OK です。

また、時間変化する値は draw() の以下の部分から受け渡している構成です。

  const u_time = millis() * 0.003;
  s.setUniform("u_time", u_time);

余談

ここからは余談ですが、上記の 2つ目のほうを使った描画で、夜の空みたいな見え方をするものを作ってみました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2