【Three.js】渲染模型卡顿的优化办法
创始人
2025-05-28 18:55:47
0

事先说明

优化方法是根据chatGPT的回答下,我这里记录一下,有的方法进行了尝试,有的还没有。


1、模型面数过多导致渲染卡顿

可以通过减少面数来优化,也可以使用LOD技术(Level of Detail)在不同距离下使用不同的模型细节来优化。

使用LOD技术可以在不同距离下使用不同的模型细节来优化three.js渲染性能,下面是具体步骤:

  1. 创建多个模型,每个模型的面数和细节不同,这些模型应该是同一个对象的不同版本。

  2. 将这些模型按照从低到高的细节顺序添加到同一个LOD(Level of Detail)对象中,如下所示:

const lod = new THREE.LOD();
const lowDetailModel = ... // 低细节模型
const midDetailModel = ... // 中细节模型
const highDetailModel = ... // 高细节模型
lod.addLevel(lowDetailModel, 0); // 添加低细节模型,距离为0
lod.addLevel(midDetailModel, 100); // 添加中细节模型,距离为100
lod.addLevel(highDetailModel, 200); // 添加高细节模型,距离为200
  1. LOD对象添加到场景中。
scene.add(lod);
  1. 在渲染循环中,根据相机与LOD对象的距离,自动选择当前需要显示的模型细节等级。可以使用THREE.LOD对象的update方法来实现。
function render() {requestAnimationFrame(render);lod.update(camera);renderer.render(scene, camera);
}

2、材质贴图过大导致渲染卡顿

可以通过减小贴图尺寸压缩贴图格式,使用纹理集(Texture Atlas)等方式来优化。

使用纹理集(Texture Atlas)可以将多张小纹理图合并成一张大纹理图,从而减少渲染时的纹理切换次数,优化three.js渲染性能,下面是具体步骤:

  1. 创建一张大纹理图,并将多张小纹理图拼接在一起,这些小纹理图应该是同一对象的不同部分,如下所示:
const texture = new THREE.TextureLoader().load('atlas.png');
const material = new THREE.MeshBasicMaterial({ map: texture });
  1. 将每个物体的UV坐标映射到对应的小纹理图区域,需要根据小纹理图在大纹理图中的位置和大小计算出UV坐标,如下所示:
const geometry = new THREE.BoxGeometry(1, 1, 1);
const uvAttribute = geometry.attributes.uv;
for (let i = 0; i < uvAttribute.count; i++) {const u = uvAttribute.getX(i);const v = uvAttribute.getY(i);// 根据小纹理图在大纹理图中的位置和大小计算出UV坐标uvAttribute.setXY(i, u * smallTextureWidth / bigTextureWidth + smallTextureX / bigTextureWidth, v * smallTextureHeight / bigTextureHeight + smallTextureY / bigTextureHeight);
}
  1. 在渲染循环中,更新大纹理图的偏移和缩放值。
function render() {requestAnimationFrame(render);const time = Date.now() * 0.001;texture.offset.x = time * 0.1; // x方向偏移量texture.offset.y = time * 0.2; // y方向偏移量texture.repeat.set(2, 2); // 横向和纵向缩放值renderer.render(scene, camera);
}

3、着色器复杂度过高导致渲染卡顿

可以通过简化着色器,使用预编译的着色器,使用Instancing等方式来优化。

使用Instancing(实例化)可以将多个相同的物体复用同一个几何体和材质,并在渲染时进行一次性绘制,从而减少渲染调用次数,优化three.js渲染性能,下面是具体步骤:

  1. 创建一个几何体和材质,将它们分别作为多个物体的原型。
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
  1. 创建一个InstancedBufferGeometry对象,并将原型几何体的属性复制到它的属性中。
