以下の記事と似た方向の記事で、自分がよく使っている p5.js のフィルター用シェーダー「createFilterShader()」の話です。
- p5.js のフィルター用シェーダー「createFilterShader()」で Webカメラ画像をそのまま出力(テンプレート的なもの、WebGL 2.0/GLSL ES 3.0) - Qiita
- p5.js のフィルター用シェーダー「createFilterShader()」で色を減算する処理のわりとシンプルなもの(テンプレート的なもの、WebGL 2.0/GLSL ES 3.0) - Qiita
シェーダーで、p5.js のスケッチ側から渡した時間情報を使い、描画が時間変化するものを作ることがあるのですが、今回はそのような構成のものをシンプルにした内容という感じです。
何らか、シェーダーによる加工を試す際に、そのベースとして利用するテンプレート的なものを自分用に用意したくなり、今回の内容を書きました。
メインの内容
以下が今回の内容です。
シェーダーは、公式サンプルと違って「WebGL 2.0(GLSL ES 3.0)」を使う形にしています。
以下は、見た目に分かりやすいよう、キャンバスの色の特定の色成分を時間で変化させています。
let s;
function setup() {
createCanvas(640, 480);
s = createFilterShader(fragSrc);
s.setUniform("u_time", 0.0);
}
function draw() {
background(170);
s.setUniform("u_time", millis() / 1000.0);
filter(s);
}
const fragSrc = `#version 300 es
precision mediump float;
in vec2 vTexCoord;
out vec4 outColor;
uniform sampler2D tex0;
uniform float u_time;
void main() {
vec4 color = texture(tex0, vTexCoord);
// -50 から +50 の範囲で動く
float oscillatingValue = 50.0 * sin(u_time);
// 色に影響を与える(ここでは緑成分に加算)
color.g += oscillatingValue / 255.0;
outColor = clamp(color, 0.0, 1.0);
}`;
キャンバス側でシェーダーの処理に、時間変動する値を渡す部分が以下になります。
s.setUniform("u_time", millis() / 1000.0);
p5.js で時間を取得できる millis() を使っています。その際に、単位をミリ秒から秒になるように変換したものを使います。
それを、以下の setUniform でシェーダー側に渡します。
●setUniform
https://p5js.org/reference/p5.Shader/setUniform/
そして、シェーダー側では以下の部分で、受けとった値をもとに時間変動する値を作ります。さらに、その値を使って、RGB の中の緑成分を変動させるようにしています。
const fragSrc = `
...
uniform float u_time;
void main() {
...
// -50 から +50 の範囲で動く
float oscillatingValue = 50.0 * sin(u_time);
// 色に影響を与える(ここでは緑成分に加算)
color.g += oscillatingValue / 255.0;
...
}`;
これを実行した結果は以下のとおりで、色が時間変化する様子が確認できます。
余談
今回は色を時間変化させましたが、例えば何か描画するものの位置や透明度などを時間変化させるというパターンもあるかと思います。
以下は、描画する様々な要素を時間変化させたもので、試しにシェーダー側で処理全般を書いてみた例です。
キャンバス側の処理は以下のようなシンプルなもので、処理全般はフラグメントシェーダーで書いています。
上で書いた例では、基本的にキャンバス側からシェーダー側に渡す値は時間の情報のみとしていました。このあたりは、慣れの有無もあるかもしれないですが、自分にはわりと大変でした。
時間変動する値を扱う場合という話について、もしかしたら場合によってはキャンバス側で複数の変動する値を計算して(結果を配列などにする感じで)、それらをシェーダー側に渡すという構成のほうが良い場合があるかもしれません。