InstancedMesh won't show materials but using mesh does

Hello, I am struggling to render multiple instance of a mesh. :slightly_frowning_face:
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.

it’s not how THREE.InstancedMesh works, you use it the same exact way as in vanilla three.js docs

Constructor
InstancedMesh( geometry : BufferGeometry, material : Material, count : Integer )
geometry - an instance of BufferGeometry.
material - an instance of Material. Default is a new MeshBasicMaterial.
count - the number of instances.

you’re doing the following, which is not correct since the buffers are created in the constructor

const instance = new THREE.InstancedMesh()
instance.count = smokeCount
instance.geometry = new THREE.PlaneGeometry()
instance.material = new THREE.MeshLambertMaterial()

THREE.InstancedMesh has a bad api, optional args by language convention must always go last. i opened an issue for this but authors disagreed. so …

const instance = new THREE.InstancedMesh(null, null, smokeCount)
instance.geometry = new THREE.PlaneGeometry()
instance.material = new THREE.MeshLambertMaterial()

which in fiber is equal to

<instancedMesh args={[null, null, smokeCount]}>
  <planeGeometry />
  <meshLambertMaterial />

more mistakes

- scale={[1,1,1]} // scale is 1 by default, short form would be ={1}
- matrix={new Matrix4()} // you don't need this, instances have their own matrices

- color={new Color(color)} 
+ color={color} 

- <planeGeometry args={[300,300]}/>
+ <planeGeometry /> // scale the instances instead

https://codesandbox.io/p/sandbox/instanced-vertex-colors-8fo01

Hello thank you for your response, I see some progress .
I don’t see the warning in the console anymore but I can still not see the smoke in the canvas.
I can assure that they do exist inside the canvas by the use of the onBeforeRender function but I cannot see them on the screen.

let temp3dObj = new Object3D();
const Smoke = ({position, scale, count, color} : { position?: [number,number,number], scale? : number, count : 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 < count; 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
            instancedMeshRef.current.onBeforeRender = () => {console.log("temp3dObj.rotation.z")};
        }
    }, []);

    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}
                args={[undefined,undefined,count]}
            >
                <planeGeometry/>
                <meshLambertMaterial
                    map={smokeTexture}
                    emissive={0x222222}
                    color={color}
                    opacity={0.15}
                    transparent={true}
                />

            </instancedMesh>
        </group>
    );
};

export default Smoke;

Should I change in the level of the group component, what more can I do?