1.重用Material和Geometry
2.不在render()中实例化或是赋值操作
3.粒子系统代替粒子
4.操作一组对象使用Object3D或group
5.网格合并merge
6.使用Web workers
https://threejs.org/examples/?q=sand#raytracing_sandbox
7.分时加载算法(大数组)
8.延时渲染THREE.DefaultLoadingManager
9.删除模型remove,使用dispose释放几何和材质,RenderTarget,texture
10.BufferGeometry代替Geometry
11.减少浮点计算,写成小数乘法
12.使用clone()方法
13.纹理图片尺寸一定得是2的幂次方,并尽可能的小
14.跳帧设置
15.降低模型的复杂度减少面数
16.使用chrome的插件three.js inspector
17.使用stats.js进行检测帧率
18. JavaScript中的对象创建很昂贵,因此不要在循环中创建对象。相反,创建一个单独的对象,如Vector3,并vector.set()
在循环中使用或类似的方法
19.Web浏览器使用的JavaScript引擎经常更改,并在幕后对代码进行了大量优化。不要相信你的直觉会更快,总是测试。不要相信几年前的文章告诉你要避免某些方法,如array.map或array.forEach。自己测试一下。
模型,网格和其他可见的东西
- 避免使用常见的基于文本的3D数据格式(如Wavefront OBJ或COLLADA)进行资产交付。相反,请使用针对Web优化的格式,例如glTF
- 使用带有glTF的Draco网格压缩
- 如果您需要使大组对象可见且不可见(或在场景中添加/删除它们),请考虑使用图层以获得最佳性能
- 位于相同位置的物体会导致闪烁。尝试抵消一些微小的东西<math><semantics><annotation encoding="application/x-tex">0.001</annotation></semantics></math>0 。0 0 1使事物看起来像是在同一个位置,但让你的GPU保持开心
- 使场景以原点为中心,以防止在大坐标处出现浮点错误
- 切勿移动Scene对象。它创建于<math><semantics><annotation encoding="application/x-tex">(0,0,0)</annotation></semantics></math>(0 ,0 ,0 ),这是其中所有对象的默认参考框架。
使您的平截头体尽可能小,以获得更好的性能。在开发中使用大平截头体是很好的,但是一旦你对应用程序进行微调以进行部署,就可以使你的平截头体尽可能小,以获得一些FPS
不要把东西放在远剪裁平面上(特别是如果你的远剪裁平面真的很大),这可能会导致闪烁 - 不要使用
TriangleFanDrawMode
,它很慢。 - 当您有数百或数千个类似的几何时,请使用几何实例化
- 在GPU而不是CPU上进行动画处理,尤其是在为顶点或粒子设置动画时(有关此方法的一种方法,请参阅THREE.Bas)
性能
- 设置
object.matrixAutoUpdate = false
为静态或很少移动的对象,并object.updateMatrix()
在更新其位置/四元数/比例时手动调用 - 透明对象很慢,在场景中使用尽可能少的透明对象
-
alphatest
如果可能,使用而不是标准透明度,它更快 - 在测试应用程序的性能时,您需要做的第一件事就是检查它是受CPU限制还是GPU绑定。使用基本材料替换所有材料
scene.overrideMaterial
(请参阅初学者提示和页面开头)。如果性能提高,那么您的应用就是GPU限制。 - 在快速机器上进行性能测试时,您可能会获得60fps的最大帧速率。使用
open -a "Google Chrome" --args --disable-gpu-vsync
fo run 无限帧率运行chrome - 现代移动设备具有高的像素比率 <math><semantics><annotation encoding="application/x-tex">五</annotation></semantics></math>5 - 考虑在这些设备上将最大像素比率限制为2或3,但会牺牲一些非常轻微的场景模糊
- 烘焙光照和阴影贴图以减少场景中的灯光数量
- 如果您有资源和时间,请使用压缩纹理。不幸的是,为网络设置这些内容是一件很痛苦的事情,因为所有设备都不支持任何格式
- 密切关注场景中的drawcalls数量。一个好的经验法则是减少绘制调用=更好的性能
- 远处的物体不需要与靠近相机的物体具有相同的细节水平。有许多技巧用于通过降低远处物体的质量来提高性能。例如,LOD(细节级别)对象存储不同距离的不同对象(例如,近,中和远)。您也可能只为远处的物体每隔2帧或3帧更新位置/动画
尽可能减少多重计数。请记住,模型根本不应该过于详细。
将Obj文件转换为Json和/或为场景中的每个对象创建单独的JSON文件。请记住,geometry是您尝试加载的文件中最重的数据块。因此,如果可能,BufferGeometry为每个文件创建单独的文件并通过它加载BufferGeometryLoader
在加载所有js和json文件之前,您可以使用一些压缩/解压缩技术。查看OpenCTM压缩。您也可以尝试gzip您的文件并对其进行放气。
在创建网格的3d建模阶段,合并尽可能多的几何。最简单的方法是识别具有相同/相似材料的所有网格,合并这些几何,并为这些几何分配相同的材质。您拥有的几何数量越少,渲染管道中绘制调用的次数就越少。因此,您的表现将会提高。
您可以跟踪您的drawcalls使用情况renderer.info
保持纹理文件大小较低的分辨率。我一般建议少于1K。
材料性能成本是这样的MeshBasicMaterial< MeshLambertMaterial< MeshPhongMaterial< MeshStandardMaterial< MeshPhysicalMaterial。在将材质指定给几何体时要小心。Threejs doc清楚地说明了每种材料的所有特征。因此,如果您知道对象不需要材料的某些属性,则回退到更便宜的材质。
使用normalMaps伪造的goemetries。这将减少您vertices所需的数量。
如果您的场景是静态使用aoMap并且LightMap在场景中消除了对光源的需求及其计算成本。
如果场景是静态的,请停止requestAnimationFrame并仅在需要时渲染。例。如果您使用的是orbitcontrol.js,请onChange在触发用户活动时使用方法进行渲染。
使用Chrome扩展程序,如Threejs Inspector和WebGL Inspector进行调试。
如果动态删除场景中的任何内容,请确保正确处理数据。
300 MB的文件非常大。确保将其减少到30 mb或更低,理想情况下,低于10 mb以获得最佳体验。
从场景中删除一些东西?
首先,考虑不这样做,特别是如果你稍后再添加它。您可以暂时隐藏对象object.visible = false(也适用于灯光),或者material.opacity = 0。您可以设置light.intensity = 0禁用灯光而不会导致着色器重新编译。
后期处理
- 内置的抗锯齿功能不适用于后期处理(至少在WebGL 1中)。您需要使用FXAA或SMAA手动执行此操作(可能更快,更好)
- 由于您没有使用内置AA,请务必禁用它!
- three.js有大量的后处理着色器,这真是太棒了!但请记住,每次传递都需要渲染整个场景。完成测试后,请考虑是否可以将一些传递合并到一个自定义传递中
几何
避免使用线环,因为它必须由线条模拟
纹理
- 你的所有纹理都必须是2的幂(POT)大小: <math><semantics><annotation encoding="application/x-tex">1,2,4,8,16,...,512,2048 ......</annotation></semantics></math>1 ,2 ,4 ,8 ,1 6 ,...,5 1 2 ,2 0 4 8 ,...
- 不要更改纹理的尺寸。相反,创建新的,它更快
- 尽可能使用最小的纹理尺寸(你可以使用256x256平铺纹理逃脱吗?你可能会感到惊讶!)
- 非二次幂(NPOT)纹理需要线性或最近的滤波,以及钳位到边界或钳位到边缘的包装。不支持Mipmap过滤和重复包装。但严重的是,只是不要使用NPOT纹理
- 具有相同尺寸的所有纹理在内存中的大小相同,因此JPG的文件大小可能比PNG小,但它会占用GPU上相同的内存量
定制材料
只有在他们改变时才更新你的制服,而不是每一帧。
物料
内置的three.js材料具有简单的性能/质量权衡:
-
MeshStandardMaterial
最高质量/最慢 MeshPhongMaterial
MeshLambertMaterial
-
MeshBasicMaterial
质量最低/最快
使用您能承受的最优质材料,并在需要时切换到质量较差的材料。
-
MeshLambertMaterial
不适用于有光泽的材料,但对于像布料这样的哑光材料,它会产生非常相似的结果,MeshPhongMaterial
但速度更快 - 如果您正在使用变形目标,请确保
morphTargets = true
在材料中设置,否则它们将无效! - 同去的变形法线
- 如果您使用SkinnedMesh进行骨骼动画,请确保
material.skinning = true
- 与变形目标,变形法线或蒙皮一起使用的材质无法共享。您需要为每个蒙皮或变形网格创建一个独特的材质(
material.clone()
这里是您的朋友)。
阴影
- 如果场景是静态的,则仅在更改内容时更新阴影贴图,而不是每帧
- 使用a
CameraHelper
可视化阴影相机的视锥体 - 请记住,点光阴影比其他阴影类型更昂贵,因为它们必须渲染六次(每个方向一次),而单个时间
DirectionalLight
和SpotLight
阴影相比 - 虽然我们对的话题
PointLight
阴影,注意,CameraHelper
只能可视化一个出6用于可视化的点光源阴影时,阴影的方向。它仍然有用,但你需要将你的想象力用于其他5个方向
灯,尤其是SpotLight,PointLight,DirectionalLight很慢。在场景中使用尽可能少的灯光
避免在场景中添加和删除灯光,因为这需要WebGLRenderer重新编译所有着色器程序(它会缓存程序以便随后执行此操作,它会比第一个更快)
打开renderer.physicallyCorrectLights使用SI单位的精确照明
渲染
-
preserveDrawingBuffer
除非您需要,否则请勿启用 - 除非您需要,否则禁用alpha缓冲区
- 除非您需要,否则不要启用模板缓冲区
- 除非你需要它,否则禁用深度缓冲区(但你可能确实需要它)
-
powerPreference: "high-performance"
在创建渲染器时使用。这可能使用户系统在多GPU系统中选择高性能GPU。 - 考虑仅在相机位置通过epsilon或动画发生时更改时进行渲染
- 如果您的场景是静态的并且使用
OrbitControls
,您可以监听change
事件以仅在相机移动时渲染:
OrbitControls.addEventListener( 'change', () => renderer.render( scene, camera ) );
你不会从最后两个获得更高的帧速率,但你会得到的是更少的笔记本电脑风扇开启,以及移动设备上的电池消耗更少。
注意:我已经看到网络上的一些地方建议您禁用抗锯齿并应用后处理AA通道。在我的测试中,这不是真的。在现代硬件上,即使在低功耗移动设备上,内置MSAA似乎也非常便宜,而后处理FXAA或SMAA通过导致相当大的帧丢失
Three.js OffscreenCanvas
产生阴影的:
THREE.DirectionLight() 平型光
THREE.PointLight() 点光源
THREE.SpotLight() 聚光灯
不产生阴影的:
THREE.AmbientLight() 环境光
THREE.HemiphereLight() 半球光
THREE.RectAreaLight() 平面光源