Hello, I am struggling to render multiple instance of a mesh.
I want to create a smoke animation in the background of my page using a canvas that is similar like this example.
I tried replicate this using the regular mesh to display multiple smoke texture inside a map and transform all of them inside a useFrame:
const Smoke = ({position, scale, smokeNum, color} : { position?: [number,number,number], scale? : number, smokeNum : NumericRange<CreateArrayWithLengthX<0>,99>, color? : string}) => {
const smokeTexture = new TextureLoader().load(smoke.src);
smokeTexture.colorSpace = SRGBColorSpace
let smokeParticles = useRef<Array<Mesh | null>>(Array(smokeNum).fill(null));
useFrame((state, delta)=>{
console.log(smokeParticles.current)
smokeParticles.current.forEach((smoke: Mesh | null, index)=>{
if (smoke) {
smoke.rotation.z += (delta*0.2);
}
});
});
return (
<group
position={position}
dispose={null}
scale={scale ?? 1}
>
<spotLight
position={[0, 0, 2]}
angle={1}
penumbra={1}
intensity={300}
castShadow
shadow-mapSize={1024}
/>
{
smokeParticles.current.map((refMesh, index)=>{
const position = [getRandomNumber(-20,20),getRandomNumber(-20,20),getRandomNumber(-60,-50)] as [number,number,number];
const rotationZ = Math.random() * 360;
return (
<mesh
key={`smoke${index}`}
ref={el => smokeParticles.current[index] = el}
scale={[1,1,1]}
position={position}
rotation={[0,0,rotationZ]}
matrix={new Matrix4()}
count={smokeNum}
>
<planeGeometry args={[300,300]}/>
<meshLambertMaterial
map={smokeTexture}
emissive={0x222222}
color={new Color(color)}
opacity={1}
transparent={true}
/>
</mesh>
);
})
}
</group>
);
};
export default Smoke;
This code work fine and display the smoke but based on the Three.js doc, you should use an instancedMesh when creating a lot of objects with the same geometry and material but have different world transformations.
So I tried code it using the instancedMesh by following the react-three-fiber docs and the minimal instanced mesh example:
let temp3dObj = new Object3D();
const Smoke = ({position, scale, smokeCount, color} : { position?: [number,number,number], scale? : number, smokeCount : NumericRange<CreateArrayWithLengthX<0>,99>, color? : string}) => {
const smokeTexture = new TextureLoader().load(smoke.src);
smokeTexture.colorSpace = SRGBColorSpace
const instancedMeshRef = useRef<InstancedMesh>(null);
useEffect(() => {
if (instancedMeshRef.current) {
console.log(instancedMeshRef.current);
// Set positions
for (let i = 0; i < smokeCount; i++) {
temp3dObj.position.set(getRandomNumber(-20,20),getRandomNumber(-20,20),getRandomNumber(-60,-50));
temp3dObj.rotation.z = Math.random() * 360;
temp3dObj.updateMatrix()
instancedMeshRef.current.setMatrixAt(i, temp3dObj.matrix)
}
// Update the instance
instancedMeshRef.current.instanceMatrix.needsUpdate = true
}
}, [])
return (
<group
position={position}
dispose={null}
scale={scale ?? 1}
>
<spotLight
position={[0, 0, 2]}
angle={1}
penumbra={1}
intensity={300}
castShadow
shadow-mapSize={1024}
/>
<instancedMesh
ref={instancedMeshRef}
scale={[1,1,1]}
matrix={new Matrix4()}
count={smokeCount}
>
<planeGeometry args={[300,300]}/>
<meshLambertMaterial
map={smokeTexture}
emissive={0x222222}
color={new Color(color)}
opacity={0.15}
transparent={true}
/>
</instancedMesh>
</group>
);
};
export default Smoke;
But it is not showing anything in the page, I don’t have any error in the console, only a warning:
drawElementsInstanced: Instance fetch requires 30, but attribs only supply 0.
What did I do wrong in my code?
Thank you advance for your responses.