reglとは
WebGLでお絵描きをしたいとき、大きく分けて以下の2つの方向性がある。1
- three.jsなどの高レベルなライブラリを使う
- GLSLでシェーダを記述し、WebGL APIをそのまま扱う
後者のようにGLSLを扱えると自由度が高い一方で、WebGL APIに関連して "おまじない" 的なコードが大量に必要になってしまうという問題があった。
この記事で扱う regl は、WebGLのwrapperに相当する。初見でもWebGL APIと1:1に対応づけられる程度には原型を維持することで自由度を担保しつつ、大量の "おまじない" や煩雑な状態管理を単純化し、コードの可読性を大幅に向上してくれる。
公式GitHubではFunctional WebGL
と謳っているが、おそらく「変数を入れると(GLSLに基づいて)描画が得られる」ことを指してFunctional
と言っているのだと思われる。ReactでいうところのstatelessなFunction Componentみたいなイメージか。
Reactでreglを扱う
GitHubにいくつかサンプルコードがあるものの、Reactで扱っている例はググってもあまり見当たらなかった。Reactでのシンプルなコードを以下に記載する。
このコードには基本的なシェーダの扱いとアニメーションの方法が含まれており、静止した三角形の色が時間とともに変化する。
import React, { useEffect } from "react";
import createRegl from "regl";
const Sample = () => {
const regl = createRegl(); // No arguments: create a full screen canvas
const drawTriangle = regl({
frag: `
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}`,
vert: `
precision mediump float;
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0, 1);
}`,
attributes: {
position: [[0, -1], [-1, 0.5], [1, 1]] // No need to flatten
},
uniforms: {
color: regl.prop("color")
},
// Number of vertices to draw in this command
count: 3
});
useEffect(() => {
regl.frame(({ time }) => {
regl.clear({
color: [0, 0, 0, 0],
depth: 1
});
drawTriangle({
color: [
Math.cos(time * 1.0),
Math.sin(time * 0.8),
Math.cos(time * 3.0),
1
]
});
});
return () => { regl.destroy() }; // Clean up when unmounted
}, []);
return <></>;
};
export default Sample;
ポイント
初期化
const regl = createRegl()
のところで引数をしていないため、全画面のcanvasが新たに作成される(reglの仕様)。
既存のcanvasを使用したい場合等は引数で指定できる(公式ドキュメント参照)
描画
drawTriangle()
は、frag
, vert
に文字列として記載されたGLSLに基づいて三角形を描画する関数。ここで、
color: regl.prop("color")
という部分に注目。drawTriangle()
に引数color
を渡すことで動的に描画内容を変えられるようになっている。
副作用フック
useEffect
によって、Reactコンポーネントの副作用として描画を実行している。
useEffect
の第二引数が[]
なので副作用は一度だけ実行されるが、その中でregl.frame()
が呼ばれることによりアニメーションが開始する。
副作用のclean up
return () => { regl.destroy() };
useEffect
のなかで上記のようなreturn
文がある。今回はcomponentと無関係に全画面のcanvasが作成されているので、このように副作用のclean upを行わないと、別コンポーネントに画面表示を切り替えた後にもcanvasが残ってしまう。
※ そもそもコンポーネント外にcanvasを作成するのは良くないので、component内に予め用意したcanvasをcreateReglに渡すのが良い気がする
-
three.jsでシェーダを扱う方法もあります ↩