How to work with Display P3 Wide-gamut color profile in WebGL/Three.js?

Hello there.

I wonder if anyone here knows if it is possible to work with P3 wide-gamut color profiles in WebGL and Three.js.

As of today, Chrome already offers support for wide-gamut color profiles like Display P3.
Also, most recent MacBooks and iOS devices have screens able to show colors in P3 color profile. The difference between P3 from sRGB is quite noticeable.

This is a standard sRGB color profile (currently supported by three.js)
Webkit-logo-sRGB

And this is an image with P3 display color profile (not supported in Three.js):
Webkit-logo-P3

If your screen is able to reproduce colors in the P3 gamut, you’ll be able to see the Webkit logo in the second image. As you can see, this is a pretty noticeable color difference.

To understand how P3 is in comparison with sRGB:
P3_vs_sRGB-1

Does anyone have a clue of how this could be implemented?

Note: I am not talking about supporting p3 in images or textures only, instead I am talking about using p3 to manage the colors in whole WebGL renderer.

Related posts:
https://discourse.threejs.org/t/whats-this-about-gammafactor/4264/12
https://discourse.threejs.org/t/support-for-30-bit-displays/4355

References:
https://webkit.org/blog-files/color-gamut/
https://www.webdesignerdepot.com/2017/05/this-red-goes-to-fg0000/

2 Likes

Recent Safari update added wide gamut color support for canvas, but only for 2D contexts for now. Still, better, than nothing! Chromium recently got “canvas color management” for 2D contexts too, but I did not manage to get P3 colors anyway.

Yeah I don’t think this is possible with WebGL in any browser today. See GitHub - WICG/canvas-color-space: Proposed web platform feature to add color management, wide gamut and high bit-depth support to the <canvas> element. for where that might be headed. If you’re interested in the topic you may also wish to follow Add THREE.ColorManagement by donmccurdy · Pull Request #22346 · mrdoob/three.js · GitHub, although the focus there is (at least initially) on better supporting a linear workflow with sRGB input/output.

2 Likes

While I understand that the the canvas 3d context does not yet support P3, could I not “render” the scene to a 2d context (that does support the P3 color space), “converting” the rgb values as P3 values? It would be like stretching the color into the wide gamut domain.

If none of the inputs to the rendering pipeline are in a wide-gamut color space, then I don’t expect you’ll get much benefit out of a wide-gamut display.

The rendering pipeline looks something like this:

  • load input textures and colors in one or more “source” color spaces
  • convert all inputs to the linear “working” color space
  • render in linear working color space
  • output to some “output” color space supported by the display

In a current rendering setup, your source, working, and output color spaces are not wide gamut. In a wide-gamut rendering setup, your working and output color spaces are probably wide gamut, and at least some of your source color spaces are too.

Two parts of this pipeline are currently limited by device and browser support:

  1. The GL context must be tagged to tell the browser the drawing buffer contains Display P3 output. Currently only recent Chrome and Safari support this.
  2. You must have a display that supports Display P3, such as a recent Apple laptop display.

There’s some (very experimental) support coming in r157. It currently supports only unlit rendering, like MeshBasicMaterial, and lit materials will be shaded incorrectly. You can have a look at https://github.com/mrdoob/three.js/pull/26644 for details, as there isn’t documentation for this work yet.

1 Like

unlit rendering is my safe space and my current set of tests is completely unlit meshes with vertex colors.
thank you for the link. I will try this out!

2 Likes

Here is a quick comparison.

First, with

renderer.outputColorSpace = THREE.SRGBColorSpace

Second with

THREE.ColorManagement.workingColorSpace = THREE.LinearDisplayP3ColorSpace 
renderer.outputColorSpace = THREE.DisplayP3ColorSpace

Interesting how the subtle differences in the red faces get blown out when switching to P3.

If the input is really sRGB then switching the output to Display P3 should not expand it. Nothing in the unlit pipeline is attempting to change your colors, only to pass them through accurately to the display.

1 Like

The scene is a a mesh with vertex colors, using a THREE.MeshBasicMaterial( { vertexColors:true } ) so does this mean my input is sRGB?

1 Like

Vertex colors are assumed to be in the working color space, so the meaning of <1,0,0> changes when you change the working color space from Linear sRGB to Linear Display P3.

Most 3D models contain vertex colors in sRGB or Linear-sRGB color spaces, so three.js loaders may (eventually) need to do a conversion here, but we don’t today.

1 Like

Yes! My vertex colors were passing through THREE.Color.setRGB, etc. Now I’ve filled up a buffer attribute directly with the color values and the results are more like what I expected. Still tinkering, but thanks for all of your help on this. I think I understand all of this better!

(first: rgb colors, second: enabling p3 wil rgb colors, third: colors not passing through THREE.Color)

1 Like