three.js全景搭建 、锚点标注添加、 OrbitControls 轨道方向控制 、 场景切换

本文档详细介绍了如何使用Three.js创建全景场景,并实现从二维点击到三维坐标的转换、锚点绘制、场景切换等功能。通过加载纹理、创建几何体和设置相机,构建了基本的全景环境。同时,文档提到了轨道控制的调整以及添加锚点的关键步骤,包括Raycaster用于获取点击坐标。虽然未实现标注点添加gif图片,但提供了加载图片的方法。此外,还讨论了数据格式设计,用于存储场景和锚点信息。
摘要由CSDN通过智能技术生成

背景: 公司产品预演全景参访
实现: 场景查看 锚点标注
关键点: 二维点击转换三维坐标轴、 锚点绘制、场景切换、数据格式设计

未实现: 标注点添加gif 图片
添加视屏未实践 文档中有 添加视频的材质 可以尝试一下

场景查看

一、 创建一个场景
为了真正能够让你的场景借助three.js来进行显示,我们需要以下几个对象:场景、相机和渲染器,这样我们就能透过摄像机渲染出场景。
直接看官网文档 贼详细 three.js

实操

场景创建

// 场景
const scene = new THREE.Scene();
// 透视相机
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
	/**
    *  透视相机四个参数 :视野角度
    *      长宽比
    *      近截面
    *      远截面
    **/		
    			
 // 渲染器
const renderer = new THREE.WebGLRenderer();
// 渲染大小
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// 几何体 --------
const geometry = new THREE.BoxGeometry();
// 球体 ----
	// SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
  // - radius:球体半径
  // - widthSegments,
  // - heightSegments:水平方向和垂直方向上分段数。widthSegments最小值为3,默认值为8。heightSegments最小值为2,默认值为6。
  // - phiStart:水平方向上的起始角,默认值0
  // - phiLenght:水平方向上球体曲面覆盖的弧度,默认Math.PI * 2
  // - thetaStart : 垂直方向上的起始角, 默认0
  // - thetaLength: 垂直方向是球体曲面覆盖的弧度,默认值为Math.PI
  const geometry = new THREE.SphereGeometry(500, 60, 40)
// -------------

const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

 /**
 	默认情况下,当我们调用scene.add()的时候,物体将会被添加到(0,0,0)坐标。但将使得摄像机和立方体彼此在一起。为了防止这种情况的发生,我们只需要将摄像机稍微向外移动一些即可。
 */
camera.position.z = 5;

const animate = function () {
	requestAnimationFrame( animate );
	cube.rotation.x += 0.01;
	cube.rotation.y += 0.01;
	renderer.render( scene, camera );
};
animate();
	图片加载
	- 加载图片 贴到纹理图  然后add 创建网格 添加到场景
	- 引用图片有两种方式
	(第二种方式 引入图片不清晰 应该是canvas 绘图的原因)
 let demo = new THREE.TextureLoader().load(vrImgurl)
// 方法一
  //防止跨域用canvas作为纹理
      let canvas = document.createElement("canvas");
      canvas.style.backgroundColor = "rgba(255,255,255,0)";
      let context = canvas.getContext("2d");
      let img = new Image();
      img.src = imgurl
      // img.src='';      //绘制全景图
      img.onload = function () {
        canvas.width = this.width;
        canvas.height = this.height;
        context.drawImage(img, 0, 0, this.width, this.height);
        let texture = new THREE.Texture();
        texture.image = canvas;
        texture.needsUpdate = true;//开启纹理更新
        texture.minFilter = THREE.LinearFilter;//minFilter属性:指定纹理如何缩小
        let material = new THREE.MeshBasicMaterial({
          map: texture,
          transparent: false
        });
        mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);
     };
// 方法二、
  let demo = new THREE.TextureLoader().load(vrImgurl)
     let material = new THREE.MeshBasicMaterial({
         map: demo, // 此处使用 demo 的参数 图片更为清晰
         transparent: false,
     })
   mesh = new THREE.Mesh(geometry, material)
             // 几何体  材料(渲染图)
     scene.add(mesh)
  }

场景切换

	1、先获取挂载元素的子节点
	2、由于mesh  和scene 是全局定义 所以需要清除  mesh && scene.remove(mesh)
	3、重新实例化即可
 // 初始化先删除子节点
        let container = document.getElementById('container')
        if (container.childNodes.length) {
            container.removeChild(container.childNodes[0])
        }

轨道控制

