/** * 初始化场景 */ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import * as THREE from 'three' import { PCDLoader } from 'three/examples/jsm/loaders/PCDLoader.js' // 类声明 class SceneInit { /** * 构造函数,用来初始化 * @param dom dom * @param cameraObj */ constructor(dom, cameraObj) { console.log('dom', dom) console.log('cameraObj', cameraObj) this.dom = dom // 创建dom属性 this.groupObj = { // 创建groupObj属性 gridHelperGroup: undefined, } // render前的回调函数集合 this.animateCallBackArray = [] this.init(cameraObj) } /** * 初始化 */ init(cameraObj) { // 初始化Renderer this.initRenderer() // 初始化摄像头 this.initCamera(cameraObj) // 初始化场景 this.initScene() // 初始化灯源 this.initLight() // 摄像头控制器 this.initControls() this.animateCallBackArray = [] // 动画 // setTimeout(() => { this.animate() this.resizeFun = this.updataWh.bind(this) window.addEventListener('resize', this.resizeFun) } /** * 初始化渲染器 */ initRenderer() { this.renderer = new THREE.WebGLRenderer({ alpha: this.alpha, // 画布透明 depthTest: true, // 深度测试 antialias: true, // 是否开启抗锯齿效果,默认值为false。 logarithmicDepthBuffer: true, // 对数深度缓存 }) // width和height用来设置Three.js输出的Canvas画布尺寸(像素px) this.width = this.dom.clientWidth // 窗口文档显示区的宽度作为画布宽度 this.height = this.dom.clientHeight // 窗口文档显示区的高度作为画布高度 this.renderer.localClippingEnabled = true // 启用局部裁剪 this.renderer.setSize(this.width, this.height) // 设置three.js渲染区域的尺寸(像素px) this.renderer.shadowMap.enabled = true // 如果遇到阴影贴图 必须渲染 this.renderer.sortObjects = true // 用于控制three.js中物体的渲染顺序 this.renderer.setPixelRatio(window.devicePixelRatio * 1) // 用于设置设备的像素比 通过设置设备像素比,可以优化高分辨率设备上的渲染效果,确保图形在不同分辨率的设备上都能清晰显示。 this.renderer.domElement.style.position = 'absolute' this.renderer.domElement.style.top = '0px' this.renderer.domElement.style.left = '0px' // three.js执行渲染命令会输出一个canvas画布,也就是一个HTML元素,你可以插入到web页面中 this.dom.appendChild(this.renderer.domElement) this.renderer.domElement.classList.add('threeCanvas') // 设置画布的class,非必须 // this.renderer.outputEncoding = THREE.sRGBEncoding; this.renderer.gammaFactor = 2.2 this.renderer.gammaOutPut = true this.renderer.shadowMap.type = THREE.PCFSoftShadowMap this.renderer.toneMappingExposure = 1.0 // 物理灯光 this.renderer.physicallyCorrectLights = true } /** * 初始化摄像头 */ initCamera(cameraObj) { if (!cameraObj) { cameraObj = { fov: 45, // 摄像机视锥体垂直视野角度。 aspect: this.width / this.height, // 摄像机视锥体长宽比。 near: 0.1, // 摄像机视锥体近端面。 far: 100000, // 摄像机视锥体远端面。 // position: [0, 50, 85], // position: [62, 67, 20], position: [0, 0, 12], } } this.camera = new THREE.PerspectiveCamera( cameraObj.fov, this.width / this.height, // 摄像机视锥体长宽比。 // cameraObj.aspect, cameraObj.near, cameraObj.far, ) this.camera.position.set(...cameraObj.position) // this.camera.position.set([0, 50, 85]) } /** * 初始化场景 */ initScene() { this.scene = new THREE.Scene() } /** * 初始化灯源 */ initLight() { // 默认环境光 const ambientLight = new THREE.AmbientLight(0xFFFFFF) // 环境光 this.scene.add(ambientLight) ambientLight.intensity = 0.3 // 光照强度 const hemiLight = new THREE.HemisphereLight(0xFFFFBB, 0x080820, 1) // 模拟天空和地面的光照效果 this.scene.add(hemiLight) } /** * 初始化控制器 */ initControls() { this.controller = new OrbitControls(this.camera, this.renderer.domElement) } /** *宽高变更时自动适配 */ updataWh() { this.width = this.dom.clientWidth this.height = this.dom.clientHeight this.camera.aspect = this.width / this.height this.camera.updateProjectionMatrix() this.renderer.setSize(this.width, this.height) } /** * 动画渲染 * threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame实现动画渲染。 **/ animate(time) { // 请求动画帧window.requestAnimationFrame // requestAnimationFrame实现周期性循环执行 // requestAnimationFrame默认每秒钟执行60次,但不一定能做到,要看代码的性能 this.animateKey = requestAnimationFrame(this.animate.bind(this)) if (this.controller) { this.controller.update() } // 额外回调 for (let i = 0; i < this.animateCallBackArray.length; i++) { if (this.animateCallBackArray[i]) { this.animateCallBackArray[i](time, this.renderer, this.scene) } } this.renders() } /** * 网格辅助线 */ grid() { // 网格的大小(宽度和高度) 它定义了从中心到边缘的距离,整个网格的实际尺寸是2 * size const size = 1000 // 网格的划分数量,即网格线上有多少个分割点 const divisions = 60 // 主要颜色,默认为0x444444(深灰色),用于主轴线 const colorMain = '0x444444' // 次要颜色,默认为0x888888(浅灰色),用于其他线 const colorSecondary = '0x888888' this.groupObj.gridHelperGroup = new THREE.GridHelper( size, divisions, // colorMain, // colorSecondary, ) this.scene.add(this.groupObj.gridHelperGroup) } /** * 渲染 */ renders() { if (this.renderer) { this.renderer.render(this.scene, this.camera) } } /** * 获取根对象,便于其它类使用公共场景变量 */ getRoot() { return { scene: this.scene, renderer: this.renderer, controller: this.controller, camera: this.camera, addAnimateCallBack: this.addAnimateCallBack, removeAnimateCallBack: this.removeAnimateCallBack, } } /** * * 更新前的回调,暂用于模型矩阵变化动画 */ addAnimateCallBack = (fun) => { const index = this.animateCallBackArray.length this.animateCallBackArray.push(fun) return index } /** * * 更新前的回调,暂用于模型矩阵变化动画 */ removeAnimateCallBack = (index) => { this.animateCallBackArray[index] = false } /** * 销毁,释放内存 */ destroy() { cancelAnimationFrame(this.animateKey) this.scene.traverse((child) => { if (child instanceof THREE.Mesh) { if (child.material) { child.material.dispose && child.material.dispose() } if (child.geometry) { child.geometry && child.geometry.dispose() } !!child.clear && child.clear() child = null } }) this.scene.environment = null this.scene.clear() const dom = this.renderer.domElement if (dom) { dom.parentNode.removeChild(dom) } this.renderer.renderLists.dispose() this.renderer.dispose() this.renderer.forceContextLoss() this.renderer.domElement = null this.renderer.content = null this.renderer = null window.removeEventListener('resize', this.resizeFun) } // 加载点云 loadPCD(padPath) { const loader = new PCDLoader() loader.load(padPath, (points) => { this.scene.add(points) // 直接将点云添加到场景中 this.animate() }) } // 加载三维地图 load3DMap() { // const tileMap = new TileMap({ // renderer: this.renderer, // camera: this.camera, // scene: this.scene, // }) // tileMap.load() } } export default SceneInit