前言
随着数字技术的日新月异,3D可视化技术已在众多行业中占据举足轻重的地位,尤其在游戏开发、建筑设计、虚拟现实(VR)以及电子商务等领域,展现出其无尽的潜力与广阔的发展前景。
在众多3D可视化技术解决方案中,Three.js以其丰富的应用程序接口(API)和强大的功能,成为了中小项目3D可视化的首选。然而,尽管Three.js功能强大,但其API种类繁多,学习曲线较为陡峭,使用起来颇为繁琐。为了解决这一问题,React-Three-Fiber应运而生。它通过巧妙地封装Three.js的API,极大地简化了3D可视化开发的流程,使得开发者能够更加高效地进行创作
接下来将从以下方面阐述React-Three-Fiber的优点:
- 更优美的代码
- 更强大的事件交互
- 更显著的性能提升
优点一:更优美的代码
threejs原生代码:
const scene = new THREE.Scence();
const camera = new THREE.PerspectiveCamera(75, width / height);
const htmlEle = document.querySelector('container')
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.querySelector('container').appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandarMaterial();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene,, camera);
}
animate();
React-Three-Fiber代码:
<Canvas>
<mesh>
<boxGeometry />
<meshStandardMaterial />
<mesh>
</Canvas>
将原生Three.js与React-Three-Fiber在创建三维场景时的代码量进行对比,不难发现React-Three-Fiber在代码简洁性和可维护性上展现出了显著优势。React-Three-Fiber凭借其清晰、条理分明的代码结构,不仅极大地提升了代码的可读性,还使得多人协作开发变得更加顺畅和高效。
优点二:更强大的事件交互
<mesh
onClick={(e) => console.log('click')}
onContextMenu={(e) => console.log('context menu')}
onDoubleClick={(e) => console.log('double click')}
onWheel={(e) => console.log('wheel spins')}
onPointerUp={(e) => console.log('up')}
onPointerDown={(e) => console.log('down')}
onPointerOver={(e) => console.log('over')}
onPointerOut={(e) => console.log('out')}
onPointerEnter={(e) => console.log('enter')}
onPointerLeave={(e) => console.log('leave')}
onPointerMove={(e) => console.log('move')}
onPointerMissed={() => console.log('missed')}
onUpdate={(self) => console.log('props have been updated')}
/>
从上面的代码可以看出React-Three-Fiber提供了很多便捷的回调钩子,接下来以点击事件为例对比一下具体的内容
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const onClick = (event) => {
// 归一化设备坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
intersects[0].object.material.color.setHex(0xff0000);
}
}
window.addEventListener('click', onClick, false)
通过这两段代码可以看出,React-Three-Fiber 封装了许多底层逻辑,使得我们能够更方便地操作 Three.js。然而,它的局限性在于只能获取到对应的模型,而无法直接获取模型下的子模型数组(即射线穿透过的模型)。
对于大多数简单项目而言,React-Three-Fiber所提供的API已经足够满足需求。但当项目需求更为复杂时,开发者可以通过进一步封装逻辑来创建射线实例等方法,从而实现类似的功能。总的来说,React-Three-Fiber为开发者提供了极为便捷的API,使得他们无需深入关注底层实现的繁琐细节,而是可以将更多的精力投入到业务逻辑的创新与优化中。
优点三:更显著的性能提升
性能提升将会从渲染帧跟资源释放两个方面进行分析
渲染帧
function animate() {
requestAnimationFrame(animate);
renderer.render(scene,, camera);
}
3D模型在屏幕上展示最终形态还是2D展示,所以是在不断的对场景生成新的快照,进而实现了旋转、缩放的效果。requestAnimationFrame是浏览器下次重绘前执行的钩子,可以确保动画与屏幕刷新频率同步,从而提高视觉效果并减少资源消耗。
虽然我们不会一直操作 3D 场景,但快照却在持续更新,这无疑会造成性能消耗。React-Three-Fiber 对此进行了逻辑优化,使用起来也非常方便,只需在 canvas 标签上添加一个属性即可:
<Canvas frameloop="demand">
设置了该属性以后,当场景中的元素停止运动或者变化以后,将不会继续保持渲染状态。
资源释放
在3D模型的应用场景中,经常会将一些相似的基础模型统一添加到组(group)内。当需要销毁这些模型时,我们需要遍历组内的子模型,并对材质、纹理和几何体执行 dispose 函数,大概的流程如下:
- 移除模型
- 清理纹理资源
- 释放几何体资源
- 释放材质资源
function destroyModel(scene, modelName) {
// 获取场景中的模型
const model = scene.getObjectByName(modelName);
if (model) {
// 从场景中移除模型
scene.remove(model);
// 遍历模型的所有子对象
model.traverse(child => {
if (child instanceof THREE.Mesh) {
// 清理纹理资源
if (child.material.map) {
child.material.map.dispose();
child.material.map = null;
}
if (child.material.lightMap) {
child.material.lightMap.dispose();
child.material.lightMap = null;
}
if (child.material.emissiveMap) {
child.material.emissiveMap.dispose();
child.material.emissiveMap = null;
}
// 释放几何体资源
if (child.geometry) {
child.geometry.dispose();
}
// 释放材质资源
if (child.material) {
child.material.dispose();
}
}
});
}
}
// 使用示例
destroyModel(scene, 'modelName');
从上面的代码可以看出,销毁模型的流程确实比较繁琐,但这一操作是不可或缺的。当多个页面都存在模型时,如果不及时销毁模型,会很大程度地影响页面流畅度,从而影响用户体验。
React-Three-Fiber 在这方面进行了优化,只需要在组(group)标签上维护一个变量。当我们需要销毁模型时,只需将其置为 null 即可。
<group dispose={null}>
<mesh geometry={globalGeometry} material={globalMaterial} />
</group>
除了以上提及到的两点,React-Three-Fiber还做了很多的性能相关的优化操作,大家可以继续深入学习
总结
React-Three-Fiber无疑给我留下了深刻的印象,但值得一提的是,它并非3D可视化的唯一或最优解决方案。在选择技术栈时,我们必须充分考虑项目的具体需求。例如,在需要对单个模型进行复杂操作的场景中,React-Three-Fiber可能并非最佳选择。
在使用React-Three-Fiber之前,建议先掌握一定的Three.js基础,这正如在深入学习Vue或React之前,需要先了解前端基础技术(HTML、CSS、JavaScript)一样。一旦熟悉了Three.js,学习React-Three-Fiber将会变得更加顺畅和自然。
在我看来,没有绝对最好的技术,只有最适合特定需求的技术。每种技术都拥有其独特的优势和局限性,关键在于我们如何根据项目的实际需求来做出明智的选择。因此,在决定采用何种技术之前,深入了解项目的需求是至关重要的。只有这样,我们才能确保所选的技术栈能够最大限度地满足项目的需求,从而实现最佳的开发效果。
本文旨在给各位推荐React-Three-Fiber这个插件,而不是教程文章,大家如果感兴趣可以去官网继续学习:r3f.docs.pmnd.rs/getting-sta…