2018年12月15日 - WebVR・Three.js
Three.jsでキューブ環境マッピング
「The British Museum」を見てユーザーインターフェースに使えそうだったので、映り込みが表現できるキューブ環境マッピングを試してみました。
※Three.jsの仕様が変更になったため内容を修正しました。(2019年12月13日)
キューブ環境マッピング
キューブ環境マッピングには、映り込みを表現する反射マッピングと、ガラス玉のように向こう側が屈折してみえる屈折マッピングがあります。
● テクスチャ画像を用意する
キューブ環境マッピングという名前の通り、立方体の展開図のような6枚のテクスチャ画像を用意する必要がありますが、下記サイトでパノラマ画像からキューブ環境マッピング用のテクスチャ画像に変換することができます。
変換した画像をフォトショップで6枚の画像に分けます。
● テクスチャの設定
6枚のテクスチャ画像をCubeTextureLoaderで読み込み、textureCube.mappingをCubeReflectionMappingにします。CubeRefractionMappingにすると屈折マッピングになります。
//テクスチャ画像の配列 const urls = [ 'img/posx.jpg','img/negx.jpg', 'img/posy.jpg','img/negy.jpg', 'img/posz.jpg','img/negz.jpg', ]; //ローダーで画像読み込み const loader = new THREE.CubeTextureLoader(); const textureCube = loader.load(urls); //反射マッピングの設定 textureCube.mapping = THREE.CubeReflectionMapping; const geometry = new THREE.SphereGeometry(1,64,64); const material = new THREE.MeshPhongMaterial({ envMap:textureCube, }); const sphere = new THREE.Mesh(geometry,material);
● script.js
まず、「Three.jsで360度パノラマコンテンツ制作」でやったように、必要なライブラリを読み込みます。
<script src="js/preloadjs.min.js"></script> <script src="js/TweenMax.min.js"></script>
Three.js関連のライブラリはscript.jsからインポートするので、script.jsはtype="module"をつけて読み込みます。
<script src="js/script.js" type="module"></script>
完成したscript.jsです。「Three.jsでオブジェクトを選択」でやったように影をつけています。
//=============================================================== // Import Library //=============================================================== import * as THREE from './lib/three_jsm/three.module.js'; import { OrbitControls } from './lib/three_jsm/OrbitControls.js'; import { VRButton } from './lib/three_jsm/VRButton.js'; //=============================================================== // Main //=============================================================== window.addEventListener('load',function(){ init(); }); let scene,camera,renderer; let orbitControls; let texture; function init(){ scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,0.1,1000); camera.position.set(0,1.6,3); scene.add(camera); renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth,window.innerHeight); renderer.setClearColor(new THREE.Color(0x000000)); renderer.shadowMap.enabled = true; const container = document.querySelector('#canvas_vr'); container.appendChild(renderer.domElement); document.body.appendChild(VRButton.createButton(renderer)); window.addEventListener('resize',function(){ camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth,window.innerHeight); },false); checkDevice(); setLoading(); } function checkDevice(){ if ('xr' in navigator) { navigator.xr.isSessionSupported('immersive-vr').then(function(supported){ if(supported){ renderer.xr.enabled = true; }else{ setController(); } }); } else { setController(); } } function setLoading(){ TweenMax.to('.loader',0.1,{opacity:1}); const manifest = [ {id:'ground',src:'./img/ground.png'} ]; const loadQueue = new createjs.LoadQueue(); loadQueue.on('progress',function(e){ const progress = e.progress; }); loadQueue.on('complete',function(){ const image = loadQueue.getResult('ground'); texture = new THREE.Texture(image); texture.needsUpdate = true; TweenMax.to('#loader_wrapper',1,{ opacity:0, onComplete: function(){ document.getElementById('loader_wrapper').style.display ='none'; } }); threeWorld(); setLight(); rendering(); }); loadQueue.loadManifest(manifest); } function threeWorld(){ texture.repeat.set(50, 50); texture.wrapS = texture.wrapT = THREE.RepeatWrapping; texture.magFilter = THREE.NearestFilter; let geometry = new THREE.PlaneGeometry(250,250); let material = new THREE.MeshStandardMaterial({map:texture,roughness:0.0,metalness:0.6}); const floor = new THREE.Mesh(geometry,material); floor.rotation.x = -Math.PI/2; floor.receiveShadow = true; scene.add(floor); const 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', ]; const loader = new THREE.CubeTextureLoader(); const textureCube = loader.load(urls); textureCube.mapping = THREE.CubeReflectionMapping; geometry = new THREE.SphereGeometry(1.25,64,64); material = new THREE.MeshPhongMaterial({ envMap:textureCube, }); const sphere = new THREE.Mesh(geometry,material); sphere.position.x = 0; sphere.position.y = 2; sphere.position.z = -7; sphere.castShadow = true; scene.add(sphere); } function setLight(){ const ambientLight = new THREE.AmbientLight(0xFFFFFF); scene.add(ambientLight); const 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 setController(){ document.addEventListener('touchmove',function(e){e.preventDefault();},{passive:false}); orbitControls = new OrbitControls(camera,renderer.domElement); orbitControls.target.set(0,1.6,0); orbitControls.enableDamping = true; orbitControls.dampingFactor = 0.5; orbitControls.enableZoom = false; } function rendering(){ renderer.setAnimationLoop(animate); } function animate(){ if(orbitControls){ orbitControls.update(); } renderer.render(scene,camera); }
完成したデモになります。ヘッドマウントディスプレイで確認すると、ルーブル美術館が映り込んだ球体が表示されます。