[ Three.js ] 3D猫はいかがですか?
Three.js?
ブラウザに3Dオブジェクトを簡単にレンダリングしてくれるJavaScriptの3Dライブラリのことです。
最近興味があったので今回簡単にglTFのフォーマットの3Dアニメーションモデルを画面に描画してみました。
全体のソースコードはこちらです。
部分的に説明しているので、全体のソースコードと一緒に見た方がいいかもしれません。
下準備
HTMLファイル準備
今回は簡単に動作を試してみるために、HTMLファイルだけ使うことにしました。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>My first three.js app</title>
<style>
body {
margin: 0;
}
canvas {
display: block;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
</html>
3D Modelをインストール
3D Modelはsketchfabからダウンロードしました。
ダウンロードが可能で著作権に問題ないものにしましょう。今回はこちらの猫を使ってみました。
レッサーパンダにしてみたかったのですが、検索するとなんだか怖いレッサーパンダが多くて猫にしました。
(いつか自分で作ってみたいですね。)
"An Animated Cat" (https://skfb.ly/6YPwH) by Evil_Katz is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
フォーマットは glTF
でダウンロードします。
※ glTF(GL Transmission Format)
JSONによって3Dモデルやシーンを表現するフォーマット
ディレクトリ構成
glTFファイルのzipを解凍し、index.htmlと同じ階層に入れます。
Three.jsを触る
install
npmでインストールする方法もありますが、今回はhtmlファイルだけ利用したので、CDNからのインストールにしました。
https://threejs.org/docs/index.html#manual/ja/introduction/Installation
bodyの中に以下を書いてimportします。
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.141.0/build/three.module.js",
"GLTFLoader": "https://unpkg.com/three@0.141.0/examples/jsm/loaders/GLTFLoader.js",
"OrbitControls": "https://unpkg.com/three@0.139.2/examples/jsm/controls/OrbitControls.js"
}
}
</script>
- glTFを読み込むために利用します。
- カメラの位置をコントロールできる。マウスでカメラの位置を変更したり、zoom操作も可能です。
三つの基本要素: scene / camera / renderer
scene
- modelが配置されて背景、質感などが表示される空間
camera
- sceneを見るカメラ
- カメラの位置、角度、方向などを決められます。
-
PerspectiveCamera
とOrthographicCamera
がありますが、遠近感が適用されるカメラのPerspectiveCamera
を使いました。オプションについての説明は長くなるため、省略します。
renderer
- sceneとcameraのデータをもらって画面にレンダリングしてくれる
<script type="module">
import * as THREE from "three";
import { GLTFLoader } from "GLTFLoader";
import { OrbitControls } from "OrbitControls";
// scene
const scene = new THREE.Scene();
scene.background = new THREE.Color("white");
// camera
const camera = new THREE.PerspectiveCamera(90, 1000 / 1000, 1, 2000);
camera.position.set(0, 0, 25);
// renderer
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#canvas"),
});
// modelを表示させる
const loader = new GLTFLoader();
loader.load("cat/scene.gltf", function (cat) {
// sceneにmodelを入れる
scene.add(cat.scene);
// sceneとcameraをレンダリング
renderer.render(scene, camera);
});
</script>
ここまでだと、このように3Dモデルが表示されます。
暗かったのでライトを入れました。また、sRGBEncoding
を入れることで本来のモデルの色に近くなりました。
const light = new THREE.AmbientLight(0xffffff, 4);
scene.add(light);
renderer.outputEncoding = THREE.sRGBEncoding;
アニメーションを入れる
この3Dモデルは元々動くモデルだったのでせっかくなので動かしてみます。
AnimationMixer
Modelのアニメーションプレイヤー
// AnimationMixerを作成
const mixer = new THREE.AnimationMixer(cat.scene);
// 全てのアニメーションを再生させる
cat.animations.forEach((clip) => {
mixer.clipAction(clip).play();
});
scene.add(cat.scene);
AnimationMixer
を作るだけだと、画面にレンダリングされないため、requestAnimationFrame
を利用して描画させる必要があります。
https://threejs.org/docs/#api/en/animation/AnimationMixer
requestAnimationFrame
requestAnimationFrame(callback)
- callback関数を渡す
- ブラウザが再描画可能なタイミングに実行される
- 1秒に60回程度
以下はanimate()
関数は自分自身をrequestAnimationFrame()
を使って呼び出していてループをさせています。つまりanimate()
は1秒に60回くらい実行されます。
function animate() {
requestAnimationFrame(animate);
console.log("moving");
}
animate();
猫の描画のためにこのように書いてみました。requestAnimationFrameでrendererを読んでいます。
function animate() {
// 毎回レンダリングをすることでアニメーション効果を出す
renderer.render(scene, camera);
// 1秒に60回
requestAnimationFrame(animate);
}
animate();
次は上で作ったAnimationMixer
を呼び出します。
Clock + mixer
指定した時間分、AnimationMixerを定期的に動かす必要があります。
Clockは時間を把握するオブジェクトです。ここでは時間差を取得できるgetDeltaを使いました。
1秒に60回くらい(60fps)の頻度でanimate()が実行されるので、その間隔の時間を取得し、その時分のアニメーションフレームを更新します。
const clock = new THREE.Clock();
function animate() {
// 次に実行されるときの時間差を保存
const delta = clock.getDelta();
// その時間差分のアニメーションをフレームを更新させる
if (mixer) mixer.update(delta);
// 毎回レンダリングをすることでアニメーション効果
renderer.render(scene, camera);
// 1秒に60回
requestAnimationFrame(animate);
}
animate();
https://threejs.org/docs/#api/en/core/Clock
このようにアニメーションが適用されました。
マウス操作を入れる
マウスで3Dモデルをくるくるさせたりできます。
OrbitControlersの引数にカメラとDOM要素を渡すことで実現できます。
new OrbitControls(camera, renderer.domElement);
- Orbit: 左ボタンでドラッグ
- Zoom: マウスホイール
- pan: 右ボタンでドラッグ
https://threejs.org/docs/#examples/en/controls/OrbitControls
https://ics.media/tutorial-three/camera_orbitcontrols/
ミニ感想
3Dモデルを入れたmixerを再生するだけではなく、requestAnimationFrameを利用して定期的に描画しないといけないところがややこしかったです。
また、Three.js自体は公式ドキュメントを含めてドキュメントの量が少ない印象を受けました。
今回は簡単なオブジェクトレンダリングでしたが、実際にゲームなど規模の大きい開発になると大変そうです。
3Dになると一気に数学計算しないといけない部分も出てきたりするので、そこの理解も深めないといけないと思いました。
Discussion