three.js 实践3D渲染
需求分析
因为业务里面需要能够渲染上传后的3D模型数据,本意是需要根据不同的数据格式,支持不同的渲染的,因此想到了使用 three.js
来实现这个功能,本来想直接在网上借鉴一下大佬的内容,因为我们的技术选型是 React
,所以一开始还尝试使用 github
上的一些别人配置好的 react-three
这样的,但是好像没有我需要的那种,只好自己手撸。
实现方式
其实整体的还是根据 three官方文档 上面的参数进行设置,不多废话直接上代码:
引入组件
import React, { Component, Fragment } from 'react' import * as THREE from 'three' // import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' // import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader' import FileService from '@/services/FileService' import './styles.less'
内容渲染
class Online3DView extends Component { constructor(props) { super(props) this.state = { isModel: false, currentName: '暂无名字', clientX: 0, clientY: 0, } this.threeRef = React.createRef() } componentDidMount() { const { height, width, fileId } = this.props let that = this // 加载要渲染的文件的数据流(Blob) FileService.downloadForPreview(fileId) .then((res) => { const url = window.URL.createObjectURL(res) // todo 初始化场景 const scene = new THREE.Scene() // todo 加载相机 const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 80) camera.position.set(1, 25, 25) camera.lookAt(new THREE.Vector3(0, 0, 0)) //todo 加载光线 const ambLight = new THREE.AmbientLight(0x404040, 1) const pointLight = new THREE.PointLight(0x404040, 0.8) const directionalLight = new THREE.DirectionalLight(0xffffff, 1) pointLight.position.set(100, 10, 0) pointLight.receiveShadow = true scene.add(ambLight) scene.add(pointLight) scene.add(directionalLight) //todo renderer const renderer = new THREE.WebGLRenderer({ antialias: true, }) renderer.setSize(width, height - 10) renderer.setClearColor(0xb9d3ff, 1) // 这里使用哪一种loader就要构建相应的loader,我这里使用了glb文件类型,所以加载了这个 let glbLoader = new GLTFLoader() glbLoader.load(url, function (glTF) { glTF.scene.traverse(function (child) { if (glTF.isMesh) { glTF.frustumCulled = false //模型阴影 glTF.castShadow = true //模型自发光 glTF.material.emissive = glTF.material.color glTF.material.emissiveMap = glTF.material.map } }) scene.add(glTF.scene) // todo 场景控制器初始化 const controls = new OrbitControls(camera, renderer.domElement) controls.enabled = true // 鼠标控制是否可用 // 是否自动旋转 controls.autoRotate = true controls.autoRotateSpeed = 0.05 //是否可旋转,旋转速度(鼠标左键) controls.enableRotate = true controls.rotateSpeed = 0.3 //controls.target = new THREE.Vector();//摄像机聚焦到某一个点 //最大最小相机移动距离(景深相机) controls.minDistance = 10 controls.maxDistance = 100 //最大仰视角和俯视角 controls.minPolarAngle = Math.PI / 4 // 45度视角 controls.maxPolarAngle = Math.PI / 1 // 75度视角 //惯性滑动,滑动大小默认0.25 controls.enableDamping = true controls.dampingFactor = 0.25 //是否可平移,默认移动速度为7px controls.enablePan = true controls.panSpeed = 0.5 //controls.screenSpacePanning = true; //滚轮缩放控制 controls.enableZoom = true controls.zoomSpeed = 1.5 //水平方向视角限制 //controls.minAzimuthAngle = -Math.PI/4; //controls.maxAzimuthAngle = Math.PI/4; //todo 绑定到类上 that.scene = scene that.camera = camera that.renderer = renderer that.controls = controls //鼠标移入和移出事件高亮显示选中的模型 that.currentObjectColor = null //移入模型的颜色 that.currentObject = null //鼠标移入的模型 // 初始化场景 // 加载到dom元素上 that.threeRef.current.appendChild(that.renderer.domElement) that.start() that.resizeFunc1() that.resizeFunc2() }) }) .catch((err) => {}) window.addEventListener('resize', this.resizeFunc1, false) window.addEventListener('resize', this.resizeFunc2, false) } componentWillUnmount() { this.stop() this.renderer && this.threeRef.current.removeChild(this.renderer.domElement) window.removeEventListener('resize', this.resizeFunc1, false) window.removeEventListener('resize', this.resizeFunc2, false) } // 初始化 start = () => { if (!this.frameId) { this.frameId = requestAnimationFrame(this.animate) } } // 卸载组件的时候去除 stop = () => { cancelAnimationFrame(this.frameId) } // 更新状态 animate = () => { this.controls.update() this.renderScene() this.frameId = requestAnimationFrame(this.animate) } renderScene = () => { this.renderer.render(this.scene, this.camera) } closeModel = (e) => { e.stopPropagation() if (this.controls && !this.controls.autoRotate) { this.controls.autoRotate = true } this.setState({ isModel: false, }) } resizeFunc1 = () => { this.controls.update() } resizeFunc2 = (e) => { const dom = document.getElementById('test_three') const { offsetWidth, offsetHeight } = dom this.camera.aspect = offsetWidth / offsetHeight this.camera.updateProjectionMatrix() this.renderer.setSize(offsetWidth, offsetHeight) } render() { return ( <Fragment> <div className={this.props.className || 'three-component'} id="test_three" ref={this.threeRef} /> </Fragment> ) } }
使用他
<Online3DView height={600} width={1150} fileId={record.mediaFileId} />
转载自:https://segmentfault.com/a/1190000041955224