对 Three.js 演示进行细分,使用 BatchedMesh 进行高效的网格渲染,并使用 TSL 探索新的后处理管道。
Three.js 已逐步推出基于 WebGPU 构建的新 WebGPURenderer,同时还引入了其节点系统和 Three Shading 语言。它有很多东西需要解开,虽然它尚未完全投入生产,但它显示出巨大的前景。在本文中使用新的 WebGPURenderer、BatchedMesh 和一些有趣的后期处理效果来突破网络上实时 3D 的极限。此演示通过展示以下内容突出了 Three.js 最新功能的潜力:- BatchedMesh 对象,允许快速渲染一定数量的几何图形
在演示中,BatchedMesh 是随机组合的,然后通过交互式指针控制沿着分形噪声进行动画处理,另外还有一个带有附加动画的明暗模式切换。WebGPURenderer 是 Three.js 中 WebGLRenderer 的演进版本。它旨在通过精简的软件包为 WebGL/2 和 WebGPU 后端提供统一的接口。现有渲染器的许多功能都已移植,并根据用户的设备提供适当的回退功能。BatchedMesh 可与 WebGLRenderer 和 WebGPURenderer 配合使用,但我在某些浏览器上遇到了一些回退问题。因此此演示仅在 WebGPU 上运行。它的初始化与 WebGLRenderer 完全相同:在 Three.js 中使用 WebGPU 时,请务必从“three/webgpu”导入,因为它是它自己的分支。BatchedMesh 是 Three.js 工具包中的最新成员,它可自动优化渲染大量具有相同材质但具有不同世界变换和几何形状的对象。它的界面和用法与 InstancedMesh 非常相似,主要区别在于您需要知道网格所需的顶点和索引总数。perObjectFrustumCulled:默认为 true,表示每个单独的对象都被视锥剔除sortObjects:对 BatchedMesh 中的对象进行排序,以减少过度绘制(渲染已经绘制的片段)创建对象后,我们需要定义它包含的内容。在演示中,我总共要显示九个不同的几何体 - 三个底部部分和六个顶部部分。这些几何体需要传递给 BatchedMesh,以便它知道它正在处理什么:addGeometry函数返回一个唯一的 ID,您可以使用它将 BatchedMesh 中的对象实例与其几何体关联起来。接下来,我们需要通过简单地将几何 ID 链接到实例 ID 来指定哪个实例使用哪个几何:在这里,使用之前生成的块定义将每个块与网格中的两个实例进行匹配 - 一个用于顶部几何体,另一个用于底部几何体。如果您要使用颜色属性,您还需要在渲染之前对其进行初始化。在渲染循环中,按照预先建立的索引将变换矩阵和颜色传递给网格,该索引的顺序与块数组的顺序相同。矩阵是通过操纵虚拟对象创建的。随着 WebGPURenderer 的推出,Three.js 还引入了 TSL,即 Three Shading Language,一种类似于着色器的、基于节点的 GLSL / WGSL JavaScript 接口,您可以使用它来编写着色器,包括计算着色器。在此演示中,TSL 主要用于定义渲染和后处理管道。但是,也与新的基于节点的材质进行了大量交互,这些材质相当于标准的 Three.js 材质(例如,MeshStandardNodeMaterial 而不是 MeshStandardMaterial)。在这里,目标是创建移轴效果,以强调主体和色彩的趣味性,因此实现了强大的景深效果,并使用简单的自动对焦近似值每帧更新动态参数。还通过屏幕空间环境光遮蔽、晕影效果和一些抗锯齿功能增加了额外的体积。const scenePass = pass(this.scene, this.camera);
scenePass.setMRT(mrt({
output: output,
normal: transformedNormalView
}));
const scenePassColor = scenePass.getTextureNode('output');
const scenePassNormal = scenePass.getTextureNode('normal');
const scenePassDepth = scenePass.getTextureNode('depth');
const aoPass = ao(scenePassDepth, scenePassNormal, this.camera);
...
const blendPassAO = aoPass.getTextureNode().mul(scenePassColor);
const scenePassViewZ = scenePass.getViewZNode();
const dofPass = dof(blendPassAO, scenePassViewZ, effectController.focus, effectController.aperture.mul(0.00001), effectController.maxblur);
const vignetteFactor = clamp(viewportUV.sub(0.5).length().mul(1.2), 0.0, 1.0).oneMinus().pow(0.5);
this.post.outputNode = fxaa(dofPass.mul(vignetteFactor));
用到的许多函数都是各种类型的节点(例如pass、perceivedNormalView、ao、viewportUV等),库中已经有很多这样的函数。- viewportUV返回标准化的视口坐标,非常方便。
- effectController的属性定义为uniform(),供着色器使用。当这些属性在运行时更新时,着色器也会更新。
- 大多数常见的 GLSL 操作都有一个 TSL 等效项,您可以将其与其他节点链接起来(例如,clamp、mul、pow等)。
在渲染循环中,使用弹性阻尼调整焦点和光圈值来模拟自动对焦效果:this.effectController.focus.value = MathUtils.lerp(this.effectController.focus.value, this.camDist * .85, .05);
this.effectController.aperture.value = MathUtils.lerp(this.effectController.aperture.value, 100 - this.camDist * .5, .025);
想象 BatchedMesh 的 Web 用例有哪些?虽然 WebGPURenderer 尚未像原版那样兼容所有浏览器和设备,但正在努力实现这一目标。https://tympanus.net/codrops/2024/10/30/interactive-3d-with-three-js-batchedmesh-and-webgpurenderer/
https://github.com/ULuIQ12/codrops-batchedmesh
类似项目:
https://github.com/Faboolea/shaders-on-scroll
有相同兴趣爱好的可通过加星球的方式添加作者微信。加入后查看置顶评论可加微信交流。做一只爬的最久的乌龟,保持学习保持好奇,即使慢一点,遇到一点困难,只要最后能到达终点,又有什么关系呢。