Three.js实例化技术:同时渲染多个3D对象
学习如何使用React Three Fiber中的实例化技术高效渲染数千个3D对象,正如性能优化的basement.studio网站所展示的那样。
引言
实例化是一种性能优化技术,允许你同时渲染共享相同几何体和材质的多个对象。如果需要渲染森林场景,你会需要大量的树木、岩石和草地。如果它们共享相同的基础网格和材质,你就可以通过单次绘制调用渲染所有对象。
绘制调用是CPU向GPU发出的绘制指令,比如绘制一个网格。每个独特的几何体或材质通常需要自己的调用。过多的绘制调用会损害性能。实例化通过将多个副本批量处理为一个来减少这种情况。
基础实例化
让我们从一个传统方式渲染一千个方块的例子开始:
const boxCount = 1000function Scene() {return (<>{Array.from({ length: boxCount }).map((_, index) => (<meshkey={index}position={getRandomPosition()}scale={getRandomScale()}><boxGeometry /><meshBasicMaterial color={getRandomColor()} /></mesh>))}</>)
}
查看示例 | 源代码
如果添加性能监视器,会发现"calls"数量与boxCount匹配。
使用drei/instances可以快速实现实例化:
import { Instance, Instances } from "@react-three/drei"const boxCount = 1000function Scene() {return (<Instances limit={boxCount}><boxGeometry /><meshBasicMaterial />{Array.from({ length: boxCount }).map((_, index) => (<Instancekey={index}position={getRandomPosition()}scale={getRandomScale()}color={getRandomColor()}/>))}</Instances>)
}
现在"calls"减少到1,即使我们显示了一千个方块。
多组实例
要渲染森林场景,可能需要不同的实例组:
import { createInstances } from "@react-three/drei"const boxCount = 1000
const sphereCount = 1000const [CubeInstances, Cube] = createInstances()
const [SphereInstances, Sphere] = createInstances()function InstancesProvider({ children }: { children: React.ReactNode }) {return (<CubeInstances limit={boxCount}><boxGeometry /><meshBasicMaterial /><SphereInstances limit={sphereCount}><sphereGeometry /><meshBasicMaterial />{children}</SphereInstances></CubeInstances>)
}
自定义着色器实例
要为自定义着色器添加实例支持:
const baseMaterial = new THREE.RawShaderMaterial({vertexShader: /*glsl*/ `attribute vec3 position;attribute vec3 instanceColor;attribute vec3 normal;attribute vec2 uv;uniform mat4 modelMatrix;uniform mat4 viewMatrix;uniform mat4 projectionMatrix;attribute mat4 instanceMatrix;uniform float uTime;uniform float uAmplitude;vec3 movement(vec3 position) {vec3 pos = position;pos.x += sin(position.y + uTime) * uAmplitude;return pos;}void main() {vec3 blobShift = movement(position);vec4 modelPosition = modelMatrix * instanceMatrix * vec4(blobShift, 1.0);vec4 viewPosition = viewMatrix * modelPosition;vec4 projectionPosition = projectionMatrix * viewPosition;gl_Position = projectionPosition;}`,fragmentShader: /*glsl*/ `void main() {gl_FragColor = vec4(1, 0, 0, 1);}`
})
创建森林场景
使用实例化网格创建森林场景:
const [TreeInstances, Tree] = createInstances()
const treeCount = 1000function Scene() {const { scene, nodes } = useGLTF("/stylized_pine_tree_tree.glb") as unknown as TreeGltfreturn (<group><TreeInstanceslimit={treeCount}scale={0.02}geometry={nodes.tree_low001_StylizedTree_0.geometry}material={nodes.tree_low001_StylizedTree_0.material}>{Array.from({ length: treeCount }).map((_, index) => (<Tree key={index} position={getRandomPosition()} />))}</TreeInstances></group>)
}
整个森林仅用三次绘制调用渲染:天空盒一次,地面平面一次,所有树木一次。
延伸阅读
- 批处理网格:允许同时渲染不同几何体
-
- 骨骼动画:当前不支持实例化
-
- 变形动画:实例支持但批处理网格不支持
- 更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)