4月頭から育休を頂いていて、この機会に何かスキルを伸ばしたいなと考えていたときにnormalize.fmというWebGL関連のPodcastを見つけました。
以前からWebGLに興味があったので、その場の流れで主催者の方が開催しているWebGLスクールに申し込み5月から講義に参加しています。
表現の次のステージへ! WebGL スクール第12期の募集を開始します!(リモート開催) - WebGL 総本山
webgl.souhonzan.org
WebGLスクールは隔週開催で毎回宿題が課されます。
WebGL以外の使ったことがない技術を他にも試したいなと思っていたので各回の宿題に織り交ぜてみることにしました。
スクールの内容に関しては一部の資料を除き公開OKということなので、可能な限り実装メモを残すことにしようと思います。
初回の宿題の成果物
初回の宿題は「画面内に100個直方体を配置してみよう」でした。ただ直方体を並べるだけだと単調になりそうなので、辺だけを表示してよしなに回転させました。
試した技術1: ReactとThree.jsの組み合わせ
課題をReactとTypeScriptで書かれた自分のブログにデプロイしようと思っていたところ、スクールでは生のJSでゴリゴリ書いていく感じになりそうだったのでどうしようか非常に悩みました。TypeScriptに関しては書き換えれば済む話ですが、Reactはそういうわけにもいきません。
Three.js周りのステートを管理するClassと、Reactの宣言的な書き方をうまく組み合わせる必要があります。
結局のところこういうときはuseRef + useImperativeHandleで頑張るしかないと思っています。Three.jsのステート管理をするクラスは内部の状態を可能な限りカプセル化し、少数の外部操作用のメソッドを定義しておきます。その上でThree.jsのステートを管理するクラスをuseRefに渡してレンダー間でオブジェクトを共有できるようにして、useImperativeHandleを実装してref経由でThree.jsのステート管理をできるようにしています。
useImperativeHandle – React
react.dev
この方法でThree.js周りの初期化タイミングがかなり遅くなるので、今後は別の方法を模索する必要があるかもしれません。(Reactがロードされる -> 初回の変更がコミットされる -> レンダーが走ってやっとThree.js関連の初期化が走る感じになりそう)
試した技術2: Scroll Snap
スクロールスナップはランディングページとか以外で使うことが無いテクニックな気がしていて使う機会がなかったのですが、CSSのscroll-snap-typeを試してみました。スナップスクロールを実現するだけであれば数行のCSSで実現できるので非常に楽です。
一方でスナップしたタイミングでJSの処理を発火するのが地味に面倒という落とし穴がありました。
scrollsnapchangeとscrollsnapchangingというEventが定義されていますが、ブラウザのサポートが限定的かつ細かい調整が難しく正直使い勝手が悪いです。SnapEvent - Web APIs | MDN
developer.mozilla.org
結局今回の実装としてはwindowのinnerHeightとセクションのscrollTopから現在のページ数を算出するという泥臭い処理を入れています。scrollイベント毎に計算してThree.js側のステート更新すると重くて不快感が強かったので200msくらい毎に処理するようなスロットリングを入れています。
試した技術3: mix-blend-mode プロパティ
これもアプリケーションの開発という文脈だと使うことがまずないプロパティだと思います。イラレとかFigmaとかにもある重なりあった要素同士の見せ方を弄るプロパティです。今回は
mix-blend-mode: exclusionで特定のエリアでだけ立方体の色が反転して見えるようにしています。mix-blend-mode - CSS | MDN
developer.mozilla.org
3Dの辺が一部の場所だけ反転して見えると面白いのでは? と思って試してみましたがこれも使うのが難しいプロパティでした。デフォルトだと後ろにあるすべての要素とblendを計算してしまうので、
isolation: isolateで計算したい対象だけが含まれるコンテキストを適切に作る必要があります。途中で考えるのが辛くなったのでAIに丸投げしたらいい感じになりました。試した技術4: camera-controls
技術というよりライブラリですが
camera-controlsというライブラリでカメラの移動をさせています。OrbitControlsだと視点の移動は自由にできますが、easingできなさそうだったので別のライブラリを探しました。
camera-controlsは多機能で各メソッドにスムーズに移動させるか否かのフラグを渡すことができます。これを使ってカメラの視点を移動させるだけでちょっとリッチな見た目になりました。GitHub - yomotsu/camera-controls: A camera control for three.js, similar to THREE.OrbitControls yet supports smooth transitions and more features.
github.com
まとめ
Three.js側でやっていることはシンプルな図形の配置ですが、CSSと組み合わせるとそれだけでちょっといい感じになりますね。
育休中なので多少時間に余裕がありますが終わったらここまでのリソースは割けなくなるかも...