const instances = 10000; // 实例数量
const instancedGeometry = new THREE.InstancedBufferGeometry();
instancedGeometry.copy(geometry); // 复制几何体属性
const translations = new Float32Array(instances * 3); // 实例位置数组
for (let i = 0; i < instances; i++) {translations[i * 3] = Math.random() * 100 - 50;translations[i * 3 + 1] = Math.random() * 100 - 50;translations[i * 3 + 2] = Math.random() * 100 - 50;
}
instancedGeometry.setAttribute('translation', new THREE.InstancedBufferAttribute(translations, 3));
  1. 创建一个InstancedMesh对象,并将原型材质和实例化几何体作为它的参数。
const instancedMesh = new THREE.InstancedMesh(instancedGeometry, material, instances);
scene.add(instancedMesh);
  1. 在渲染循环中,更新实例化几何体的属性,即实例的位置、旋转和缩放等信息。
function render() {requestAnimationFrame(render);const time = Date.now() * 0.001;for (let i = 0; i < instances; i++) {const translation = instancedMesh.geometry.attributes.translation;translation.setXYZ(i, Math.sin(time + i * 0.5) * 5, Math.cos(time + i * 0.3) * 5, i * 0.1);}instancedMesh.geometry.attributes.translation.needsUpdate = true; // 更新实例位置属性renderer.render(scene, camera);
}

4、不合理的渲染方式导致渲染卡顿

可以通过使用合适的渲染方式,如WebGL2渲染,使用Web Worker等方式来优化。

Ⅰ、使用WebGL2可以在现代浏览器中利用新的图形处理能力,优化three.js渲染性能,下面是具体步骤:
① 在渲染器中启用WebGL2。

const renderer = new THREE.WebGLRenderer({ canvas: canvas, context: canvas.getContext('webgl2') });

② 使用WebGL2支持的新特性,如transform feedbackinstanced arrays等。
例如,以下代码演示了如何使用transform feedback来记录顶点位置的变化:

