Three.js实例化技术:同时渲染多个3D对象
学习如何使用React Three Fiber中的实例化技术高效渲染数千个3D对象,正如性能优化的basement.studio网站所展示的那样。
引言
实例化是一种性能优化技术,允许你同时渲染共享相同几何体和材质的多个对象。如果需要渲染森林场景,你会需要大量的树木、岩石和草地。如果它们共享相同的基础网格和材质,你就可以通过单次绘制调用渲染所有对象。
绘制调用是CPU向GPU发出的绘制指令,比如绘制一个网格。每个独特的几何体或材质通常需要自己的调用。过多的绘制调用会损害性能。实例化通过将多个副本批量处理为一个来减少这种情况。
基础实例化
让我们从一个传统方式渲染一千个方块的例子开始:
const boxCount = 1000function Scene() { return ( <> {Array.from({ length: boxCount }).map((_, index) => ( <mesh key={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) => ( <Instance key={index} position={getRandomPosition()} scale={getRandomScale()} color={getRandomColor()} /> ))} </Instances> )}
现在"calls"减少到1,即使我们显示了一千个方块。
多组实例
要渲染森林场景,可能需要不同的实例组:
import { createInstances } from "@react-three/drei"const boxCount = 1000const 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 TreeGltf return ( <group> <TreeInstances limit={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> )}
整个森林仅用三次绘制调用渲染:天空盒一次,地面平面一次,所有树木一次。
延伸阅读
- 批处理网格:允许同时渲染不同几何体骨骼动画:当前不支持实例化变形动画:实例支持但批处理网格不支持