`在轨道控制中 全景图方向与鼠标拖拽方向一致  如果需要翻转 则在源码找到`
	function rotateLeft( angle ) {
		sphericalDelta.theta -= angle;
	}
	function rotateUp( angle ) {
		sphericalDelta.phi -= angle;
	}
	// ---- 修改为 即可修正方向
	function rotateLeft( angle ) {
		sphericalDelta.theta += angle;
	}
	function rotateUp( angle ) {
		sphericalDelta.phi += angle;
	}
	import OrbitControls from 'three-orbitcontrols'
	// 在场景初始化完成后 初始化控制器
	  // 初始化控制器
  	const initcontrols = () => {
	    controls = new OrbitControls(camera, renderer.domElement)
	    console.log(controls,'--controls')
	      //是否可以缩放
	      controls.enableZoom = false
	      //是否自动旋转
	      controls.autoRotate = autoRotate // 动态控制是否制动旋转、
	      // 使动画循环使用时阻尼或自转 意思是否有惯性
	      controls.enableDamping = true;
	  }

锚点添加 关键点 二维视图获取三维场景中的点击坐标 (THREE.Raycaster)

1、获取点击时的坐标位置
	1-1、 创建场景时绑定 点击事件
	1-2、在当前相机所正视的世界方向建立 三维向量vector3
	1-3、 根据页面展示大小 设置该向量的x、y 和 z 分量。
	1-4、(重点)使用 光线投射Raycaster 计算鼠标在三维坐标中点击的坐标位置
	1-5、传入鼠标点击点击坐标在三维坐标中的中位置信息 		
2、绘制需要标注的精灵 Sprite
	在下面代码中可以 有具体添加步骤	
3、将绘制的精灵添加到 场景 全局 scene
    // 鼠標点击添加一个 确定点击位置  --  锚点 ---待配置 热点图片
    const onDocumentMouseDown = event => {
     /**
     * 1、 camera.target 当前相机所正视的世界空间方向 赋值给 vector
     * 2、根据配置页面 展示的宽高值 设置XYZ 轴
     * 3、 vector.unproject(camera) 在投影中使用的摄像机。
     * 4、 使用 光线投射Raycaster 计算鼠标在三维坐标中点击的坐标位置
     *     这将创建一个新的raycaster对象。
     *        let raycaster = new THREE.Raycaster(
     *            camera.position,
     *            vector.sub(camera.position).normalize() //初始化
     *        )
     *      Raycaster( origin : Vector3, direction : Vector3, near : Float, far : Float ) {
            origin —— 光线投射的原点向量。
            direction —— 向射线提供方向的方向向量,应当被标准化。
            near —— 返回的所有结果比near远。near不能为负值,其默认值为0。
            far —— 返回的所有结果都比far近。far不能小于near,其默认值为Infinity(正无穷。)
     * 
    */
        isUserInteracting = true
        if (forType === 'Equirectangular') {
            event.preventDefault()
            // let vector = new THREE.Vector3() //三维坐标对象
            let vector = camera.target

            console.log(vector, 'vector预计是坐标轴的位置')
            vector.set(
                ((event.clientX - 248) / (window.innerWidth - 248)) * 2 - 1,
                -((event.clientY - 32) / (window.innerHeight - 32)) * 2 + 1,
                0.5
            )
            // 在投影中使用的摄像机。
            vector.unproject(camera)
            // 这将创建一个新的raycaster对象。
            let raycaster = new THREE.Raycaster(
                camera.position,
                vector.sub(camera.position).normalize() //初始化 光线投射的原点向量
            )
            raycaster.camera = camera
            // 得到 点击的坐标 或 点击的标注点 
            // intersects 每项中的object 的type 可以分辨 点击的是标注还是 场景图
            let intersects = raycaster.intersectObjects(scene.children)
            //如果绘制热点属于激活状态
            // 此处需要判断 是否有两个坐标为0
              
            let isOnShaft = []
            Object.keys(intersects[0].point).forEach(v => {
                if (intersects[0].point[v] === 0) {
                    isOnShaft.push(1)
                }
            })
   //---------------------------添加標注-----------------------------------------
   				// 打开添加热点 true    坐标轴中是否存在两坐标为0  打开删除热点开关 为fasle
            if (refIsHotspot.current && isOnShaft.length < 2 && !refIsDelete.current) {
            	// 绘制热点 
            	//	绘制图片有两种方式
            	//	一种直接引入 new img	
            	//	一种使用 canvas  
            	/**
            		 	let canvas = document.createElement('canvas')
		                canvas.style.backgroundColor = 'rgba(255,255,255,0)'
		                let context = canvas.getContext('2d')
		                canvas.width = 128
		                canvas.height = 128
		                 context.drawImage(img, 0, 0, 128, 128)
           		*/
                let img = new Image()
                img.src = hotspot //( 标注使用的图片) 也可以使用canvas 绘制文字 
                img.onload = function () {
                    let texture = new THREE.Texture(img)
                    texture.needsUpdate = true
                    texture.minFilter = THREE.LinearFilter
                    var spriteMaterial = new THREE.SpriteMaterial({
                        map: texture,
                        transparent: false,
                    })
                     // 创建一个 sprite  物体
                    var sprite = new THREE.Sprite(spriteMaterial)
                    sprite.scale.set(30, 30, 30)
                    let rate = 0.8
                    var endV = new THREE.Vector3(
                        intersects[0].point.x * rate,
                        intersects[0].point.y * rate,
                        intersects[0].point.z * rate
                    )
                    sprite.position.copy(endV)
                    scene.add(sprite)
                    // addHotspot(intersects[0].point) //同步到全局数据
                }
                //移除热点
            } else {
                if (!refIsDelete.current) return
                if (intersects.length > 0) {
                    const target = intersects[0]
                    console.log(!refIsHotspot.current, refIsDelete.current, '删除打印结果')
                    try {
                        if (target.object && target.object.type.length > 0) {
                            if (target.object.type.toLowerCase() === 'sprite') {
                                scene.remove(target.object)
                            }
                        }
                    } catch (e) {
                        console.log(e)
                    }
                }
            }
        }
    }

