Search code examples
three.jsgltf

gltfLoader vertexColors issue


I am using glTF-transform to create GLB (compressed by gltf-pack), and encountered strange behavior in THREE.GLTFLoader.

I am using vertex colors and passing them this way

My pipeline looks like: Create Glb -> gltfpack -> load to THREE using gltfLoader -> mergeBufferGeometry -> render() with opacity in fragmentShader

const primitive = doc
            .createPrimitive()
            .setAttribute('POSITION', position)
            .setIndices(indices);
          const positionArr = position.getArray();
          if (positionArr) {
            const colors = [];
            const colorAccessor = doc
              .createAccessor()
              .setType('VEC4')
              .setBuffer(buffer);
            const rgba = this.toRGBAColor(Number.parseInt(sPrim.c));
            for (let i = 0; i < position.getCount(); i++) {
              colors.push(rgba[0], rgba[1], rgba[2], rgba[3]);
            }
            colorAccessor.setArray(new Float32Array(colors));
            primitive.setAttribute('COLOR', colorAccessor);
          }
          mesh.addPrimitive(primitive);

In this case, the colors are correct, RGBA from 0 to 1

console.log(primitive.getAttribute('COLOR')?.getArray());
-> Float32Array(16) [
   0.4627451002597809, 0.27450981736183167,
  0.20000000298023224,                   1,
   0.4627451002597809, 0.27450981736183167,
  0.20000000298023224,                   1,
   0.4627451002597809, 0.27450981736183167,
  0.20000000298023224,                   1,
   0.4627451002597809, 0.27450981736183167,
  0.20000000298023224,                   1
]

But, when I load the model into gltfLoader, the color attribute array becomes Uint8Array (with values 0-255 even for alpha)

So, what is the correct way to pass the color of the vertices?


Solution

  • COLOR_0 (not COLOR) is the expected name of the vertex color attribute – the model could have more than one vertex color set. gltfpack is "normalizing" the float32 colors, which means it replaces float32 values in [0, 1] with uint8 values in [0, 255] instead, requiring ~75% less space. three.js can support both.