概要
この記事では「three.js超入門」と題して、three.jsの基礎からシェーダーの利用までをやっていきます。
ターゲットは主に「canvas表現を触ったことがないフロントエンドエンジニア」を想定しているので、jsの構文などの説明は省略しています。
three.jsのバージョンは執筆時点で最新のr98を使用します。
three.js超入門 第0回 3Dコンピュータグラフィックスの基礎
three.js超入門 第1回 レンダリングまでの流れ
three.js超入門 第2回 アニメーションと時間ベースでの制御
three.js超入門 第3回 マウスやスクロールでのインタラクション
three.js超入門 第4回 getBoundingClientRect()を使ったDOM要素との連携
three.js超入門 第5回 シェーダー(GLSL)の基礎
three.js超入門 第6回 ShaderMaterialでメッシュを変形、着色する
three.js超入門 第7回 シェーダーに変数を渡す
three.js超入門 第8回 シェーダーをインタラクティブに動かす
three.js超入門 第9回 シェーダーでテクスチャにエフェクトをかける
前回は3Dオブジェクトを静止画にレンダリングするところまでやりました。
今回はレンダリングのループ処理を作って3Dオブジェクトをアニメーションさせてみます。
アニメーション
javascript
でアニメーションを実装するには、window.requestAnimationFrame()
を使用して描画ループを作成し、その中で数値を動かします。
描画ループを作る
Canvas
クラスに描画ループ用のrender
関数を作成します。
render
関数内のrequestAnimationFrame
で次のフレームにもrender
関数が呼ばれるようにすることで、ループ処理を作っています。
// ~ 省略 ~
export default class Canvas {
constructor() {
// ~ 省略 ~
// 画面に表示
// this.renderer.render(this.scene, this.camera);
// 描画ループ開始
this.render();
}
render() {
// 次のフレームを要求
requestAnimationFrame(() => { this.render(); });
// 画面に表示
this.renderer.render(this.scene, this.camera);
}
};
まだループの中でモデルを動かしていないので見た目には変化はありませんが、chromeの検証ツールでRendering > Paint flashing
とRendering > FPS meter
にチェックを入れると、60fpsで再描画されていることがわかります。
数値を動かす
それでは、描画ループ内で数値を動かして、アニメーションさせてみましょう。
render
関数が1秒間に約60回呼ばれるので、1フレーム分の処理では小さい数値を足します。
render() {
// 次のフレームを要求
requestAnimationFrame(() => { this.render(); });
// ちょっとずつ回転させる
this.mesh.rotation.x += 0.01;
this.mesh.rotation.y += 0.01;
// 画面に表示
this.renderer.render(this.scene, this.camera);
}
ここで、ひとつ問題があります。
requestAnimationFrame
は実行するブラウザによって処理の間隔が違うのです。
上記のコードは60fps
の環境では1秒間に0.6
まで動きますが、30fps
の環境では1秒間に0.3
までしかいきません。これではせっかく数値を調整しても違うブラウザで見た時に「思ってたんと違う、、😢」となってしまいます。
これを解決するには時間を元にパラメーターの数値を制御する必要があります。
時間ベースのアニメーション
時間の取得にはperformance.now()
を使用します。
performance.now()
はページ表示時からの経過時間がミリ秒で返ってくるため、1 / 1000
にして単位を秒にすると数値の計算がしやすくなります。
render() {
// 次のフレームを要求
requestAnimationFrame(() => { this.render(); });
// ミリ秒から秒に変換
const sec = performance.now() / 1000;
// 1秒で45°回転する
this.mesh.rotation.x = sec * (Math.PI / 4);
this.mesh.rotation.y = sec * (Math.PI / 4);
// 画面に表示
this.renderer.render(this.scene, this.camera);
}
これでブラウザ間でのfpsの差異に影響されないようになりました。
rotation
だけでなく、position
やscale
もアニメーションさせてみましょう!