【threejs效果】全景图效果

已被阅读 818 次 | 文章分类:javascript | 2021-12-03 23:58

全景图,即通过广角的表现手段以及绘画、相片、视频、三维模型等形式,尽可能多表现出周围的环境;

1 效果一

官网实现方式,效果如下

/net/upload/image/20211203/16507592-84a2-4e36-b008-be1b3a50f9bd.gif

实现该效果需要这样一张图片

/net/upload/image/20211203/4f8a29cf-bbb6-49cc-a9d5-e0d742d3d85b.jpg

全部代码

                                            
import * as THREE from 'three'
let scene,camera,gridHelper,axes,renderer;
initScene();
function initScene(){
    // ---------------------------------------------------------------------
    // 场景和相机
    // ---------------------------------------------------------------------
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000);
    // 环境光
    var ambientLight = new THREE.AmbientLight("white");
    scene.add(ambientLight);  
    // ---------------------------------------------------------------------
    // 渲染器
    // ---------------------------------------------------------------------
    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );


    // 添加球体模型
    let path="./statics/imgs/texture1.jpg";
    const geometry = new THREE.SphereGeometry(500,60,40 );
    geometry.scale(-1, 1, 1 );    // 翻转几何体,让所有面指向内心
    const texture = new THREE.TextureLoader().load(path);
    const material = new THREE.MeshBasicMaterial( { map: texture } );
    const mesh = new THREE.Mesh( geometry, material );
    scene.add(mesh);
    animate();
    document.body.style.touchAction = 'none';
    document.body.addEventListener('pointerdown', onPointerDown );
    document.addEventListener('wheel', onDocumentMouseWheel );
    document.addEventListener('dragover', function (event) {
      event.preventDefault();
      event.dataTransfer.dropEffect = 'copy';
    });
    document.addEventListener('dragenter', function () {
      document.body.style.opacity = 0.5;
    });
    document.addEventListener('dragleave', function () {
      document.body.style.opacity = 1;
    });
    document.addEventListener('drop', function (event) {
      event.preventDefault();
      const reader = new FileReader();
      reader.addEventListener( 'load', function ( event ) {
        material.map.image.src = event.target.result;
        material.map.needsUpdate = true;
      });
      reader.readAsDataURL( event.dataTransfer.files[ 0 ] );
      document.body.style.opacity = 1;
    });
    window.addEventListener('resize', onWindowResize );
}
  var isUserInteracting = false,
  onPointerDownMouseX = 0, 
  onPointerDownMouseY = 0,
  lon = 0, onPointerDownLon = 0,
  lat = 0, onPointerDownLat = 0,
  phi = 0, theta = 0;

  // 浏览器窗口
  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
  }
  // 鼠标按下事件
  function onPointerDown( event ) {
    if ( event.isPrimary === false ) return;
    isUserInteracting = true;
    onPointerDownMouseX = event.clientX;
    onPointerDownMouseY = event.clientY;
    onPointerDownLon = lon;
    onPointerDownLat = lat;
    document.addEventListener( 'pointermove', onPointerMove );
    document.addEventListener( 'pointerup', onPointerUp );
  }
  // 鼠标移动事件
  function onPointerMove( event ) {
    if ( event.isPrimary === false ) return;
    lon = ( onPointerDownMouseX - event.clientX ) * 0.1 + onPointerDownLon;
    lat = ( event.clientY - onPointerDownMouseY ) * 0.1 + onPointerDownLat;
  }
  // 鼠标抬起事件
  function onPointerUp() {
    if ( event.isPrimary === false ) return;
    isUserInteracting = false;
    document.removeEventListener( 'pointermove', onPointerMove );
    document.removeEventListener( 'pointerup', onPointerUp );
  }
  // 滚轮滚动事件
  function onDocumentMouseWheel( event ) {
    const fov = camera.fov + event.deltaY * 0.05;
    camera.fov = THREE.MathUtils.clamp( fov, 10, 75 );
    camera.updateProjectionMatrix();
  }

  function animate() {
    requestAnimationFrame(animate);
    update();
    renderer.render(scene, camera);
  }
  // 自动传动事件
  function update() {
    if (isUserInteracting === false ) {
      lon += 0.1;
    }
    lat = Math.max(-85, Math.min( 85, lat) );
    phi = THREE.MathUtils.degToRad( 90 - lat );
    theta = THREE.MathUtils.degToRad( lon );
    const x = 500 * Math.sin( phi ) * Math.cos( theta );
    const y = 500 * Math.cos( phi );
    const z = 500 * Math.sin( phi ) * Math.sin( theta );
    camera.lookAt( x, y, z );
  }
                                            
                                        

2 用球体(SphereGeometry)方式实现以上效果

