InstancedMesh setColorAt unexpected behavior

Hi there,

I am trying to use instanced mesh and update colors when certain events happen and I tried to follow the example. But all I can see is either original color or a blend of that color with black.

Here is my minimal code example for debugging:
minimal_example_debug.html (6.7 KB)

            /* Setup */

            import * as THREE from 'three';
            import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

            const scene = new THREE.Scene();
            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.z = 2;

            const renderer = new THREE.WebGLRenderer();
            renderer.setSize( window.innerWidth, window.innerHeight );
            renderer.setClearColor( 0xCCCCCC, 1 );
            document.body.appendChild( renderer.domElement );

            const controls = new OrbitControls( camera, renderer.domElement );

            const light = new THREE.AmbientLight( 0x404040, 2 ); // soft white light scene.add( light );
            const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
            scene.add( directionalLight );
            scene.add(light)

            const axesHelper = new THREE.AxesHelper(1)
            scene.add(axesHelper)


            /* Constants */

            const COLOR_PALETTE = [0xff0000, 0x00ff00, 0x0000ff, 0xff00ff, 0xffff00, 0x00ffff]
            const COLOR_DUMMY = new THREE.Color()
            const MATRIX_DUMMY = new THREE.Matrix4()
            const BASE_COLOR = 0x00ff00


            /* Code */

            let latestPointsInstancedMesh = null

            const generatePoints = (color) => {
                const points = [
                    [Math.random(), Math.random(), Math.random()],
                    [Math.random(), Math.random(), Math.random()],
                    [Math.random(), Math.random(), Math.random()],
                    [Math.random(), Math.random(), Math.random()],
                    [Math.random(), Math.random(), Math.random()],
                ]
                const data = {
                    points: points,
                    color: color
                }
                return data
            }

            const addPoints = (data) => {
                try {
                    const {points, color} = data
                    const baseColor = Array.isArray(color) ? BASE_COLOR : color
                    const sphereMat = new THREE.MeshStandardMaterial({color: baseColor})
                    const geometry = new THREE.IcosahedronGeometry(0.01, 1)
                    const instancedSphere = new THREE.InstancedMesh(geometry, sphereMat, points.length)
                    latestPointsInstancedMesh = instancedSphere
                    const scl = 4
                    scene.add(instancedSphere)
                    COLOR_DUMMY.set(baseColor)

                    points.forEach((coords, i) => {
                        const p = [
                            coords[0],
                            coords[1] === undefined ? 0.0 : coords[1],
                            coords[2] === undefined ? 0.0 : coords[2]
                        ]
                        MATRIX_DUMMY.setPosition(p[0], p[1], p[2])
                        MATRIX_DUMMY.elements[0] = scl
                        MATRIX_DUMMY.elements[5] = scl
                        MATRIX_DUMMY.elements[10] = scl
                        instancedSphere.setMatrixAt(i, MATRIX_DUMMY)

                        if (Array.isArray(color))
                            instancedSphere.setColorAt(i, COLOR_DUMMY.set(color[i]))
                        else 
                            instancedSphere.setColorAt(i, COLOR_DUMMY)
                        console.log('setting color for i', i, COLOR_DUMMY)
                    })

                    instancedSphere.instanceMatrix.needsUpdate = true
                    instancedSphere.instanceColor.needsUpdate = true
                } catch (e) {
                    console.log('ERROR', e)
                }
            }

            const randomizeLatestPointsColors = () => {
                if (!latestPointsInstancedMesh) return

                for(let i = 0; i < 5; i++) {
                    const color = COLOR_PALETTE[Math.floor(Math.random() * COLOR_PALETTE.length)]
                    latestPointsInstancedMesh.setColorAt(i, COLOR_DUMMY.set(color))
                }
                latestPointsInstancedMesh.instanceColor.needsUpdate = true
            }

            const consoleLogLatestPointsColors = () => {
                if (!latestPointsInstancedMesh) return

                for(let i = 0; i < 5; i++) {
                    latestPointsInstancedMesh.getColorAt(i, COLOR_DUMMY)
                    console.log('COLOR AT: ', i, COLOR_DUMMY)
                }
            }

            /* Buttons */

            document.getElementById('button_single').onclick = () => {
                addPoints(generatePoints(0x0000ff))
            }
            document.getElementById('button_array_same_color').onclick = () => {
                addPoints(generatePoints([0x0000ff, 0x0000ff, 0x0000ff, 0x0000ff, 0x0000ff]))
            }
            document.getElementById('button_array').onclick = () => {
                addPoints(generatePoints([0x00ff00, 0xff0000, 0x0000ff, 0xffff00, 0x00ffff]))
            }
            document.getElementById('button_colors').onclick = () => {
                randomizeLatestPointsColors()
            }
            document.getElementById('button_colors_check').onclick = () => {
                consoleLogLatestPointsColors()
            }


            /* Loop */

            function animate() {
                requestAnimationFrame(animate);
                renderer.render(scene, camera);
            }
            animate(); 

Note that there are multiple sources of base color available:

  • material.map
  • material.color
  • vertex colors
  • instance colors

All of these sources will be multiplied together, so if any one of them contains a zero RGB component, that component goes to zero. With a base color of 0x00FF00, your R and B channels go to zero. You probably want to keep the material color at 0xFFFFFF and then each instance color will be unmodified.

1 Like