Paul Brink
Paul Brink

Reputation: 464

Threejs points - set gradient color to points

I am using threejs to create and render a point cloud. What I would like to do is give each point a specific color based on its Z coordinate. This color should be mapped from some color like green having the smallest Z coordinate, throughout blue, yellow and red having the largest Z values. What is the simplest way to give each point a specific color that follows this gradient approach?

My script is quite long, so I have simplified it to only these few lines of code:

function drawCloud(){
    for(i = 0; i < totalPoints * 3; i = i + 3){
        //Setting the new Z value of each point
        //Each third value is the Z value, therefore using i+2
        points.geometry.attributes.position.array[i+2] = theNewZValueForThisPoint;

        //Now setting the color of each point
        // i, i+1 AND i+2 is the R,G, B value of each point
        //here each point is set to a green color
        points.geometry.attributes.color.array[i] = 0;
        points.geometry.attributes.color.array[i+1] = 1;
        points.geometry.attributes.color.array[i+2] = 0;
    }    
}

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


I have already created a kind of segmentation approach where each point gets a fixed color within a range. Something like this:

function getColor() {
    if(ZValue > 0 && ZValue < 100){
        color = green;
    }
    if(ZValue > 100 && ZValue < 200){
        color = blue;
    }
}

This is not what I want, as there would be a region where the color is drastically changed. I would like it to have a more gradient approach that changes slowly as the Z value increases.

Keep in mind that this code has been simplified to a great extent to keep it simple and only show the basic idea. Any recommendations on other improvements would also be appreciated.

Upvotes: 1

Views: 644

Answers (1)

Patrick
Patrick

Reputation: 468

Given that you have the minimum z-value and maximum z-value of your point cloud, you can use this function to get the color of each point. You can customize the colors and breakpoints of your gradient in the gradient-array accordingly:

function getColor(z, zMin, zMax) {
  
  // normalize v in the range of vMin and vMax
  function normalize(v, vMin, vMax) {
    return ((v - vMin) / (vMax - vMin));
  }

  // clamp a value between min and max inclusive
  function clamp(value, min, max) {
    if (value >= max) return max;
    if (value <= min) return min;
    return value;
  }

  // calculates the linear interpolation of two numbers
  function lerp(a, b, alpha) {
    return a + (b - a) * clamp(alpha, 0, 1);
  }
  
  const zNorm = normalize(z, zMin, zMax);

  // gradient definition. Each element defines a breakpoint within the normalized z-range (0 - 1) and color
  // important: has to be sorted ascendingly by bp
  const gradient = [
    {bp: 0,   r: 0, g: 1, b: 0},
    {bp: 1/3, r: 0, g: 0, b: 1},
    {bp: 2/3, r: 1, g: 1, b: 0},
    {bp: 1,   r: 1, g: 0, b: 0}
  ];

  let red, green, blue;

  // find the color segment (between breakpoints), interpolate the color between breakpoints
  for(let i = 0, g = gradient.length; i < g; i++) {
    if(zNorm < gradient[i].bp || gradient[i].bp === 1) {
      red = lerp(gradient[i-1].r, gradient[i].r, normalize(zNorm, gradient[i-1].bp, gradient[i].bp));
      green = lerp(gradient[i-1].g, gradient[i].g, normalize(zNorm, gradient[i-1].bp, gradient[i].bp));
      blue = lerp(gradient[i-1].b, gradient[i].b, normalize(zNorm, gradient[i-1].bp, gradient[i].bp));
      break;
    }
  }
  
  return {r: red, g: green, b: blue};
}

Upvotes: 4

Related Questions