2019年03月03日

glTFをThree.jsで読み込み

Blenderで3DCG制作で制作したドーナツをglTFに出力して、Three.jsで読み込んでみました。

BlenderからglTFを出力

● glTFとは

glTF (GL Transmission Format)は、Khronos Groupによって開発された、JSON形式の3Dファイルフォーマットです。もともとOpenGL(特にWebGL)での利用が想定され、現在のバージョンはglTF2.0です。

● glTF Exporterの準備

BlenderでglTFを出力するためには、アドオンのglTF Exporterが必要です。

GitHubからデータをダウンロードして、「scripts/addon/io_scene_gltf2」を Blenderのアドオンディレクトリに追加します。アドオンディレクトリは、Macの場合、Blenderアプリを右クリックして、「パッケージの内容を表示」から、「Contents/Resources/2.79/scripts/addons」です。

「ユーザー設定 > アドオン」の「Import-Export:glTF 2.0 format」にチェックを入れて更新し、アドンを有効化します。

● PBRマテリアルの設定

マテリアルとテクスチャを表示させるため、BlenderでPBRマテリアルを設定しました。PBR(物理ベースレンダリング)は現実の光学現象をシミュレートすることにより、リアルな質感を表現できるレンダリング方法です。

GitHubからダウンロードしたデータの「pbr_node」中に「glTF2.blend」があるので、「ファイル > リンク > glTF2.blend」を選択します。その中の「NodeTree > glTF Metallic Roughness」を選択すると、ノートエディターでglTF Metallic Roughnessが使用できるようになります。

ノードエディターの「追加 > グループ」の中にglTF Metallic Roughnessが追加されます。マテリアルカラーを設定したい場合は、BaseColorFactorのカラーを設定します。テクスチャを設定したい場合は、Image TextureをBaseColorにつなげます。詳細は、英語ですが下記ページに書いてあります。

● glTFを出力

準備ができたら「ファイル > エクスポート > glTF2.0 (.gltf)」で出力します。設定はデフォルトのままで問題ありません。

必要であれば、glTFを出力するさい、モディファイアーを適用しておきます。

Three.jsでglTFを読みこみ

● Three.jsで読み込む

出力したglTFは、THREE.GLTFLoaderで読み込むことができます。

//glTFの読み込み
var loader = new THREE.GLTFLoader();

loader.load('./donut.gltf',function(data){
	var gltf = data;
	var obj = gltf.scene;
	scene.add(obj);
});

//読み込んだシーンが暗いので、明るくする
renderer.gammaOutput = true;

マテリアルとテクスチャが反映され、ドーナツが表示されました。

ただ、皿と机に影ができていません。また、ライティングはThree.jsで設定するので、ライティングと影を調整します。影のつけ方は、Three.jsでオブジェクトを選択で説明していますが、皿と机に影をつけるためには、それぞれMeshを取得する必要があります。

glTFはJSONデータで、読み込んだglTFの中のMeshは、下記のように「.children」で取得することができます。Meshの名前はBlenderで設定したものが入っています。

//glTFの読み込み
var loader = new THREE.GLTFLoader();

loader.load('./donut.gltf',function(data){
	var gltf = data;
	var obj = gltf.scene;

	for(var i = 0; i < obj.children.length; i++){

		var mesh = obj.children[i];

		//コンソールにMeshの名前一覧を出力。
		//console.log(i,mesh.name);
	}
	scene.add(obj);
});

//読み込んだシーンが暗いので、明るくする
renderer.gammaOutput = true;

まだまだわからないことが多く、もっと質感やクオリティはあげられると思いますが、一旦ライティングと影を調節して完成です。

● script.js

必要なライブラリを読み込みます。

<script src="js/lib/preloadjs.min.js"></script>
<script src="js/lib/TweenMax.min.js"></script>
<script src="js/lib/three_vr/three.min.js"></script>
<script src="js/lib/three_vr/OrbitControls.js"></script>
<script src="js/lib/three_vr/GLTFLoader.js"></script>
<script src="js/script.js"></script>