实现原理较为简单,给球体贴图就可以;前提是要制作出如上提供的纹理图片,代码如下

                                            
import * as THREE from 'three'
import { OrbitControls } from '../utils/controls/OrbitControls';
let scene,camera,gridHelper,axes,renderer,controls;
    // ---------------------------------------------------------------------
    // 场景和相机
    // ---------------------------------------------------------------------
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(157, 73, 241);


    // 环境光
    var ambientLight = new THREE.AmbientLight("white");
    scene.add(ambientLight);  
    // ---------------------------------------------------------------------
    // 渲染器
    // ---------------------------------------------------------------------
    renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );
    controls = new OrbitControls( camera, renderer.domElement );
    // 添加球体模型
    let path="./statics/imgs/texture1.jpg";
    // 添加背景模型
    let texture = new THREE.TextureLoader().load(path);
    let backgroundBall = new THREE.Mesh(
        new THREE.SphereGeometry(500, 128, 128),
        new THREE.MeshBasicMaterial({
            map: texture,
            // side:THREE.DoubleSide
        })
    );
    backgroundBall.geometry.scale(-1, 1, 1);
    scene.add(backgroundBall);
    animate();
  function animate() {
    requestAnimationFrame(animate);
    controls.update(); 
    renderer.render(scene, camera);
  }
                                            
                                        

3 效果二

也是官方提供的demo,效果如下

/net/upload/image/20211203/d5d31d52-95f1-40cf-aef0-2a43fbd6f436.gif

依赖图片

/net/upload/image/20211203/211c005b-0d98-4bca-8578-a8e1af22d6a4.jpg

实现原理:利用BoxGeometry几何体,然后给六个面分别创建贴图材质实现;六个纹理分别从图片中根据宽度读取像素,使用canvas绘制

                                            
import * as THREE from 'three'
import { Reflector } from "three/examples/jsm/objects/Reflector.js"
import { OBJLoader } from '../utils/OBJLoader.js';
import { MTLLoader } from '../utils/MTLLoader.js';
import { OrbitControls } from '../utils/controls/OrbitControls';
let scene,camera,gridHelper,axes,renderer,controls;
initScene();
function initScene(){
    // ---------------------------------------------------------------------
    // 场景和相机
    // ---------------------------------------------------------------------
    scene = new THREE.Scene();
	camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 0.1, 100 );
	camera.position.z = 0.01;
    //光源一定要添加,模型没有光 肯定是黑色
    var light = new THREE.DirectionalLight(0xffffff);//光源颜色
    light.position.set(2000, 1000, 305);//光源位置
    scene.add(light);//光源添加到场景中

    //环境光
    var ambientLight = new THREE.AmbientLight("white");
    scene.add(ambientLight);  
    // ---------------------------------------------------------------------
    // 渲染器
    // ---------------------------------------------------------------------
	renderer = new THREE.WebGLRenderer();
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	document.body.appendChild( renderer.domElement );
	
    controls = new OrbitControls( camera, renderer.domElement );
	controls.enableZoom = false;
	controls.enablePan = false;
	controls.enableDamping = true;
	controls.rotateSpeed = - 0.25;

	animate();
	let path="./statics/imgs/sun_temple_stripe.jpg";
	// 创建6个面的纹理
	const textures = getTexturesFromAtlasFile(path,6);
	const materials = [];
	// 创建6个面的材质
	for ( let i = 0; i < 6; i++) {
		materials.push(new THREE.MeshBasicMaterial({ map:textures[i]}));
	}
	// 创建mesh
	const skyBox = new THREE.Mesh(new THREE.BoxGeometry(1,1,1), materials );
	skyBox.geometry.scale(1,1,-1);
	scene.add(skyBox);
	// 注册浏览器窗口改变事件
	window.addEventListener( 'resize', onWindowResize );
}
	function getTexturesFromAtlasFile( atlasImgUrl, tilesNum ) {
		const textures = [];
		for ( let i = 0; i < tilesNum; i ++ ) {
			textures[i] = new THREE.Texture();
		}
		new THREE.ImageLoader().load(atlasImgUrl, (image) => {
			let canvas, context;
			const tileWidth = image.height;
			for ( let i = 0; i < textures.length; i ++ ) {
				canvas = document.createElement( 'canvas' );
				context = canvas.getContext( '2d' );
				canvas.height = tileWidth;
				canvas.width = tileWidth;
				context.drawImage(image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth );
				textures[i].image = canvas;
				textures[i].needsUpdate = true;
			}
		});
		return textures;
	}
	function onWindowResize() {
		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();
		renderer.setSize( window.innerWidth, window.innerHeight );
	}
	function animate() {
		requestAnimationFrame( animate );
		controls.update(); 
		renderer.render( scene, camera );
	}
                                            
                                        

QQ:3410192267 | 技术支持 微信:popstarqqsmall

Copyright ©2017 xiaobaigis.com . 版权所有 鲁ICP备17027716号