Hello 小伙伴們,如果覺得本文還不錯,記得給個 star , 小伙伴們的 star 是我持續更新的動力!GitHub 地址 簡介 全景圖分兩種 由六張正方形圖片組成的SkyBox 一整張的寬高比為2比1的全景圖片。 今天我就實現一整張全景圖的案例。 思路 我們超贊的設計師畫的中秋全景圖(利用透視 ...
Hello 小伙伴們,如果覺得本文還不錯,記得給個 star , 小伙伴們的 star 是我持續更新的動力!GitHub 地址
簡介
全景圖分兩種
- 由六張正方形圖片組成的SkyBox
- 一整張的寬高比為2比1的全景圖片。
今天我就實現一整張全景圖的案例。
思路
我們超贊的設計師畫的中秋全景圖(利用透視網格輔助PS繪製)
創建一個球體網格,對網格進行x軸反轉,使所有的面點向內
let geometry = new THREE.SphereGeometry( 500, 60, 40 ); geometry.scale( -1, 1, 1 );
使用上面的全景貼圖創建基礎材質
let material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load( 'panorama.jpg'), depthTest: false//此參數控制是否使用像素深度來計算新像素的值 }); let mesh = new THREE.Mesh( geometry, material ); scene.add( mesh );
把相機設置為球的中心點
let camera = new THREE.PerspectiveCamera( 100, window.innerWidth / window.innerHeight, 1, 1100 ); camera.target = new THREE.Vector3( 0, 0, 0 ); camera.position.set(0, 0, 0);
陀螺儀相機控制器,實現移動端陀螺儀控制相機
let controls = new THREE.DeviceOrientationControls( camera );
此時還沒有動畫效果,還需要增加一個實時更新渲染動畫
function animate() { render(); requestAnimationFrame(animate); } function render() { //更新控制器 controls.update(); camera.lookAt( camera.target ); renderer.render(scene, camera); }
簡單案例代碼
這就簡單實現了一個全景圖,貼出以上的全部代碼
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>簡單的全景圖</title> <link rel="stylesheet" type="text/css" href="./css/simple-index.css"> </head> <body> <div id="container"></div> <script type="text/javascript" src="./js/three.min.js"></script> <script type="text/javascript" src="./js/DeviceOrientationControls.js"></script> <script type="text/javascript"> class panorama{ constructor () { this.scene = new THREE.Scene(); this.initCamera(); this.initMesh(); this.initRenderer(); this.animate(); } initCamera () { let camera = this.camera = new THREE.PerspectiveCamera( 100, window.innerWidth / window.innerHeight, 1, 1100 ); camera.position.set(0, 0, 0); this.controls = new THREE.DeviceOrientationControls( camera ); this.controls.connect(); } initMesh () { let geometry = new THREE.SphereGeometry( 500, 60, 40 ); geometry.scale( -1, 1, 1 ); geometry.rotateY(-Math.PI / 2) let material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('./textures/SphericalMap.jpg') }); let mesh = new THREE.Mesh( geometry, material ); this.scene.add( mesh ); } initRenderer () { let container = document.getElementById( 'container' ); let renderer = this.renderer = new THREE.WebGLRenderer({ logarithmicDepthBuffer: true }); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.sortObjects = false; renderer.autoClear = false; container.appendChild( renderer.domElement ); } animate() { this.render(); requestAnimationFrame( ()=>{this.animate()}); } render() { //更新控制器 this.controls.update(); this.renderer.render(this.scene, this.camera); } } new panorama(); </script> </body> </html>
相機
直接上圖,常規的全景漫游的進場效果:
左邊是效果,右邊是相機輔助效果。
思路分析
相機起始在球體接近頂部位置,從上往下看
let camera = new THREE.PerspectiveCamera( 150, window.innerWidth / window.innerHeight, 1, 2000 ); camera.position.set(0, 450, 0);//相機定位在y軸450 camera.target = new THREE.Vector3( 0, -500, 0 );//設置目標點 camera.lookAt( camera.target );//看向y軸負方向
相機有上往下移動到求的中心點(0, 0, 0)。同時,相機目標點從底部(0, -500, 0)轉到背面(0, 0, -500)。把fov從150調整為100,效果更贊了。
new TWEEN.Tween( { y : 450, lat : 0, fov : 150 } ) .to( { y : 0, lat : 90, fov : 100 }, 2500 ) .onUpdate(function() { camera.position.y = this.y; let phi = THREE.Math.degToRad( this.lat ); camera.target.y = -500 * Math.cos( phi ); camera.target.z = -500 * Math.sin( phi ); camera.fov = this.fov; camera.updateProjectionMatrix(); })
進場案例代碼
DEMO: songdy.github.io/panorama/ca…
輔助理解DEMO: songdy.github.io/panorama/ca…
把簡單版加入進場效果
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>玩轉相機</title> <link rel="stylesheet" type="text/css" href="./css/simple-index.css"> </head> <body> <div id="container"></div> <script type="text/javascript" src="./js/three.min.js"></script> <script type="text/javascript" src="./js/DeviceOrientationControls.js"></script> <script type="text/javascript" src="./js/Tween.js"></script> <script type="text/javascript"> class panorama{ constructor () { this.SCREEN_WIDTH = window.innerWidth; this.SCREEN_HEIGHT = window.innerHeight; this.scene = new THREE.Scene(); this.initCamera(); this.initMesh(); this.initRenderer(); this.animate(); this.start(); } initCamera () { let aspect = this.SCREEN_WIDTH / this.SCREEN_HEIGHT; this.camera = new THREE.PerspectiveCamera( 150, 0.5 * aspect, 1, 2000 ); this.camera.position.set(0, 450, 0); this.camera.target = new THREE.Vector3( 0, -1, 0 ); } initMesh () { let geometry = new THREE.SphereGeometry( 500, 60, 40 ); geometry.scale( -1, 1, 1 ); geometry.rotateY(-Math.PI / 2) let material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('./textures/SphericalMap.jpg') }); this.mesh = new THREE.Mesh( geometry, material ); this.scene.add( this.mesh ); } initRenderer () { let container = document.getElementById( 'container' ); let renderer = this.renderer = new THREE.WebGLRenderer({ logarithmicDepthBuffer: true }); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.sortObjects = false; renderer.autoClear = false; container.appendChild( renderer.domElement ); } start () { let {camera} = this; new TWEEN.Tween( { lat : 0, y : camera.position.y, fov : camera.fov } ) .to( { lat: 90, y : 0, fov : 100 }, 2500 ) .delay(1000) .easing(TWEEN.Easing.Cubic.InOut) .repeat(Infinity) .onUpdate(function() { let phi = THREE.Math.degToRad( this.lat ); camera.target.y = -500 * Math.cos( phi ); camera.target.z = -500 * Math.sin( phi ); camera.position.y = this.y; camera.fov = this.fov; camera.updateProjectionMatrix(); }) .start() } animate() { this.render(); requestAnimationFrame( ()=>{this.animate()}); } render() { TWEEN.update(); this.camera.lookAt( this.camera.target ); this.renderer.clear(); this.renderer.render( this.scene, this.camera ); } } new panorama(); </script> </body> </html>