I am porting a working code from three.js r167 to react-three-fiber.
In three.js I have something like that
const baseGeoemtry = new THREE.PlaneGeometry(1, 1, 1, 1);
const geometry = new THREE.InstancedBufferGeometry().copy(baseGeometry);
geometry.instanceCount = points.length / 3.;
geometry.setAttribute('aPos', new THREE.InstancedBufferAttribute(new Float32Array(points), 4, false));
const materialPoints = new THREE.ShaderMaterial(...);
const meshPoints = new THREE.Mesh(geometry, materialPoints);
dotsInstancedMesh.renderOrder = 1
parent.add(meshPoints);
Here points
is a flatted array of (x, y, z)
coordinate values, that’s why instanceCount
is divided by 2.
Now I am try to port this code to react-three-fiber, but I am getting some issues, mainly because I am totally newbie with three.js and r3f. So, I need to port the code to r3f and achieve best performance. Highly prefer declarative way.
At the moment I find some possible solutions, but I am not sure if I am doing it right, even if the result I get look correct.
const MaterialPoints = shaderMaterial(...);
extend({ MaterialPoints });
- Three.js way
function Points() {
const meshRef = useRef();
const geometry = useMemo(() => {
const baseGeometry = new PlaneGeometry(1, 1, 1, 1);
const geometry = new InstancedBufferGeometry().copy(planeGeo);
geometry.instanceCount = points.length / 3;
geometry.setAttribute('aPos', new InstancedBufferAttribute(new Float32Array(points), 4, false));
return geometry;
}, []);
return (
<mesh ref={meshRef} geometry={geometry} renderOrder={1}>
<materialPoints key={MaterialPoints.key} transparent side={DoubleSide} depthTest={false} />
</mesh>
);
}
- Drei way
function Points() {
const pointsArray = useMemo(() => new Float32Array(points), []);
const instanceCount = points.length / 3;
return (
<Instances limit={instanceCount} renderOrder={1}>
<planeGeometry args={[1, 1, 1, 1]}>
<instancedBufferAttribute
attach="attributes-aPos"
args={[pointsArray, 4, false]}
/>
</planeGeometry>
<materialPoints key={MaterialPoints.key} transparent side={DoubleSide} depthTest={false} />
{Array.from({ length: instanceCount }, (_, i) => (
<Instance key={i} />
))}
</Instances>
);
}
- Declarative way
function Points() {
const ref = useRef();
const pointsArray = useMemo(() => new Float32Array(points), []);
const instanceCount = points.length / 3;
return (
<instancedMesh ref={ref} args={[null, null, instanceCount]} renderOrder={1}>
<planeGeometry args={[1, 1, 1, 1]}>
<instancedBufferAttribute
attach="attributes-aPos"
args={[pointsArray, 4, false]}
/>
</planeGeometry>
<materialPoints key={MaterialPoints.key} transparent side={DoubleSide} depthTest={false} />
</instancedMesh>
);
}
- Hybrid way
function Points() {
const ref = useRef();
const pointsArray = useMemo(() => new Float32Array(points), []);
const geometry = useMemo(() => new PlaneGeometry(1, 1, 1, 1), []);
const instanceCount = points.length / 3;
return (
<mesh renderOrder={1}>
<instancedBufferGeometry ref={ref} instanceCount={instanceCount} index={geometry.index} attributes-position={geometry.attributes.position} attributes-uv={geometry.attributes.uv}>
<instancedBufferAttribute
attach="attributes-aPos"
args={[pointsArray, 4, false]}
/>
</instancedBufferGeometry>
<materialPoints key={MaterialPoints.key} transparent side={DoubleSide} depthTest={false} />
</mesh>
);
}
All these solutions look correct to me (the result is similar to the one on three.js), but I am not sure if I am doing something wrong (first time using shader) and what solution is better regards performance. I prefer the declarative approach of (2) and (3) compare to (1), but (2) maybe is not the best solution (points are like 5k). Not sure about (4).
About (2) I read here
Note: While creating instances declaratively keeps all the power of components with reduced draw calls, it comes at the cost of CPU overhead. For cases like foliage where you want no CPU overhead with thousands of intances you should use THREE.InstancedMesh such as in this example.
that’s why I create (4), but in my case points are a lot (5k) but they are just place above a sphere (no movement).
Examples used:
- For (4), https://codesandbox.io/s/grass-shader-5xho4
- For (3), https://codesandbox.io/s/h873k and https://codesandbox.io/s/8fo01
- For (2), https://codesandbox.io/s/floating-instanced-shoes-h8o2d
- For (1), just copy paste from three.js to r3f and
useMemo
.
So I am looking for
– confirm about the solution provided (if I am porting correctly from three.js).
– the solution that achieve better performance.