完成したscript.jsです。ライティングは、上からと四方からあてて、影が綺麗に見えるようにしました。

(function () {
	window.addEventListener("load", function () {
		startLoading();
	});

	var scene,camera,renderer;
	var texture;

	//ローディング処理
	function startLoading(){
		TweenMax.to(".loader",0.1,{opacity:1});

		var manifest = [
			{id:'donut',src:'data/donut.gltf'}
		];
		var loadQueue = new createjs.LoadQueue();

		loadQueue.on('progress',function(e){
			var progress = e.progress;
		});

		loadQueue.on('complete',function(){
			TweenMax.to("#loader_wrapper" , 1 , {opacity:0});

			init();
			initObject();
			initLight();
		});
		loadQueue.loadManifest(manifest);
	}

	//シーン、カメラ、レンダラー生成
	function init(){
		scene = new THREE.Scene();
	   	camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight);
		camera.position.set(0, 4, 4);
		scene.add(camera);

		renderer = new THREE.WebGLRenderer({antialias: true});
		renderer.setSize(window.innerWidth, window.innerHeight);
		renderer.render(scene,camera);
		renderer.shadowMap.enabled = true;
		renderer.shadowMap.type = THREE.PCFSoftShadowMap;

		var orbitControls = new THREE.OrbitControls(camera);

		var container = document.createElement('div');
		document.body.appendChild(container);
		container.appendChild(renderer.domElement);

		render();

		window.addEventListener('resize',onWindowResize,false);
		function onWindowResize(){
			camera.aspect = window.innerWidth/window.innerHeight;
			camera.updateProjectionMatrix();
			renderer.setSize(window.innerWidth,window.innerHeight);
		}
	}

	//オブジェクト生成
	function initObject(){
		//glTFの読み込み
		var loader = new THREE.GLTFLoader();
		loader.load('data/donut.gltf',function(data){
			var gltf = data;
			var obj = gltf.scene;

			for(var i = 0; i < obj.children.length; i++){

				var mesh = obj.children[i];
				//console.log(i,mesh.name);

				if(i >= 1000){
					if(i==1014 || i==1019){
						mesh.receiveShadow = true;
					}
					mesh.castShadow = true;
				}
			}

			obj.position.set(0,0.1,0);
			obj.scale.set(1,1,1);
			scene.add(obj);
		});

		renderer.gammaOutput = true;
	}

	//ライト生成
	function initLight(){
		var ambientLight = new THREE.AmbientLight(0x666666);
		scene.add(ambientLight);

		var positionArr = [
				[0,5,0,2],
				[-5,3,2,2],
				[5,3,2,2],
				[0,3,5,1],
				[0,3,-5,2]
			];

		for(var i = 0; i < positionArr.length; i++){
			var directionalLight = new THREE.DirectionalLight(0xFFFFFF, positionArr[i][3]);
			directionalLight.position.set( positionArr[i][0], positionArr[i][1], positionArr[i][2]);

			if(i == 0 || i == 2 || i == 3){
				directionalLight.castShadow = true;
				directionalLight.shadow.camera.top = 50;
				directionalLight.shadow.camera.bottom = -50;
				directionalLight.shadow.camera.right = 50;
				directionalLight.shadow.camera.left = -50;
				directionalLight.shadow.mapSize.set(4096,4096);
			}
			scene.add(directionalLight);

			//var helper = new THREE.DirectionalLightHelper( directionalLight, 1);
			//scene.add(helper);
		}
	}

	//アニメーション
	function render() {
		requestAnimationFrame(render);
		renderer.render(scene, camera);
	}
})();

完成したscript.jsを調整したデモになります。Oculus Goで確認したところ、スプリンクルの数が多すぎて負荷がかかりすぎているようなので、パソコンとスマホのみで見ることができるようにしました。

  • このエントリーをはてなブックマークに追加

関連記事

前の記事へ

Blenderで3DCG制作

次の記事へ

Blenderでモデリング