こちらの記事で書いていた内容の続きです。
●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 のページ」をご覧ください。
その処理を実装すると、以下のようなノイズ画像が得られます。
それでは、今回の追加実装の話を書いていきます。
seed を設定できるようにする
1つ目は、乱数生成でよくある seed のような設定をできるようにします。
前の記事に掲載していたプログラムで言うと、フラグメントシェーダーの以下の部分に手を加えます。
上記は 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つ目のほうを使った描画で、夜の空みたいな見え方をするものを作ってみました。