Three.jsによるWebGLで360度パノラマ表示
以下の様な360°パノラマ画像(本来の意味とは違ってくるそうですがHDRIと呼ばれることも)を元にパノラマ表示をする方法はいくつかありますが、
Three.jsを使うと、WebGLの技術を使って、
簡単に3D映像の背景として360度のパノラマを表示することができます。
↓こちらが実際に作ってみたもの
仕組み・方法
仕組みは360°パノラマ画像の画像をテクスチャとして設定した大きな球体の内側を、
球体の中心から見るようにカメラを設置するというものです。
HTML
1 2 3 | <div id="canvas-frame"></div> <script src="js/three.min.js"></script> |
HTMLではWebGLを表示するCanvasを設置する要素を設置しておき、
予めThree.jsの公式サイトからダウンロードして設置しておいたThree.jsのファイルを読み込んでおきます。
JavaScript
以下の様なscriptを記述して、設置したい背景となる360°パノラマ画像の場所を変更して完了です。
コードは公式のDEMOを元に、スマホでの操作にも対応させました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | "use strict"; //イベントの振り分け var EVENT = {}; if ('ontouchstart' in window) { EVENT.TOUCH_START = 'touchstart'; EVENT.TOUCH_MOVE = 'touchmove'; EVENT.TOUCH_END = 'touchend'; } else { EVENT.TOUCH_START = 'mousedown'; EVENT.TOUCH_MOVE = 'mousemove'; EVENT.TOUCH_END = 'mouseup'; } (function () { var camera, scene, renderer; var cube, sphere, torus, material; var count = 0, cubeCamera1, cubeCamera2; var fov = 60, isUserInteracting = false, onMouseDownMouseX = 0, onMouseDownMouseY = 0, lon = 0, onMouseDownLon = 0, lat = 0, onMouseDownLat = 0, phi = 0, theta = 0; var textureLoader = new THREE.TextureLoader(); var wrapperElm = document.getElementById("canvas-frame"); //↓ここのファイル名を変更して、背景のHDRIを変更 textureLoader.load( 'images/GravelPlaza_4k.jpg', function ( texture ) { texture.mapping = THREE.UVMapping; init( texture ); animate(); } ); function init( texture ) { camera = new THREE.PerspectiveCamera( fov, wrapperElm.clientWidth / wrapperElm.clientHeight, 1, 1000 ); scene = new THREE.Scene(); var mesh = new THREE.Mesh( new THREE.SphereGeometry( 500, 32, 16 ), new THREE.MeshBasicMaterial( { map: texture } ) ); mesh.scale.x = -1; scene.add( mesh ); renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( wrapperElm.clientWidth, wrapperElm.clientHeight ); cubeCamera1 = new THREE.CubeCamera( 1, 1000, 256 ); cubeCamera1.renderTarget.texture.minFilter = THREE.LinearMipMapLinearFilter; scene.add( cubeCamera1 ); cubeCamera2 = new THREE.CubeCamera( 1, 1000, 256 ); cubeCamera2.renderTarget.texture.minFilter = THREE.LinearMipMapLinearFilter; scene.add( cubeCamera2 ); wrapperElm.appendChild( renderer.domElement ); // document.addEventListener( EVENT.TOUCH_START, onDocumentMouseDown, false ); document.addEventListener( 'mousewheel', onDocumentMouseWheel, false ); document.addEventListener( 'MozMousePixelScroll', onDocumentMouseWheel, false); window.addEventListener( 'resize', onWindowResized, false ); onWindowResized( null ); } function onWindowResized( event ) { renderer.setSize( wrapperElm.clientWidth, wrapperElm.clientHeight ); camera.projectionMatrix.makePerspective( fov, wrapperElm.clientWidth / wrapperElm.clientHeight, 1, 1100 ); } function onDocumentMouseDown( event ) { event.preventDefault(); if(event.clientX) { onMouseDownMouseX = event.clientX; onMouseDownMouseY = event.clientY; } else if(event.touches) { onMouseDownMouseX = event.touches[0].clientX onMouseDownMouseY = event.touches[0].clientY; } else { onMouseDownMouseX = event.changedTouches[0].clientX onMouseDownMouseY = event.changedTouches[0].clientY } onMouseDownLon = lon; onMouseDownLat = lat; document.addEventListener( EVENT.TOUCH_MOVE, onDocumentMouseMove, false ); document.addEventListener( EVENT.TOUCH_END, onDocumentMouseUp, false ); } function onDocumentMouseMove( event ) { event.preventDefault(); if(event.clientX) { var touchClientX = event.clientX; var touchClientY = event.clientY; } else if(event.touches) { var touchClientX = event.touches[0].clientX var touchClientY = event.touches[0].clientY; } else { var touchClientX = event.changedTouches[0].clientX var touchClientY = event.changedTouches[0].clientY } lon = ( touchClientX - onMouseDownMouseX ) * -0.1 + onMouseDownLon; lat = ( touchClientY - onMouseDownMouseY ) * -0.1 + onMouseDownLat; } function onDocumentMouseUp( event ) { document.removeEventListener( EVENT.TOUCH_MOVE, onDocumentMouseMove, false ); document.removeEventListener( EVENT.TOUCH_END, onDocumentMouseUp, false ); } function onDocumentMouseWheel( event ) { // WebKit if ( event.wheelDeltaY ) { fov -= event.wheelDeltaY * 0.05; // Opera / Explorer 9 } else if ( event.wheelDelta ) { fov -= event.wheelDelta * 0.05; // Firefox } else if ( event.detail ) { fov += event.detail * 1.0; } camera.projectionMatrix.makePerspective( fov, wrapperElm.clientWidth / wrapperElm.clientHeight, 1, 1100 ); } function animate() { requestAnimationFrame( animate ); render(); } function render() { var time = Date.now(); lon += .15; lat = Math.max( - 85, Math.min( 85, lat ) ); phi = THREE.Math.degToRad( 90 - lat ); theta = THREE.Math.degToRad( lon ); camera.position.x = 100 * Math.sin( phi ) * Math.cos( theta ); camera.position.y = 100 * Math.cos( phi ); camera.position.z = 100 * Math.sin( phi ) * Math.sin( theta ); camera.lookAt( scene.position ); // pingpong if ( count % 2 === 0 ) { cubeCamera2.updateCubeMap( renderer, scene ); } else { cubeCamera1.updateCubeMap( renderer, scene ); } count ++; renderer.render( scene, camera ); } })(); |
3Dのオブジェクトを中に設置することもでき、
背景の映り込みも設定できます。
割りと簡単に設置できますので、
利用しやすいのではないかと思います。
2018.7.10 追記
↓動画版の方法はこちら
参考サイト
three.js – Javascript 3D library
three.js/webgl_materials_cubemap_dynamic2.html at master · mrdoob/three.js · GitHub
HDRI素材元
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE