Three.js性能优化和实践建议

Three.js 是一个功能强大的 3D 引擎,当场景足够大的时候,就会出现卡顿的现象,首先要保证电脑的性能够用,然后看看下面方法,帮助你提高应用的运行效率。

1. 使用 stats.js 监视性能

在进行任何优化之前,首先要监视应用的性能。stats.js 是一个简单而有效的工具,可以帮助你实时监视帧率(FPS)、每帧渲染所需时间(MS)和内存使用情况(MB)。

安装和使用 stats.js

首先,通过 npm 安装 stats.js

npm install --save stats.js

然后,可以在 Three.js 项目中使用它:

import Stats from 'stats.js';
​
const stats = new Stats();
stats.showPanel(0); // 显示面板 0: fps, 1: ms, 2: mb, 3+: custom
document.body.appendChild(stats.dom);
​
const tick = () => {stats.begin();// 监视的代码放在这里stats.end();requestAnimationFrame(tick);
};
​
requestAnimationFrame(tick);

FPS:在最后一秒内渲染的帧数。数值越高越好。

MS:渲染一帧所需的毫秒数。数值越低越好。

MB:分配的内存大小(以兆字节为单位)。需要在 Chrome 中使用 --enable-precise-memory-info 启动。

CUSTOM:用户自定义面板支持。

2. 优化几何体和材质

复杂的几何体和高分辨率的材质会显著影响渲染性能。以下是一些优化建议:

降低几何体细节

使用 THREE.LOD(Level of Detail)类来根据摄像机距离动态切换几何体细节。

