一、功能介绍
1、模块功能
* 【模块功能】
* ThreeJs的初始化涉及相机、场景、光源、鼠标操控器、Axe辅助器、渲染循环、自适应根据窗口大小调整等一系列常规基础功能实现。
* 基本上每个项目都要做这些初始化
* 为了项目间方便复用,减少冗余代码,现将这些功能统一封装起来方便调用
2、输入输出
* 【输入】
* 1、ThreeJs的画布所放置的html元素<div>
* 【输出】
* 1、Web3DRender对象
* 【可自定义调整内容】
* 1、相机
* 2、光源
* 3、鼠标操控器
* 4、axe辅助器
二、核心逻辑
1、场景
__create_scene(){const scene = new THREE.Scene();scene.background = new THREE.Color( 0xAAAAAA );return scene}
2、渲染器
__create_render(){const renderer = new THREE.WebGLRenderer();renderer.setPixelRatio( window.devicePixelRatio );// 我们需要跟随者canvas_element的大小renderer.setSize(this.canvas_element.clientWidth, this.canvas_element.clientHeight);renderer.toneMapping = THREE.ACESFilmicToneMapping;renderer.toneMappingExposure = 1;this.canvas_element.appendChild(renderer.domElement);return renderer}
3、渲染循环
_animate(){const self = thisrequestAnimationFrame(animateLoop)this._updateCameraInfo()this.renderer.render(this.scene, this.camera);function animateLoop() {self._animate()}}
4、摄像机
__create_camera() {const camera = new THREE.PerspectiveCamera(75, this.canvas_element.clientWidth / this.canvas_element.clientHeight, 0.1, 1000);camera.position.set(0.5, 0.6, 0.8); // 设置相机位置camera.lookAt(this.scene.position); // 让相机朝向场景中心return camera}
5、鼠标控制器
__create_controls() {const controls = new OrbitControls( this.camera, this.renderer.domElement );controls.enableDamping = true;controls.target.set( 0, 0.1, 0 );controls.update();controls.minDistance = 0.5;controls.maxDistance = 1000;controls.maxPolarAngle = 0.5 * Math.PI;return controls}
三、封装代码
/*** 【模块功能】* ThreeJs的初始化涉及相机、场景、光源、鼠标操控器、Axe辅助器、渲染循环、自适应根据窗口大小调整等一系列常规基础功能实现。* 基本上每个项目都要做这些初始化* 为了项目间方便复用,减少冗余代码,现将这些功能统一封装起来方便调用** 【输入】* 1、ThreeJs的画布所放置的html元素<div>* 【输出】* 1、Web3DRender对象* 【可自定义调整内容】* 1、相机* 2、光源* 3、鼠标操控器* 4、axe辅助器* **/import * as THREE from "three"
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';class Web3DRenderer {constructor(canvas_element) {this.canvas_element = canvas_element;this.scene = this.__create_scene()this.renderer = this.__create_render()this.__add_light()this.camera = this.__create_camera()this.controls = this.__create_controls()this.cameraInfo = this.__create_camera_info()this.axesHelper = this.__add_axes_helper()this._animate()// 处理大小调整事件,自适应跟随窗口大小调整window.addEventListener('resize', () => {this.renderer.setSize(this.canvas_element.clientWidth, this.canvas_element.clientHeight);this.camera.aspect = this.canvas_element.clientWidth / this.canvas_element.clientHeight;this.camera.updateProjectionMatrix();this._animate();});}_animate(){const self = thisrequestAnimationFrame(animateLoop)this._updateCameraInfo()this.renderer.render(this.scene, this.camera);function animateLoop() {self._animate()}}__create_camera_info() {const cameraInfoElement = document.createElement("div")this.canvas_element.appendChild(cameraInfoElement)cameraInfoElement.style.position = "absolute"cameraInfoElement.style.top = "1%"cameraInfoElement.style.left = "1%"cameraInfoElement.style.backgroundColor = "black";cameraInfoElement.style.color = "white"cameraInfoElement.style.padding = "10px"cameraInfoElement.style.fontFamily = "Arial, sans-serif"/* #camera-info {position: absolute;top: 10px;left: 10px;background-color: rgba(0, 0, 0, 0.5);color: white;padding: 10px;font-family: Arial, sans-serif;}*/return cameraInfoElement}_updateCameraInfo() {const camera = this.camerathis.cameraInfo.innerHTML = `摄像头信息:<br>位置: (${camera.position.x.toFixed(2)}, ${camera.position.y.toFixed(2)}, ${camera.position.z.toFixed(2)})<br>角度: (${camera.rotation.x.toFixed(2)}, ${camera.rotation.y.toFixed(2)}, ${camera.rotation.z.toFixed(2)})<br>缩放: ${camera.zoom.toFixed(2)}`;}__create_scene(){const scene = new THREE.Scene();scene.background = new THREE.Color( 0xAAAAAA );return scene}__create_render(){const renderer = new THREE.WebGLRenderer();renderer.setPixelRatio( window.devicePixelRatio );// 我们需要跟随者canvas_element的大小renderer.setSize(this.canvas_element.clientWidth, this.canvas_element.clientHeight);renderer.toneMapping = THREE.ACESFilmicToneMapping;renderer.toneMappingExposure = 1;this.canvas_element.appendChild(renderer.domElement);return renderer}__add_light() {this.scene.add( new THREE.DirectionalLight( 0xffffff, 2 ) );this.scene.add(new THREE.AmbientLight(0xffffff, 2))}__create_camera() {const camera = new THREE.PerspectiveCamera(75, this.canvas_element.clientWidth / this.canvas_element.clientHeight, 0.1, 1000);camera.position.set(0.5, 0.6, 0.8); // 设置相机位置camera.lookAt(this.scene.position); // 让相机朝向场景中心return camera}__create_controls() {const controls = new OrbitControls( this.camera, this.renderer.domElement );controls.enableDamping = true;controls.target.set( 0, 0.1, 0 );controls.update();controls.minDistance = 0.5;controls.maxDistance = 1000;controls.maxPolarAngle = 0.5 * Math.PI;return controls}__add_axes_helper() {//辅助观察的坐标系const axesHelper = new THREE.AxesHelper(100);this.scene.add(axesHelper);return axesHelper}}export {Web3DRenderer
};
四、演示使用代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Three.js GUI复杂案例</title><style>body { margin: 0; overflow: hidden; }#three-3d {width: 100vw;height: 100vh;}</style>
</head>
<body>
<div id="three-3d"></div>
<div id="processBar"></div>
<script type="importmap">{"imports": {"three": "../../three.js-master/build/three.module.js","three/addons/": "../../three.js-master/examples/jsm/","threeBase/": "../"}}
</script>
<script type="module">import * as THREE from "three"import { Web3DRenderer } from 'threeBase/Scene/Web3DRender.js';import {ResourceType, ToLoadResource, ResourceLoader, ResourceLoadState} from "threeBase/Model/ResourceLoader.js"const threeElement = document.getElementById("three-3d")const web3dRender = new Web3DRenderer(threeElement)const scene = web3dRender.sceneconst glb_resourceInfo = new ToLoadResource("../../three.js-master/examples/models/gltf/SheenChair.glb", ResourceType.GLB_TYPE)const back_textureInfo = new ToLoadResource("../../three.js-master/examples/textures/equirectangular/royal_esplanade_1k.hdr", ResourceType.RGBE_TYPE)const processBarElement = document.getElementById("processBar")const resLoader = new ResourceLoader()resLoader.loadResource([glb_resourceInfo, back_textureInfo], processBarElement, succFunc, processFunc, failFunc)function succFunc(resourceStateInfoList) {const [modelInfo, textureInfo] = resourceStateInfoListscene.add(modelInfo.resouceObj)scene.background = textureInfo.resouceObj;scene.environment = textureInfo.resouceObj;console.log("succ!")}function processFunc(resourceStateInfoList) {console.log("process!")}function failFunc(resourceStateInfoList) {console.log("fail!")}
</script>
</body>
</html>