0


【Three.js】渲染模型卡顿的优化办法

事先说明

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


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

可以通过减少面数来优化,也可以使用

LOD技术(Level of Detail)

在不同距离下使用不同的模型细节来优化。

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

  1. 创建多个模型,每个模型的面数和细节不同,这些模型应该是同一个对象的不同版本。
  2. 将这些模型按照从低到高的细节顺序添加到同一个LOD(Level of Detail)对象中,如下所示:
const lod =newTHREE.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方法来实现。
functionrender(){requestAnimationFrame(render);
  lod.update(camera);
  renderer.render(scene, camera);}

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

可以通过减小贴图尺寸压缩贴图格式,使用

纹理集(Texture Atlas)

等方式来优化。

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

  1. 创建一张大纹理图,并将多张小纹理图拼接在一起,这些小纹理图应该是同一对象的不同部分,如下所示:
const texture =newTHREE.TextureLoader().load('atlas.png');const material =newTHREE.MeshBasicMaterial({map: texture });
  1. 将每个物体的UV坐标映射到对应的小纹理图区域,需要根据小纹理图在大纹理图中的位置和大小计算出UV坐标,如下所示:
const geometry =newTHREE.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. 在渲染循环中,更新大纹理图的偏移和缩放值。
functionrender(){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 =newTHREE.BoxGeometry(1,1,1);const material =newTHREE.MeshBasicMaterial({color:0xff0000});
  1. 创建一个InstancedBufferGeometry对象,并将原型几何体的属性复制到它的属性中。
const instances =10000;// 实例数量const instancedGeometry =newTHREE.InstancedBufferGeometry();
instancedGeometry.copy(geometry);// 复制几何体属性const translations =newFloat32Array(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',newTHREE.InstancedBufferAttribute(translations,3));
  1. 创建一个InstancedMesh对象,并将原型材质和实例化几何体作为它的参数。
const instancedMesh =newTHREE.InstancedMesh(instancedGeometry, material, instances);
scene.add(instancedMesh);
  1. 在渲染循环中,更新实例化几何体的属性,即实例的位置、旋转和缩放等信息。
functionrender(){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 =newTHREE.WebGLRenderer({canvas: canvas,context: canvas.getContext('webgl2')});

② 使用WebGL2支持的新特性,如

transform feedback

instanced arrays

等。
例如,以下代码演示了如何使用transform feedback来记录顶点位置的变化:

const transformFeedback =newTHREE.WebGL2TransformFeedback();const bufferGeometry =newTHREE.BufferGeometry();const positions =newFloat32Array([0,0,0]);
bufferGeometry.setAttribute('position',newTHREE.BufferAttribute(positions,3));const shader =newTHREE.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 =newTHREE.Mesh(bufferGeometry, shader);
scene.add(mesh);functionrender(){requestAnimationFrame(render);
  renderer.setRenderTarget(null);// 开始transform feedback
  transformFeedback.begin();
  renderer.render(scene, camera);// 结束transform feedback,并将变化后的顶点位置存储到bufferGeometry中
  transformFeedback.end();// 更新顶点位置
  positions.set(bufferGeometry.getAttribute('position').array);
  bufferGeometry.setAttribute('position',newTHREE.BufferAttribute(positions,3));
  renderer.render(scene, camera);}

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


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

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

const worker =newWorker('worker.js');

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

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

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

functionrender(){requestAnimationFrame(render);// 发送任务到Web Worker
  worker.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的回调函数。
functionrender(){// 渲染代码
  renderer.render(scene, camera);// 请求下一帧动画requestAnimationFrame(render);}
  1. 在初始化时调用一次requestAnimationFrame,启动动画。
var animationId =requestAnimationFrame(render);
  1. 在动画结束时,记得停止requestAnimationFrame,以避免不必要的资源消耗。
functionstop(){cancelAnimationFrame(animationId);}

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


本文转载自: https://blog.csdn.net/jiangxinyu50/article/details/129549827
版权归原作者 小楼窗外的细雨 所有, 如有侵权,请联系我们删除。

“【Three.js】渲染模型卡顿的优化办法”的评论:

还没有评论