2018年12月15日

Three.jsでキューブ環境マッピング

The British Museumを見てユーザーインターフェースに使えそうだったので、映り込みが表現できる、キューブ環境マッピングを試してみました。

キューブ環境マッピング

キューブ環境マッピングには、映り込みを表現する反射マッピングと、ガラス玉のように向こう側が屈折してみえる屈折マッピングがあります。

● テクスチャ画像を用意する

キューブ環境マッピングという名前の通り、立方体の展開図のような6枚のテクスチャ画像を用意する必要があります。普通のパノラマ画像だと、上下が足りないため、どのように制作したらよいかわからなかったのですが、パノラマ画像からキューブ環境マッピング用のテクスチャ画像に変換してくれるサイトがありました。


変換した画像をフォトショップで6枚の画像に分けます。


● テクスチャの設定

6枚のテクスチャ画像をCubeTextureLoaderで読み込み、textureCube.mappingをCubeReflectionMappingにします。CubeRefractionMappingにすると屈折マッピングになります。

//テクスチャ画像の配列
var urls = [
	'img/posx.jpg','img/negx.jpg',
	'img/posy.jpg','img/negy.jpg',
	'img/posz.jpg','img/negz.jpg',
];

//ローダーで画像読み込み
var loader = new THREE.CubeTextureLoader();
var textureCube = loader.load(urls);

//反射マッピングの設定
textureCube.mapping = THREE.CubeReflectionMapping;

var geometry = new THREE.SphereGeometry(1,64,64);
var material = new THREE.MeshPhongMaterial({
	envMap:textureCube,
});
var sphere = new THREE.Mesh(geometry,material);

● script.js

Three.jsで360°パノラマコンテンツ制作でやったように、WebVRやローディングに必要なライブラリを読み込みます。

<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/webvr-polyfill.min.js"></script>
<script src="js/lib/three_vr/WebVR.js"></script>
<script src="js/lib/three_vr/OrbitControls.js"></script>
<script src="js/script.js"></script>

完成したscript.jsです。Three.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:'ground',src:'img/ground.png'}
		];
		var loadQueue = new createjs.LoadQueue();

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

		loadQueue.on('complete',function(){
			var image = loadQueue.getResult('ground');
			texture = new THREE.Texture(image);
			texture.needsUpdate = true;

			TweenMax.to("#loader_wrapper" , 1 , {opacity:0});

			init();
			initObject();
			initLight();
		});

		loadQueue.loadManifest(manifest);
	}

	//シーン、カメラ、レンダラー生成
	function init(){
		var polyfill = new WebVRPolyfill();

		scene = new THREE.Scene();
		camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight,0.1,500);
		camera.position.set(0, 0, 0);
		scene.add(camera);

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

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

		render();
		checkDevice();

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

	//オブジェクト生成
	function initObject(){
		texture.repeat.set(50, 50);
		texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
		texture.magFilter = THREE.NearestFilter;
		var geometry = new THREE.PlaneGeometry(250,250);
		var material = new THREE.MeshStandardMaterial({map:texture,roughness:0.0,metalness:0.6});

		var floor = new THREE.Mesh(geometry,material);
		floor.rotation.x = -Math.PI / 2;
		floor.receiveShadow = true;
		scene.add(floor);

		//テクスチャ画像の配列
		var urls = [
			'img/texture/posx.jpg','img/texture/negx.jpg',
			'img/texture/posy.jpg','img/texture/negy.jpg',
			'img/texture/posz.jpg','img/texture/negz.jpg',
			];

		//ローダーで画像読み込み
		var loader = new THREE.CubeTextureLoader();
		var textureCube = loader.load(urls);

		//反射マッピングの設定
		textureCube.mapping = THREE.CubeReflectionMapping;

		var geometry = new THREE.SphereGeometry(1.25,64,64);
		var material = new THREE.MeshPhongMaterial({
			envMap:textureCube,
		});
		var sphere = new THREE.Mesh(geometry,material);
		sphere.position.x = 2;
		sphere.position.y = 2;
		sphere.castShadow = true;

		scene.add(sphere);
	}

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

		var directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1, 0);
		directionalLight.position.set(0, 100, 0);
		directionalLight.castShadow = true;
		directionalLight.shadow.camera.top = 20;
		directionalLight.shadow.camera.bottom = -20;
		directionalLight.shadow.camera.right = 20;
		directionalLight.shadow.camera.left = -20;
		directionalLight.shadow.mapSize.set(4096,4096);
		scene.add(directionalLight);
	}

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

	//デバイス判定
	function checkDevice(){
		var ua = window.navigator.userAgent.toLowerCase();
		var _iOS,_Android,_Tablet,_Pc,_vrDisplay;

		_iOS = /ipad|iphone|ipod/.test(ua);
		_Android = /android/.test(ua);
		_Tablet = /ipad|Nexus (7|9)|xoom|sch-i800|playbook|tablet|kindle/i.test(ua);

		if (navigator.getVRDisplays) {
			navigator.getVRDisplays().then(function (displays) {
		    	var vrDisplay = displays.length && displays[0];
		    	if(vrDisplay == 0){
		    		if(!_iOS && !_Android && !_Tablet){

						orbitControls = new THREE.OrbitControls(camera);
						orbitControls.target.set(
							camera.position.x + 0.01,
							camera.position.y,
							camera.position.z
							);
					}
				}else{
					renderer.vr.enabled = true;
				}
			});
		}
	}
})();

完成したscript.jsを調整したデモになります。VRヘッドセットでアクセスすると、ルーブル美術館が映り込んだ球体が表示されます。

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

Three.jsでオブジェクトを選択

次の記事へ

Three.jsで360°パノラマギャラリー制作