import * as THREE from 'three';
​
// 创建场景和相机
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 10;
​
// 创建渲染器
const renderer = new THREE.WebGLRenderer({antialias: true,powerPreference: 'high-performance'
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
​
// 创建不同细节级别的几何体
const highDetailGeometry = new THREE.BoxGeometry(1, 1, 1, 32, 32, 32);
const mediumDetailGeometry = new THREE.BoxGeometry(1, 1, 1, 16, 16, 16);
const lowDetailGeometry = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8);
​
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
​
// 使用 LOD 动态切换几何体
const lod = new THREE.LOD();
lod.addLevel(new THREE.Mesh(highDetailGeometry, material), 0);
lod.addLevel(new THREE.Mesh(mediumDetailGeometry, material), 5);
lod.addLevel(new THREE.Mesh(lowDetailGeometry, material), 10);
scene.add(lod);
​
// 动画循环
const animate = function () {requestAnimationFrame(animate);
​// 旋转 LODlod.rotation.x += 0.01;lod.rotation.y += 0.01;
​// 更新摄像机位置camera.position.x = Math.sin(Date.now() * 0.001) * 20;camera.position.z = Math.cos(Date.now() * 0.001) * 20;camera.lookAt(scene.position);
​// 渲染场景和相机renderer.render(scene, camera);
};
​
animate();

使用压缩纹理

使用压缩纹理格式(如 DDS、KTX2)来减少内存占用和加载时间。这里以 KTX2 为例。

首先,安装 three/examples/jsm/loaders/KTX2Loader.jsBasisu 解码器,然后,在你的项目中使用 KTX2Loader 加载压缩纹理:

import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
import { MeshStandardMaterial } from 'three';
​
// 创建 KTX2Loader
const ktx2Loader = new KTX2Loader().setTranscoderPath('path/to/basisu/transcoder/') // 设置 Basisu 解码器路径.detectSupport(renderer);
​
// 加载 KTX2 压缩纹理
ktx2Loader.load('path/to/texture.ktx2', (texture) => {const material = new MeshStandardMaterial({ map: texture });
​const geometry = new THREE.BoxGeometry(1, 1, 1);const mesh = new THREE.Mesh(geometry, material);scene.add(mesh);
});

合并几何体

(1)使用Blender将模型合并一下

(2)将多个几何体合并为一个几何体,以减少绘制调用(draw call)的次数。使用将使用BufferGeometryUtils 合并几何体。

import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
​
// 创建多个几何体
const geometries = [];
for (let i = 0; i < 50; i++) {const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
​geometry.translate((Math.random() - 0.5) * 10,(Math.random() - 0.5) * 10,(Math.random() - 0.5) * 10);
​geometries.push(geometry);
}
​
// 合并几何体
const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(mergedGeometry, material);
scene.add(mesh);
​
// 动画循环
const animate = function () {requestAnimationFrame(animate);
​// 旋转合并后的几何体mesh.rotation.x += 0.01;mesh.rotation.y += 0.01;
​// 渲染场景和相机renderer.render(scene, camera);
};
​
animate();

3. 优化灯光和阴影

灯光和阴影计算开销较大,特别是多光源和动态阴影。以下是一些优化建议:

  • 减少光源数量:尽量减少场景中的光源数量,选择性能开销较小的光源如 AmbientLight 和 DirectionalLight。
  • 优化阴影贴图:降低阴影贴图的分辨率,并限制阴影相机的视野范围,以减少计算开销。
  • // 优化阴影贴图
    directionalLight.shadow.mapSize.width = 1024; // 默认值是 512
    directionalLight.shadow.mapSize.height = 1024; // 默认值是 512
    ​
    // 限制阴影相机的视野范围
    directionalLight.shadow.camera.top = 3;
    directionalLight.shadow.camera.right = 6;
    directionalLight.shadow.camera.left = -6;
    directionalLight.shadow.camera.bottom = -3;
    directionalLight.shadow.camera.near = 0.1;
    directionalLight.shadow.camera.far = 10;
    ​
    // 可选:使用相机助手查看阴影相机的范围
    const cameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
    scene.add(cameraHelper);
    
  • 静态光照贴图:对于静态场景,可以预先计算光照和阴影,生成光照贴图。这里我们使用 Lightmap,一个 Three.js 的扩展,可以帮助实现静态光照贴图。

首先,安装 three-lightmap

npm install three-lightmap

然后,在你的项目中使用 three-lightmap 来生成静态光照贴图:

import { Lightmap } from 'three-lightmap';
​
// 创建静态光照贴图
const lightmap = new Lightmap(scene, renderer, {mapSize: 1024,samples: 4,bake: true,exposure: 0.7,softEdges: 0.01,aoOnly: false,aoStrength: 0.6
});
​
// 在几何体上启用静态光照贴图
cube.material.lightMap = lightmap.generate(cube.geometry);
plane.material.lightMap = lightmap.generate(plane.geometry);
​
// 运行一次性光照贴图烘焙过程
lightmap.bake();

4. 纹理贴图

纹理贴图非常消耗 GPU 内存,以下是一些优化建议:

  • 调整尺寸:调整纹理贴图的分辨率可以通过图像编辑工具(如 Photoshop、GIMP)或编程工具(如 Sharp for Node.js)来实现。在加载纹理时,可以使用 Three.js 内置的 THREE.TextureLoader 来加载已经调整好尺寸的纹理。

  • 使用正确格式:确保使用合适的文件格式(如 .jpg 或 .png)。可以使用在线工具如 TinyPNG 来压缩纹理文件,减小文件大小,同时保持较高的视觉质量。

  • 保持分辨率为 2 的幂次方:确保纹理尺寸为 2 的幂次方(如 256x256, 512x512,1024x1024) 如果纹理的尺寸不是 2 的幂次方,Three.js 会自动调整它们,但这会影响性能。

5. 使用对象池

在动画或游戏应用中,经常需要频繁创建和销毁对象。使用对象池可以有效减少内存分配和垃圾回收频繁的开销。

class ObjectPool {constructor(createFunc, size) {this.createFunc = createFunc;this.pool = [];for (let i = 0; i < size; i++) {this.pool.push(this.createFunc());}}
​get() {return this.pool.length ? this.pool.pop() : this.createFunc();}
​release(obj) {this.pool.push(obj);}
}

6. 渲染器优化

以下是一些针对渲染器的优化建议:

限制像素比:一些设备有非常高的像素比,但渲染的像素越多,消耗的性能越大。将渲染器的像素比限制为 2:

renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

配置偏好:指定 powerPreference 属性来提示用户代理适当的 GPU 配置:

const renderer = new THREE.WebGLRenderer({ powerPreference: 'high-performance' });

抗锯齿:只有在有明显锯齿且不会显著影响性能时才启用抗锯齿。

// 创建渲染器时启用抗锯齿
const renderer = new THREE.WebGLRenderer({canvas: document.querySelector('#canvas'),antialias: true, // 启用抗锯齿powerPreference: 'high-performance' // 提示浏览器选择高性能的 GPU
});

7. 相机优化

通过缩小相机的视野范围(FOV)以及调整相机的 near 和 far 属性,可以显著减少渲染的对象数量,从而提高渲染性能。下面是具体的实现代码和逻辑说明。

缩小相机的视野范围

通过减少相机的视野角度(FOV),可以让屏幕中显示的对象更少,从而减少需要渲染的三角形数量。

调整相机的近端面和远端面

调整相机的 near 和 far 属性,可以确保只渲染特定范围内的对象,避免渲染不必要的远距离对象。

// 调整相机的视野角度和近端面、远端面
const fov = 50; // 缩小视野角度(默认值通常为75)
const aspect = window.innerWidth / window.innerHeight;
const near = 1; // 将 near 属性从 0.1 增大到 1
const far = 50; // 将 far 属性从 100 缩小到 50
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 10;

8. 清除不必要的对象

当场景中不再需要某个对象时,及时清除它:

// 创建示例对象
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
​
// 在某个时刻移除对象
function removeObject(object) {// 移除对象scene.remove(object);// 释放几何体资源if (object.geometry) {object.geometry.dispose();}// 释放材质资源if (object.material) {if (Array.isArray(object.material)) {// 如果材质是数组,遍历并释放每个材质object.material.forEach((material) => {material.dispose();});} else {// 单一材质,直接释放object.material.dispose();}}// 释放纹理资源if (object.material.map) {object.material.map.dispose();}
}
// 在某个时刻调用函数移除对象
removeObject(cube);

9. 后期处理和着色器优化

限制后期处理通道

每个后期处理过程都会增加渲染负担,尽量减少不必要的后期处理步骤。

着色器优化

  • 指定精度:强制材质中着色器的精度:

const shaderMaterial = new THREE.ShaderMaterial({ precision: 'lowp' });
  • 保持代码简单:尽量保持着色器代码简单,避免复杂的逻辑和多层嵌套。

  • 使用贴图纹理:尽量使用纹理来代替复杂的计算,例如噪声生成。

  • 使用 defines:对于不会改变的值,使用 defines 而不是 uniform

const shaderMaterial = new THREE.ShaderMaterial({defines: { uDisplacementStrength: 1.5 },
});

性能优化任重而道远,有更好的方法可以分享出来呀。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/59272.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3字符串转整数

vue3字符串转整数 思路方法代码实现示例 思路方法 在 Vue 3&#xff08;或任何 JavaScript 环境&#xff09;中&#xff0c;将字符串转换为整数可以使用 JavaScript 的内置函数 parseInt()。这个函数可以将一个字符串解析为指定基数的整数。如果不指定基数&#xff0c;默认情况…

ima.copilot-腾讯智能工作台

一、产品描述 ima.copilot是腾讯推出的基于腾讯混元大模型技术的智能工作台&#xff0c;通过先进的人工智能技术&#xff0c;为用户提供了一个全新的搜读写体验&#xff0c;让知识管理变得更加智能和高效。它不仅是一个工具&#xff0c;更是一个智能的伙伴&#xff0c;能够帮助…

Jdbc学习笔记(二)核心API--ResultSet

ResultSet之滚动结果集 ResultSet默认只能往下移 ResultSet表示结果集&#xff0c;它是一个二维的表格&#xff01;ResultSet内部维护一个行光标&#xff08;游标&#xff09;&#xff0c;ResultSet提供了一系列的方法来移动游标&#xff1a; void beforeFirst()&#xff1a;…

CSS Modules在框架中的使用

CSS Modules 是一种与框架无关的技术&#xff0c;然而不同的前端框架&#xff08;如 React、Vue、Angular&#xff09;对它的使用方式会有所不同。下面分别讲解如何在这几个框架中使用 CSS Modules。 1. React 中使用 CSS Modules React 是 CSS Modules 最常用的框架之一&…

RPA 机器人流程自动化

RPA&#xff08;机器人流程自动化&#xff09;机器人的部署通常包括以下几个主要步骤&#xff1a; 1. 准备工作 环境准备&#xff1a;确定机器人的运行环境&#xff0c;通常需要服务器或虚拟机&#xff0c;安装 RPA 软件&#xff08;如 UiPath、Automation Anywhere、Blue …

深入解析TOML、XML、YAML和JSON:优劣对比与场景应用

摘要&#xff1a;本文将介绍四种常见的配置文件和数据交换格式&#xff1a;TOML、XML、YAML和JSON&#xff0c;通过具体的使用例子分析它们的优缺点&#xff0c;并探讨在不同场景下的应用选择。 正文&#xff1a; 一、TOML 优点&#xff1a; 易于阅读和编写&#xff1a;TOML的…

【服务器】本地安装X11 服务器-Windows

【服务器】本地安装X11 服务器-Windows X11 服务器概述X Window System 简介 本地安装X11 服务器另&#xff1a;采用 MobaXterm (自带 X server) 连接远程服务器简单说明流程&#xff1a; 参考 X11 服务器概述 X11 服务器 是 X Window System&#xff08;简称 X11 或 X&#x…

Spring Boot 启动时自动配置 RabbitMQ 交换机、队列和绑定关系

在使用 Spring Boot 开发消息队列应用时&#xff0c;我们经常需要在应用启动时自动创建 RabbitMQ 的交换机、队列和绑定关系。本文将介绍如何通过 Spring Boot 的启动后执行方法来实现这一功能&#xff0c;并提供相应的演示代码和依赖配置。 一、添加依赖 为了在 Spring Boot…

【机器学习】机器学习中用到的高等数学知识-3.微积分 (Calculus)

3. 微积分 (Calculus) 导数和梯度&#xff1a;用于优化算法&#xff08;如梯度下降&#xff09;中计算损失函数的最小值。偏导数&#xff1a;在多变量函数中优化目标函数。链式法则&#xff1a;在反向传播算法中用于计算神经网络的梯度。 导数和梯度&#xff1a;用于优化算法…

Java 网络通信之 Socket 编程全解析

在当今数字化时代&#xff0c;网络通信已经成为各种应用程序不可或缺的一部分。Java 作为一种广泛应用的编程语言&#xff0c;提供了强大的网络编程能力&#xff0c;其中 Socket 编程是实现网络通信的重要手段。本文将详细介绍如何使用 Java 进行网络通信&#xff0c;重点聚焦于…

黎巴嫩和以色列的比较

现在两国战争进行的如火如荼&#xff0c;西瓜视频相关军事评论层出不穷。 ------------------------------------------ 黎巴嫩概况&#xff1a;1943年11月独立&#xff0c;国土面积10452平方公里&#xff0c;人口约607万&#xff0c;绝大多数为阿拉伯人&#xff0c;官方语言…

“Java面试必看:从基础到进阶的全方位准备指南“(2)

9. **String和StringBuffer, StringBuilder的区别有哪些&#xff1f;所有类名包含Buffer的类的内部实现原理是什么&#xff1f;有什么优势&#xff1f;** - **String**&#xff1a;String是不可变类&#xff0c;每次对字符串进行修改&#xff08;如拼接、替换等&#xff09;都会…

《EasyQuotation 与MongoDB在股市信息的奇妙融合》

《EasyQuotation 与MongoDB在股市信息的奇妙融合》 一、EasyQuotation 的强大功能二、数据存入 MongoDB&#xff08;一&#xff09;配置与连接&#xff08;二&#xff09;存储方法 三、K 线图监视股市信息&#xff08;一&#xff09;自定义性能趋势图表&#xff08;二&#xff…

Kafka新节点加入集群操作指南

一、环境准备 1. Java环境安装 # 安装JDK apt-get update apt-get install openjdk-8-jdk -y2. 下载并解压 wget https://archive.apache.org/dist/kafka/2.8.1/kafka_2.13-2.8.1.tgz tar xf kafka_2.13-2.8.1.tgz mv kafka_2.13-2.8.1 kafka二、配置环境变量 1. 创建kafka…

git配置用户信息

在 Git 中配置用户信息&#xff0c;主要是设置你的用户名和电子邮件地址&#xff0c;这些信息会被 Git 用来记录提交的作者信息。以下是配置用户信息的步骤&#xff1a; 打开命令行工具。 设置你的用户名&#xff1a; git config --global user.name "你的名字"例如…

vue3项目初始化完整流程,vue3+TypeScript+vue-router+pinia+element-plus+axios+unocss+mock

2.1项目初始化 今天来带大家从0开始搭建一个vue3版本的后台管理系统。一个项目要有统一的规范&#xff0c;需要使用eslintstylelintprettier来对我们的代码质量做检测和修复&#xff0c;需要使用husky来做commit拦截&#xff0c;需要使用commitlint来统一提交规范&#xff0c;…

[Import REC] Import REC下载及使用Import REC重建引入表Import table详细过程(附有下载文件)

前言 下载 使用夸克网盘打开链接&#xff0c;给出的是绿化版免安装 Import REC 链接&#xff1a;https://pan.quark.cn/s/552e4c1ea7d6 提取码&#xff1a;qEMM 下载之后解压得到 里面有使用更新说明 使用修复import table 演示 现在有一个程序&#xff0c;放入PEiD进行查壳…

MySQL技巧之跨服务器数据查询:高级篇-先调用A数据库的MySql存储过程再复制到B数据库的表中

MySQL技巧之跨服务器数据查询&#xff1a;高级篇-先调用A数据库的MySql存储过程再复制到B数据库的表中 基础篇已经描述&#xff1a;借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MySQL数据库的…

AI制作表情包,每月躺赚1W+,完整流程制作多重变现教学

项目介绍 AI制作表情包项目是一个利用ai&#xff0c;快速生成表情包的副业项目。 在社交平台如微信、QQ等&#xff0c;表情包已成为日常沟通不可或缺的一部分。通过AI技术&#xff0c;我们可以轻松制作出大量表情包&#xff0c;并通过多种渠道实现变现&#xff0c;非常适合追…

入侵排查之Linux

目录 1.黑客入侵后的利用思路 2.入侵排查思路 2.1.账号安全 2.1.1.用户信息文件/etc/passwd 2.1.2.影子文件/etc/shadow 2.1.3.入侵排查 2.1.3.1.排查当前系统登录信息 2.1.4.2.查询可以远程登录的账号信息 2.2.历史命令 2.2.1.基本使用 2.2.1.1.root历史命令 2.2.…