bmakan
bmakan

Reputation: 441

Vertex Colors are changing to white

I'm working with THREE.js points and sometimes I need them to have different per point color. Sometimes, I'm also modifying their alpha value so I had to write my own shader programs.

In JavaScript I have the following code:

let materials;
if (pointCloudData.colors !== undefined) {
    geometry.colors = pointCloudData.colors.map(hexColor => new THREE.Color(hexColor));
    // If the point cloud has color for each point...
    materials = new THREE.ShaderMaterial({
        vertexColors: THREE.VertexColors,
        vertexShader: document.getElementById('vertexshader').textContent,
        fragmentShader: document.getElementById('fragmentshader').textContent,
        transparent: true,
    });
} else {
    // Set color for the whole cloud
    materials = new THREE.ShaderMaterial({
        uniforms: {
            unicolor: { value: pointCloudData.color },
        },
        vertexShader: document.getElementById('vertexshader').textContent,
        fragmentShader: document.getElementById('fragmentshader').textContent,
        transparent: true,
    });
}

const pointCloud = new THREE.Points(geometry, materials);

I am basically setting the mesh color to a uniform value unless I have defined per point colors - then I set vertexColors to the geometry. I also checked the values being stored in the geometry.colors and they are correct RGB values in range [0,1].

My Vertex Shader code:

attribute float size;
attribute float alpha;

varying float vAlpha;
varying vec3 vColor;
void main() {
    vAlpha = alpha;

    #ifdef USE_COLOR
        vColor = color;
    #endif

    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    gl_PointSize = size * ( 300.0 / -mvPosition.z );
    gl_Position = projectionMatrix * mvPosition;
}

And my Fragment shader code:

uniform vec3 unicolor;

varying vec3 vColor;
varying float vAlpha;
void main() {
    #ifdef USE_COLOR
        gl_FragColor = vec4(vColor, vAlpha);
    #else
        gl_FragColor = vec4(unicolor, vAlpha);
    #endif
}

Again, I am checking if the vertexColor is set and then passing it to the Fragment Shader which then sets the per point.

For some reason, the vertices are all white when setting the color per point (screenshot: The white pixels should be green/red). I'm far from advanced user in WebGL and any help would be appreciated. Am I doing something wrong that I'm not aware of?

Upvotes: 1

Views: 1053

Answers (2)

bmakan
bmakan

Reputation: 441

OK, I think I figured it out since it's working now.

I had to set the colors as geometry attributes:

const colors = new Float32Array(n * 3);
for (let i = 0; i < n; i += 1) {
    new THREE.Color(pointCloudData.colors[i]).toArray(colors, i * 3);
}
geometry.addAttribute('colors', new THREE.BufferAttribute(colors, 1));

I also used the suggestion provided by WestLangley and removed the vertexColors: THREE.VertexColors, part from the Material definition and set the define as well:

materials = new THREE.ShaderMaterial({
    defines: {
        USE_COLOR: '',
    },
    vertexShader: document.getElementById('vertexshader').textContent,
    fragmentShader: document.getElementById('fragmentshader').textContent,
    transparent: true,
});

Then in my Vertex shader I added:

attributes vec3 colors;

to get the colors passed from the JavaScript. The rest is the same, I just passed the colors to the fragment shader using the same code as in the posted question above.

Upvotes: 2

WestLangley
WestLangley

Reputation: 104833

You are creating a custom ShaderMaterial and using this pattern in your fragment shader:

#ifdef USE_COLOR
    vColor = color;
#endif

Consequently, you need to specify the material.defines like so:

var defines = {};
defines[ "USE_COLOR" ] = "";

// points material
var shaderMaterial = new THREE.ShaderMaterial( {

    defines:        defines,
    uniforms:       uniforms,
    vertexShader:   document.getElementById( 'vertexshader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
    transparent:    true

} );

You do not need to set vertexColors: THREE.VertexColors. That is just a flag used by built-in materials to alert the renderer to set the defines for you.

three.js r.85

Upvotes: 2

Related Questions