初始化 绘制热点


    //绘制多个跳转热点
    const drawJumpHotSpots = (variable, newsrc) => {
        console.log(variable, '锚点坐标轴数据 参数')
        variable.forEach(item => {
            let position = item.point
            // TextureLoader 异步记载图片
            var texture = new THREE.TextureLoader().load(gif)
            // SpriteMaterial 材质
            var spriteMaterial = new THREE.SpriteMaterial({
                map: texture,
                transparent: true,
            })
            // 物体 Sprite
            var sprite = new THREE.Sprite(spriteMaterial)
            sprite.scale.set(30, 30, 30)
            /**
             * 此处添加自定义属性 不能跟原有属性重复避免报错
             * name: 添加锚点名称
             * ids: 唯一ID
             * iconUrl: 图标
             */
            sprite.name = item.name
            sprite.ids = item.id
            sprite.iconUrl = ''
            let rate = 0.8
            var endV = new THREE.Vector3(position.x * rate, position.y * rate, position.z * rate)
            sprite.position.copy(endV)
            scene.add(sprite)
        })
    }

操作

纹理图引入图片 ( 材质 加载图片)
// 方案一、
      	let texture = new THREE.TextureLoader().load(img)
       // // TextureLoader 异步记载图片
       var texture = new THREE.TextureLoader().load(gif)
       //  SpriteMaterial 材质
        var spriteMaterial = new THREE.SpriteMaterial({
           map: texture,
           transparent: true,
       })  
// 方案二、
        let texture = new THREE.Texture(img)
        texture.needsUpdate = true
        texture.minFilter = THREE.LinearFilter
        var spriteMaterial = new THREE.SpriteMaterial({
            map: texture,
            transparent: false,
        })
         // 创建一个 sprite  物体
        var sprite = new THREE.Sprite(spriteMaterial)
鼠标点击二维坐标 转换三维坐标轴 信息 绘制标注点 ( Raycaster)
	// 我其实也没太理解里面的 API的具体参数
	// camera.target 全局声明的 透视相机
	//	(event.clientX - 248) / (window.innerWidth - 248)) * 2 - 1
		// 页面布局 全景图距离左方 248px 
	// (event.clientY - 32) / (window.innerHeight - 32)) * 2 + 1
		// 页面布局 全景图距离左方32pX	
	//  scene.children  >>> scene 为全局声明  let scene = new THREE.Scene()
		
 	let vector = camera.target
    console.log(vector, 'vector预计是坐标轴的位置')
     vector.set(
         ((event.clientX - 248) / (window.innerWidth - 248)) * 2 - 1,
         -((event.clientY - 32) / (window.innerHeight - 32)) * 2 + 1,
         0.5
     )
     // 在投影中使用的摄像机。
     vector.unproject(camera)
     // 这将创建一个新的raycaster对象。
     let raycaster = new THREE.Raycaster(
         camera.position,
         vector.sub(camera.position).normalize() //初始化 光线投射的原点向量
     )
     raycaster.camera = camera
     // 得到 点击的坐标 或 点击的标注点 
     // intersects 每项中的object 的type 可以分辨 点击的是标注还是 场景图
     点击的坐标点上的 物体
     let intersects = raycaster.intersectObjects(scene.children)
     //如果绘制热点属于激活状态
     // 此处需要判断 是否有两个坐标为0 两个为0 时 添加的锚点会很大
     let isOnShaft = []  // 是否在坐标轴上
     Object.keys(intersects[0].point).forEach(v => {
         if (intersects[0].point[v] === 0) {
             isOnShaft.push('靓仔')
         }
     })
