使用document.getElementById时Three.js地球模型程序卡顿,求优化方案
Three.js地球模型嵌入容器后卡顿的优化方案
问题背景
刚接触Three.js,制作的地球模型程序直接在窗口运行时速度很快,但通过document.getElementById获取容器嵌入后运行极慢,怀疑是贴图使用方式有误,相关代码如下:
JS代码
import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/Addons.js'; import getStarField from './getStarfield.js'; import {getFresnelMat} from './getFresnelMat.js'; export function initializeThreeJS(){ var container = document.getElementById('world'); // a scene const scene = new THREE.Scene(); scene.background = new THREE.Color( 'gainsboro' ); var camera = new THREE.PerspectiveCamera( 30, container.clientWidth/ container.clientHeight ); camera.position.set( 2, 5, 10 ); camera.position.z = 25; var renderer = new THREE.WebGLRenderer( {antialias: true} ); renderer.setSize( container.clientWidth, container.clientHeight); renderer.setAnimationLoop( animate ); container.appendChild( renderer.domElement ); const earthGroup = new THREE.Group(); scene.add(earthGroup); /* * Starfield tb implemented */ /* * temporary lighting */ var controls = new OrbitControls( camera, renderer.domElement ); controls.enableDamping = true; var ambientLight = new THREE.AmbientLight( 'white', 0.5 ); scene.add( ambientLight ); var light = new THREE.DirectionalLight( 'white', 0.5 ); light.position.set( 1, 1, 1 ); scene.add( light ); /* * Orbit Controls tb implemented */ const loader = new THREE.TextureLoader(); /* * Earth Geometry */ const geometry = new THREE.IcosahedronGeometry(1, 12); const material = new THREE.MeshPhongMaterial({ map: loader.load("./textures/00_earthmap1k.jpg"), specularMap: loader.load("./textures/02_earthspec1k.jpg"), bumpMap: loader.load("./textures/01_earthbump1k.jpg"), bumpScale: 0.04, }); const earthMesh = new THREE.Mesh(geometry, material); earthGroup.add(earthMesh); /* * Function Loop */ function animate() { requestAnimationFrame(animate); // Rotate the cube // Render the scene renderer.render(scene, camera); } }
HTML代码
<body> <div id="container"> <div id="world" ></div> </div> </body>
优化提速方案
1. 修复重复渲染的核心问题
你同时使用了renderer.setAnimationLoop(animate)和requestAnimationFrame(animate),这会让渲染函数每帧被调用两次,直接翻倍消耗GPU资源。删掉其中一个即可,推荐保留renderer.setAnimationLoop(Three.js官方推荐的现代写法):
function animate() { // 删掉 requestAnimationFrame(animate); renderer.render(scene, camera); }
2. 优化贴图加载与配置
- 统一管理资源加载:当前贴图异步加载时直接创建材质,可能导致渲染异常和额外开销。用
LoadingManager确保所有贴图加载完成后再初始化地球网格:
const manager = new THREE.LoadingManager(); manager.onLoad = function() { // 所有资源加载完成后再创建网格 const earthMesh = new THREE.Mesh(geometry, material); earthGroup.add(earthMesh); }; const loader = new THREE.TextureLoader(manager); const earthMap = loader.load("./textures/00_earthmap1k.jpg"); const specularMap = loader.load("./textures/02_earthspec1k.jpg"); const bumpMap = loader.load("./textures/01_earthbump1k.jpg"); const material = new THREE.MeshPhongMaterial({ map: earthMap, specularMap: specularMap, bumpMap: bumpMap, bumpScale: 0.04, });
- 优化纹理采样:给贴图设置滤波参数,减少不必要的计算:
// 对所有加载的贴图设置 earthMap.minFilter = THREE.LinearMipmapLinearFilter; earthMap.magFilter = THREE.LinearFilter; specularMap.minFilter = THREE.LinearMipmapLinearFilter; specularMap.magFilter = THREE.LinearFilter; bumpMap.minFilter = THREE.LinearMipmapLinearFilter; bumpMap.magFilter = THREE.LinearFilter;
3. 确保容器尺寸正确
- 给
#world添加明确的CSS样式,避免容器宽高为0导致相机比例异常:
#world { width: 100%; height: 800px; /* 或使用100vh占满视口高度 */ }
- 添加窗口 resize 监听,动态调整相机和渲染尺寸,避免渲染分辨率过大:
window.addEventListener('resize', () => { camera.aspect = container.clientWidth / container.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(container.clientWidth, container.clientHeight); });
4. 降低几何体复杂度
IcosahedronGeometry(1, 12)的细分级别12会生成大量顶点,视觉提升有限但性能消耗大。降低到6左右即可平衡效果与性能:
const geometry = new THREE.IcosahedronGeometry(1, 6);
5. 按需关闭抗锯齿
antialias: true会增加GPU负担,如果容器尺寸不大,可暂时关闭测试性能,或根据设备能力自适应开启:
var renderer = new THREE.WebGLRenderer( {antialias: false} );
内容的提问来源于stack exchange,提问作者Jack Holly
相关产品推荐
相关产品推荐

