📛

Three.jsでwebブラウザ上に3Dモデルを表示してみよう

2023/12/21に公開

はじめに

Social Databank Tech Blog Advent Calendar 2023の21日目です。

個人的にですが、弊社プロダクトのキャラクターLinyくんの3Dモデルを作ってみました。
ポリゴン数251、テクスチャ解像度256px*256pxという省エネ造形です。
(いわゆる特定の界隈で認知されている256fesのレギュレーションにのっとって作りました。)
Alt text
せっかくなのでwebブラウザ上で表示したいのでThree.jsで表示させましょう。

Three.jsとは

webブラウザでの3DCG表現を実現できるJavaScriptライブラリです。
https://threejs.org/

やってみよう

公式ドキュメントのやり方に寄せて進めていきます。
Creating-a-scene」ページにある通りindex.htmlとmain.jsを用意します。

index.html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
		</style>
	</head>
	<body>
		<script type="module">
			import * as THREE from 'https://unpkg.com/three/build/three.module.js';

			// Our Javascript will go here.
		</script>
	</body>
</html>
main.js
//まだ何も書かない

シーン,カメラの設定

main.jsに3Dモデルを表示させるためにシーンとカメラを設定し、レンダリングする処理を記載しましょう

main.js
import * as THREE from 'three';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

camera.position.z = 5;


function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}
animate();

このままでは3D空間に何もないので黒い画面だけしか見えないと思います。
試しに3D空間を立方体を置くコードを書いてみましょう。

main.js
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

Alt text
このように作成したMeshをシーンにaddすることで3Dモデルがブラウザ上で表示できます。
次の工程で3Dモデルを配置するときにキューブが邪魔になるので、確認が終わったらこのコードは削除しておきます。

3Dモデルを読み込む

さて本題です。3Dモデルを読み込ませて表示させてみましょう。
公式ドキュメントの「Loading 3D models」を参考に進めていきます。

blenderからglbファイルとして出力

作成した3Dモデルを、これをglb(gltf)という形式で出力しましょう。
今回3Dモデルはblenderで作成したため、blenderでの出力方法を記載します。
File > Export > glTF2.0(.glb/gltf)
Alt text

読み込み

先ほど読み込んだファイルをjsのコードから読み込みます。
animate()前に記載します。

main.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('models/linykun/linykun.glb', function (gltf) {
    scene.add(gltf.scene);
}, undefined, function (error) {
    console.error(error);
});

Alt text
3Dモデルを表示させることができました。
glbファイルにテクスチャの情報も入ってるみたいでテクスチャ画像は読み込み不要でしたね。
(マテリアルの設定もいらないのか…。)

コントロールも設定して好きな方向から見られるようにしよう

このままだと正面からしかキャラクターをみることができません。
せっかく3Dとして読み込んでいるので、いろいろな角度から見られるようにしたいですよね。
Three.jsは3D空間を自由に操作できるための便利なクラスがあります。
追記しましょう。

main.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
controls.update();

OrbitControlsを設定することで原点を軸にぐるりと一周するように画面を操作できます。
そして、autoRotateのオプションも設定しました。これをtrueにすることで何も操作していなくても画面が回転します。
また、公式ドキュメントにある通り、autoRotate=trueのときにはanimate()内でcontrols.update();が必須らしいです。

main.js
function animate() {
    requestAnimationFrame(animate);
    controls.update(); //enableDampingまたはautoRotateがtrueの時には書く。
    renderer.render(scene, camera);
}

hdr画像を読み込んで背景をつけよう

背景が真っ黒というのも物足りないですね、背景画像を置きましょう。
こちらのコードを参考に読み込ませます。
https://discourse.threejs.org/t/how-to-apply-hdri-env/218/6
また、背景の画像ファイルに関してはcc0でhdr画像を頒布しているpolyhaven様から拝借いたしました。
https://polyhaven.com/a/small_empty_room_1

main.js
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const hdriLoader = new RGBELoader()
hdriLoader.load('hdri/small_empty_room_1_1k.hdr', function (texture) {
    const envMap = pmremGenerator.fromEquirectangular(texture).texture;
    texture.exposure = 1;
    //   scene.environment = envMap //光の影響を受けたい場合はこれも記述
    scene.background = envMap;
}, undefined, function (error) {
    console.log(error)
});

コードはこんな感じ

最終的には以下のような成果物ができました

index.html
<!-- 公式ドキュメントと完全一致のため省略 -->
main.js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 3Dモデル読み込み
const loader = new GLTFLoader();
loader.load('models/linykun/linykun.glb', function (gltf) {
    scene.add(gltf.scene);
}, undefined, function (error) {
    console.error(error);
});

// コントローラーセット
const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
controls.update();

// hdri読み込み
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const hdriLoader = new RGBELoader()
hdriLoader.load('hdri/small_empty_room_1_1k.hdr', function (texture) {
    const envMap = pmremGenerator.fromEquirectangular(texture).texture;
    texture.exposure = 1;
    //   scene.environment = envMap //光の影響を受けたい場合はこれも記述
    scene.background = envMap;
}, undefined, function (error) {
    console.log(error)
});

// カメラの位置変更
camera.position.z = 5;

function animate() {
    requestAnimationFrame(animate);
    // cube.rotation.x += 0.01;
    // cube.rotation.y += 0.01;
    controls.update();
    renderer.render(scene, camera);
}
animate();

まとめ

というわけで自作の3Dモデルがweb上で表示できるようになりました。
これをデプロイすればwebページとしてみんなに自作の3Dモデルをいろいろな角度から見てもらうことでができますね。

また、今回作成したような「ポリゴン数256未満、テクスチャ解像度256px*256px」の3Dモデルに興味が出た方はぜひ256fesで検索をしてみてください。
ちょっとした工作感覚で3Dモデリングに挑戦できるので、3DCGに興味を持った方は是非やってみると面白い体験になると思います✌。
(公式でのイベントは毎年2月8日~(2の8乗)に開催しているみたいです。)
https://twitter.com/256fes/status/1622973489195151362

ということで、記事を読んでいただきありがとうございました。

参考リンク

https://qiita.com/yuki_doi/items/ac201cac8220d613d819

ソーシャルデータバンク テックブログ

Discussion