坐标轴展示 开发实用 (AxesHelper)
	  //  三维坐标轴 坐标轴长度
      var axesHelper = new THREE.AxesHelper(150);
      scene.add(axesHelpe  r);
数据格式设计
 panoramicData: [
        {
            name: '会所',
            id: '2102271653',
            url: 'huisuo',
            active: true,

            // 锚点信息
            anchorPoint: [
                {
                    point: {
                        x: 180.01349809670057,
                        y: 15.79023683858044,
                        z: 465.07418151652786,
                    },
                    id: '2102091411',
                    name: '海边',
                    iconUrl: 'haibian',
                },
                {
                    point: {
                        x: 247.4793362659326,
                        y: -189.1800093391692,
                        z: 390.2798175065487,
                    },
                    id: '202102181619',
                    name: '客厅',
                    iconUrl: 'keting',
                },
            ],
        },
        {
            name: '海边',
            id: '2102091411',
            url: 'haibian',
            active: false,
            autoRotate: false,
            // 锚点信息
            anchorPoint: [
                {
                    point: {
                        x: 374.5454984418328,
                        y: -5.458415157221607,
                        z: 330.55353704327746,
                    },
                    id: '202102181621',
                    name: '豪宅',
                    iconUrl: 'haozhai',
                },
                {
                    point: {
                        x: 140.18787952741366,
                        y: -97.9969695393665,
                        z: 468.933553788003,
                    },
                    id: '202102181619',
                    name: '客厅',
                    iconUrl: 'keting',
                },
            ],
        },
        {
            name: '客厅',
            id: '202102181619',
            url: 'keting',
            active: false,
            autoRotate: false,
            // 锚点信息
            anchorPoint: [
                {
                    point: {
                        x: 481.8527362463277,
                        y: -24.6389543862957,
                        z: 127.17004633132723,
                    },
                    id: '2102271653',
                    name: '会所',
                    iconUrl: 'huisuo',
                },
                {
                    point: {
                        x: 347.09301641855546,
                        y: -109.56249057173801,
                        z: 341.54549425701236,
                    },
                    id: '2102091411',
                    name: '海边',
                    iconUrl: 'haibian',
                },
            ],
        },
        {
            name: '豪宅',
            id: '202102181621',
            url: 'haozhai',
            active: false,
            autoRotate: false,
            // 锚点信息
            anchorPoint: [
                {
                    point: {
                        x: 85.2120582814672,
                        y: -0.4428222360704279,
                        z: 492.0249309249495,
                    },
                    id: '2102271653',
                    name: '会所',
                    iconUrl: 'huisuo',
                },
                {
                    point: {
                        x: 441.06070438164795,
                        y: -157.51582618415583,
                        z: 173.01486267642798,
                    },
                    id: '2102091411',
                    name: '海边',
                    iconUrl: 'haibian',
                },
            ],
        },
    ],
实现场景切换的方法在Three.js中有多种方式。一种常见的方法是使用场景切换的特性,在Three.js中,可以通过创建不同的场景(Scene)并将其放置在一个场景管理器(Scene Manager)中来实现场景切换。通过调用场景管理器的方法,可以在不同的场景之间进行切换。这种方法适用于需要在不同场景之间切换的应用,比如产品预览、全景参观等。 另一种方法是使用摄像机的切换来实现场景切换。通过修改摄像机的位置和视角,可以让用户感觉到场景切换。这种方法适用于需要在相机视角之间进行切换的应用。通过动画或者点击事件,可以改变摄像机的位置和目标点,从而实现场景切换效果。这种方法比较灵活,可以根据具体需求进行定制和扩展。 另外,还可以使用其他的特效库,比如Tween.js或GSAP等,来实现场景切换效果。这些库可以实现丰富的过渡效果,比如淡入淡出、旋转、缩放等,可以让场景切换更加生动和流畅。 总结起来,Three.js可以通过场景管理器、摄像机的切换以及特效库等方式来实现场景切换效果。具体的实现方法可以根据具体的需求和项目来选择,可以根据场景切换的复杂度和性能要求来决定使用哪种方法。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [threejs 切换场景](https://download.csdn.net/download/rui913/86266357)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [three.js全景搭建锚点标注添加、 OrbitControls 轨道方向控制场景切换](https://blog.csdn.net/qq_42359718/article/details/114012073)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值