const transformFeedback = new THREE.WebGL2TransformFeedback();
const bufferGeometry = new THREE.BufferGeometry();
const positions = new Float32Array([0, 0, 0]);
bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const shader = new THREE.ShaderMaterial({vertexShader: `out vec3 transformedPosition;void main() {transformedPosition = position;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`,fragmentShader: `void main() {gl_FragColor = vec4(1.0);}`,transformFeedback: {// 将顶点位置记录到transformedPosition变量中varyings: ['transformedPosition'],// 开启transform feedbackenabled: true,// 设置bufferGeometry的位置属性为transform feedback的输出属性bufferGeometry: bufferGeometry}
});
const mesh = new THREE.Mesh(bufferGeometry, shader);
scene.add(mesh);
function render() {requestAnimationFrame(render);renderer.setRenderTarget(null);// 开始transform feedbacktransformFeedback.begin();renderer.render(scene, camera);// 结束transform feedback,并将变化后的顶点位置存储到bufferGeometry中transformFeedback.end();// 更新顶点位置positions.set(bufferGeometry.getAttribute('position').array);bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));renderer.render(scene, camera);
}

---------------------------------------------------------------分隔线-----------------------------------------------------------------


Ⅱ、使用Web Worker可以将计算密集型的任务分离到另一个线程中,从而避免主线程被阻塞,优化three.js渲染性能,下面是具体步骤:

① 创建一个Web Worker,用于处理计算密集型的任务。

const worker = new Worker('worker.js');

② 在Web Worker中定义处理函数。

// worker.js
function process(data) {// 计算密集型的任务return result;
}
onmessage = function(event) {const result = process(event.data);postMessage(result);
};

③ 在主线程中将任务发送到Web Worker,并设置回调函数处理返回结果。

function render() {requestAnimationFrame(render);// 发送任务到Web Workerworker.postMessage(data);worker.onmessage = function(event) {const result = event.data;// 处理返回结果};renderer.render(scene, camera);
}

通过以上步骤,就可以使用Web Worker来将计算密集型的任务分离到另一个线程中,从而避免主线程被阻塞,优化three.js渲染性能。需要注意的是,Web Worker中无法直接访问主线程的DOM和three.js对象,需要通过消息传递来实现通信。


5、CPU和GPU资源不平衡导致渲染卡顿

可以通过分析性能监控,优化代码逻辑,使用requestAnimationFrame等方式来平衡CPU和GPU资源占用。

使用requestAnimationFrame可以让浏览器根据自身的渲染节奏调整动画的帧率,从而避免过度渲染,优化three.js渲染性能,下面是具体步骤:

  1. 将渲染函数作为requestAnimationFrame的回调函数。
function render() {// 渲染代码renderer.render(scene, camera);// 请求下一帧动画requestAnimationFrame(render);
}
  1. 在初始化时调用一次requestAnimationFrame,启动动画。
var animationId = requestAnimationFrame(render);
  1. 在动画结束时,记得停止requestAnimationFrame,以避免不必要的资源消耗。
function stop() {cancelAnimationFrame(animationId);
}

需要注意的是,使用requestAnimationFrame时需要避免在渲染循环中进行过多的计算,以免影响渲染性能。

相关内容

热门资讯

幼儿园国庆活动主题方案 幼儿园国庆活动主题方案(精选16篇)  为了保障事情或工作顺利、圆满进行,时常需要预先开展方案准备工...
营销策划方案范本 营销策划方案范本  一、什么是策划方案  策划方案,是策划成果的表现形态,通常以文字或图文为载体,策...
公司年会策划方案制定注意事项 公司年会策划方案制定注意事项  公司年会庆典活动通常出席的嘉宾、参加活动的工作人员都比较多。所以组织...
办公室招新策划书 办公室招新策划书  办公室是学生会重要的一个部门,下面unjs小编整理了办公室招新策划书,欢迎阅读!...
服装营销策划方案 服装营销策划方案  为了确保事情或工作有效开展,通常会被要求事先制定方案,方案是阐明具体行动的时间,...
暑期社会实践策划书 暑期社会实践策划书  时光如箭,转眼一划而过,一段时间的工作已经结束了,我们又将接触新的知识,学习新...
高中主题班会策划 高中主题班会策划  高中主题班会策划方案应该怎么样策划?看看下面为大家整理推荐的高中主题班会策划方案...
立春温柔的文案 立春温柔的文案(精选95句)  无论是身处学校还是步入社会,越来越多人喜欢在闲暇时发布文案,文案用以...
新年年会策划方案 最新年会策划方案推荐度:年会策划方案推荐度:公司年会策划方案推荐度:年会策划方案推荐度:年会晚会策划...
药品活动策划方案 药品活动策划方案  为保障活动顺利开展,常常需要提前准备一份具体、详细、针对性强的活动方案,活动方案...
新闻媒体的策划书 新闻媒体推荐的策划书  一、前言。  现代广告的迅猛发展,已成为社会经济增长的一大优势。广告收入增长...
主题活动策划 主题活动策划15篇主题活动策划1  泼水节是傣族人民最盛大的节日,傣语称“桑康比迈”,意为六月(傣历...
运动会活动策划方案 运动会活动策划方案(精选6篇)  为确保活动顺利开展,时常需要预先开展活动方案准备工作,活动方案是阐...
真相永远只有一个“推理之绊”... 真相永远只有一个“推理之绊”系列活动策划  一、活动背景:  “真相永远只有一个”,“ 除所有不可能...
学校总务主任述职报告   述职报告是各级机关、企事业单位和社会团体的工作人员向本单位的组织部门、上级领导机关或本单位员工陈...
万圣节朋友圈必备简短文案 万圣节朋友圈必备简短文案  在日常学习、工作或生活中,越来越多人会在闲暇时发表文案,文案用以展现自己...
母亲节发朋友圈的文案 关于母亲节发朋友圈的文案(精选390句)  随着社交平台的兴起,越来越多人青睐于在社交平台上发表文案...
萝岗区及黄埔区公交线网规划方... 萝岗区及黄埔区公交线网规划方案起止点经行路段萝岗演艺中心-联和开创大道、香雪路、水西路、开创大道、广...
天津工程职业技术学院就业质量... 天津工程职业技术学院就业质量年度报告  一、学院概况  天津工程职业技术学院是一所以工科为主的全日制...