Interpolate Vertex Colors in HSL Space

I am using three.js to render results from physics simulations. A common approach to visualize the results is to assign a color from an HSL color map to each vertex and interpolate the colors. Red usually indicates a large number, and blue indicates a low number. We keep the saturation and lightness constant (usually 1 and 0.5 respectively), and just assign varying hues to each vertex, depending on the result value. See Geometry / Colors / Lookup Table for an example.

For the most part this works great, however, since the colors in the shaders are represented in RGB the individual RGB components are interpolated and we can get some unwanted side effects, such as colors with different saturation and lightness values, or no visualization of the varying degrees of hues. For instance, if two vertices on a face are red (hsl(0, 1, 0.5)) and one is blue (hsl(2/3, 1, 0.5)), what we’d prefer is the colors on the face to interpolate on just the hue component, resulting in a rainbow effect from red to blue. But instead, we just get a linear interpolation in RGB space from red to blue.

Here is an example using PlaneGeometry, with two faces, where the geometry on the left uses the standard color interpolation, and the one of the right is using a version of three.js I modified to interpolate in HSL space.

image

I have two questions:

  1. Is there already a way in Three.JS to achieve this effect, without adding more faces to the geometry and without using ShaderMaterial? I’d like to continue using Geometry and MeshLambertMaterial, because they are so easy to work with.

  2. If the answer to #1 is no - is this something that would be valuable in three.js? I’ve already done a simple implementation in my three.js fork. If this is something that could get merged into mrdoob’s repo, I’d be happy to work up some examples and create a pull request.

With the change I made you can even see some differences in the Lookup Table example on some of the larger faces:

image

No, I think you’d have to use ShaderMaterial here. It is possible to patch MeshLambertMaterial without totally starting from scratch, see this example.

If the answer to #1 is no - is this something that would be valuable in three.js? I’ve already done a simple implementation in my three.js fork.

My hunch is that something like material.vertexColorSpace = THREE.ColorSpaceHSL would too specific of a case… there are some other really critical color-related API changes needed (better sRGB / linear workflow support), and those have been challenging already.

One idea would be to check out THREE.NodeMaterial and add a new option to ColorSpaceNode… it’s extensible and not part of the core library, so that patch might be accepted more easily? Something like:

var material = new THREE.StandardNodeMaterial();
material.color = new THREE.ColorSpaceNode(
  new THREE.AttributeNode( 'color' ),
  THREE.ColorSpaceNode.HSL
);

^The downside here is that NodeMaterial currently supports Standard materials but not Lambert materials.

Thanks @donmccurdy. I like the idea of patching MeshLambertMaterial - I will look into that.

I was afraid the use case was too specific. I essentially added a new constant, material.vertexColors = Three.VertexColorsHSL, and in the vertex shader convert the vColor from RGB to HSL, and in the fragment shader convert the color back from HSL to RGB. It was a pretty simple change, but I suspect it could create problems elsewhere that I’m unaware of.

1 Like