Three.js中如何定时或点击触发替换立方体为球体几何体
Three.js 几何体切换失效解决方案
失效核心原因
- 你自定义的
movingObject对象单独存了一份初始立方体几何体的引用,替换Mesh上的geometry属性后,没有同步更新这个引用,后续动画逻辑仍然关联旧几何体 - 注释里遍历
geometry.vertices修改顶点的写法只适配r125之前的旧版Three.js,当前版本用的是BufferGeometry,没有直接暴露的vertices数组,这套写法本身就跑不起来 - 部分Three.js版本下替换几何体后需要手动标记更新,否则渲染层会沿用旧的缓存资源,看起来就像修改没生效
具体实现步骤
- 提前实例化需要切换的球体几何体,避免每次触发切换都重复创建几何体占用内存
- 封装统一的几何体切换方法,替换Mesh的geometry属性后,同步更新
movingObject内的几何体引用,标记几何体需要更新 - 分别绑定定时器、画布点击事件,触发时调用切换方法即可
修改后可直接运行的代码
export class ThreeJSService { // 新增球体几何体属性、切换状态标记 boxGeometry: THREE.BoxGeometry; sphereGeometry: THREE.SphereGeometry; isSphere: boolean = false; camera: THREE.PerspectiveCamera; cube: THREE.Mesh; movingObject; renderer: THREE.WebGLRenderer; scene: THREE.Scene; texture: THREE.Texture; /** * Setups scene for 3d objects */ constructor() { this.scene = new THREE.Scene(); this.texture = new THREE.TextureLoader().load('assets/apartment_background.png'); this.scene.background = this.texture; this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); this.renderer = new THREE.WebGLRenderer(); // 分别创建立方体、球体几何体 this.boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); this.sphereGeometry = new THREE.SphereGeometry(0.3, 32, 32); const material = new THREE.MeshBasicMaterial( {color: 0x348713} ); // 默认使用立方体几何体初始化 this.cube = new THREE.Mesh( this.boxGeometry, material ); this.movingObject = { object: this.cube, geometry: this.boxGeometry, direction: { x: 0.03, y: 0, }, }; // 绑定点击触发切换 this.renderer.domElement.addEventListener('click', () => { this.switchGeometry(); }) // 绑定定时触发切换,示例为每3秒切换一次,可自行修改间隔 setInterval(() => { this.switchGeometry(); }, 3000) } // 新增几何体切换方法 switchGeometry() { this.isSphere = !this.isSphere; const targetGeometry = this.isSphere ? this.sphereGeometry : this.boxGeometry; // 替换mesh的几何体 this.cube.geometry = targetGeometry; // 同步更新movingObject中存储的几何体引用 this.movingObject.geometry = targetGeometry; // 标记几何体更新,触发渲染缓存刷新 this.cube.geometry.needsUpdate = true; } /** * Sets all the requirements needed for the scene * @param {HTMLElement} parent The element to attach to */ setup(parent: HTMLElement) { const canvas = this.renderer.domElement; this.scene.add( this.cube ); this.camera.position.z = 5; parent.appendChild(canvas); } /** * Provides animation to the rendered object. Makes the cube move and sends it to another direction when getting to the border * @param {number | undefined} headerHeight The height of the header */ animate() { const header = document.getElementById('header')?.clientHeight; this.renderer.setSize( window.innerWidth, window.innerHeight - (header ? header : 0)); this.movingObject.object.rotateX(0.01); this.movingObject.object.rotateY(0.01); requestAnimationFrame( this.animate.bind(this) ); this.renderer.render( this.scene, this.camera ); this.movingObject.object.position.x += this.movingObject.direction.x; // Check if the position of the cube is on the border if (this.cube.position.x > 7.3 || this.cube.position.x < -7.3) { this.movingObject.direction.x = -this.movingObject.direction.x; } } }
注意点
- 示例里球体半径设为0.3,是为了和原边长0.5的立方体视觉尺寸接近,你可以根据实际需求调整
SphereGeometry的半径、分段数参数 - 如果只需要做一次立方体到球体的切换,不需要来回切,可以在切换完成后调用
this.boxGeometry.dispose()回收旧几何体占用的显存,避免内存泄漏 - 上述方案是瞬时切换效果,如果需要立方体平滑变形为球体的动画效果,可以使用Morph Target做顶点插值实现
内容的提问来源于stack exchange,提问作者iyanai m'pfunya
相关产品推荐
相关产品推荐

