Unable to show the exact vertex color using bufferGeometry

I am using bufferGeometry to draw a grid of cells with different colors in a React application. When I set the colors using bufferAttributes, the cells are displayed in a dull light color instead of the specified RGB color.
Could someone help with this?
I have posted the code and attached image where the green/pink color appears different to the expected color.

import ReactDOM from 'react-dom';
import React from 'react';
import {
    Canvas
} from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import * as THREE from 'three';

const positionsArray = new Float32Array([
    2.0, 2.0, 3.0, // bottom left
    3.0, 2.0, 3.0, // bottom right
    3.0, 3.0, 3.0, // top right

    3.0, 3.0, 3.0, // top right?
    2.0, 3.0, 3.0, // top left?
    2.0, 2.0, 3.0, // bottom left

    3.0, 3.0, 4.0, // bottom left
    4.0, 3.0, 4.0, // bottom right
    4.0, 4.0, 4.0, // top right

    4.0, 4.0, 4.0, // top right?
    3.0, 4.0, 4.0, // top left?
    3.0, 3.0, 4.0 // bottom left

]);

const colors = new Float32Array([

    /*
        I used this tool to convert the RGB colors
        to their normalised versions:
        https://doc.instantreality.org/tools/color_calculator/
    */

    // Unable to show the exact hexcode GREEN color shown with
    // buffer attribute #D524A2
    0.8, 1, 0.6,
    0.8, 1, 0.6,
    0.8, 1, 0.6,

    0.8, 1, 0.6,
    0.8, 1, 0.6,
    0.8, 1, 0.6,

    // Unable to show the exact hexcode PINK color shown with
    // buffer attribute #D524A2
    0.835, 0.141, 0.635,
    0.835, 0.141, 0.635,
    0.835, 0.141, 0.635,

    0.835, 0.141, 0.635,
    0.835, 0.141, 0.635,
    0.835, 0.141, 0.635
]);

const normalArray = new Float32Array([
    0, 0, 1,
    0, 0, 1,
    0, 0, 1,

    0, 0, 1,
    0, 0, 1,
    0, 0, 1,
]);

const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3);
const Example : React.FunctionComponent = () => (
    <div style={{ height: '100vh', backgroundColor: 'grey' }}>
        <Canvas
            dpr={Math.min(window.devicePixelRatio, 2)}
            camera={{
                fov: 45,
                position: [1, 1, 18],
                near: 0.1,
                far: 2000
            }}
        >
            <pointLight position={[0, 0, 100]} color="green" intensity={0.9} />
            <axesHelper args={[10]} />
            <OrbitControls dampingFactor={0.05} />
            <mesh>
                <bufferGeometry attach="geometry" attributes={{ position: positionsAttribute }}>
                    {/* <bufferGeometry> */}
                    <bufferAttribute
                        attachObject={['attributes', 'position']}
                        array={positionsArray}
                        itemSize={3}
                        count={positionsArray.length / 3}
                    />
                    <bufferAttribute
                        attachObject={['attributes', 'normal']}
                        array={normalArray}
                        itemSize={3}
                        count={normalArray.length / 3}
                    />
                    <bufferAttribute
                        attachObject={['attributes', 'color']}
                        array={colors}
                        itemSize={3}
                        count={colors.length / 3}
                        normalized
                    />
                </bufferGeometry>
                  
                <meshBasicMaterial side={THREE.DoubleSide} vertexColors lightMapIntensity={0} toneMapped={false} />
            </mesh>
        </Canvas>
    </div>
);

ReactDOM.render(<Example />, document.getElementById('react-page-content'));

I think you don’t need to normalize the value since you’re feeding it with floating color value already. Maybe try without normalizing:

<bufferAttribute
    attachObject={['attributes', 'color']}
    array={colors}
    itemSize={3}
    count={colors.length / 3}
    false
/>

Or try feeding your color array with Uint8, like:

new Uint8Array([255, 255, 255…])

Thanks, I have tried your solution. But, it makes no difference to the output. The rendered colors are still not correct.
Here is the sandbox for reference: zen-glade-8gv4db - CodeSandbox

The expected and actual colors clearly don’t match. (attached images for comparison)
Screenshot 2022-02-15 at 22.47.05
Screenshot 2022-02-15 at 22.46.41

Ahh I see. I think then it might be a color space issue. Try:

<Canvas linear={true}>

Vertex colors are expected to be given in Linear-sRGB color space. By comparison, most other colors in R3F are given in sRGB, and R3F handles the sRGB → Linear-sRGB conversion automatically. For vertex colors you could manually convert before writing to the positions array:

const color = new Color();
for (let i = 0; i < count; i++) {
  color.r = array[i * 3];
  color.g = array[i * 3 + 1];
  color.b = array[i * 3 + 2];

  color.convertSRGBToLinear();

  array[i * 3] = color.r;
  array[i * 3 + 1] = color.g;
  array[i * 3 + 2] = color.b;
}

Note that colors in CSS (excluding some very new CSS features) are always sRGB as well, if you are trying to match colors elsewhere on the page.

Amazing!. Thanks for letting know donmccurdy

Appreciate your answer @avseoul. Setting linear prop in Canvas is a simple fix to make it work with current rgb values for color attributes.

@donmccurdy thanks for the explanation in detail! Didn’t know CSS color value is sRGB. @karthik-selvam my bad I think my answer was a little irresponsible and more prone to error later in your project, might cause other issues