Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ColorManagement: Add ColorManagement.define( { ... } ) #29450

Merged
merged 12 commits into from
Sep 27, 2024
86 changes: 86 additions & 0 deletions examples/jsm/math/ColorSpaces.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { LinearTransfer, Matrix3, P3Primaries, Rec2020Primaries, SRGBTransfer } from 'three';

/******************************************************************************
* Display P3
*
* Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping
* or clipping. Based on W3C specifications for sRGB and Display P3,
* and ICC specifications for the D50 connection space. Values in/out
* are _linear_ sRGB and _linear_ Display P3.
*
* Note that both sRGB and Display P3 use the sRGB transfer functions.
*
* Reference:
* - http://www.russellcottrell.com/photo/matrixCalculator.htm
*/

const LINEAR_DISPLAY_P3_TO_XYZ = /*@__PURE__*/ new Matrix3().set(
0.4865709, 0.2656677, 0.1982173,
0.2289746, 0.6917385, 0.0792869,
0.0000000, 0.0451134, 1.0439444
);

const XYZ_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set(
2.4934969, - 0.9313836, - 0.4027108,
- 0.8294890, 1.7626641, 0.0236247,
0.0358458, - 0.0761724, 0.9568845
);

export const DisplayP3ColorSpace = 'display-p3';
export const LinearDisplayP3ColorSpace = 'display-p3-linear';

export const DisplayP3ColorSpaceImpl = {
transfer: SRGBTransfer,
primaries: P3Primaries,
toReference: LINEAR_DISPLAY_P3_TO_XYZ,
fromReference: XYZ_TO_LINEAR_DISPLAY_P3,
luminanceCoefficients: [ 0.2289, 0.6917, 0.0793 ],
};

export const LinearDisplayP3ColorSpaceImpl = {
transfer: LinearTransfer,
primaries: P3Primaries,
toReference: LINEAR_DISPLAY_P3_TO_XYZ,
fromReference: XYZ_TO_LINEAR_DISPLAY_P3,
luminanceCoefficients: [ 0.2289, 0.6917, 0.0793 ],
};

/******************************************************************************
* Rec. 2020
*/

const LINEAR_REC2020_TO_XYZ = /*@__PURE__*/ new Matrix3().set(
0.6369580, 0.1446169, 0.1688810,
0.2627002, 0.6779981, 0.0593017,
0.0000000, 0.0280727, 1.0609851
);

const XYZ_TO_LINEAR_REC2020 = /*@__PURE__*/ new Matrix3().set(
1.7166512, - 0.3556708, - 0.2533663,
- 0.6666844, 1.6164812, 0.0157685,
0.0176399, - 0.0427706, 0.9421031
);

export const LinearRec2020ColorSpace = 'rec2020-linear';

export const LinearRec2020ColorSpaceImpl = {
transfer: LinearTransfer,
primaries: Rec2020Primaries,
toReference: LINEAR_REC2020_TO_XYZ,
fromReference: XYZ_TO_LINEAR_REC2020,
luminanceCoefficients: [ 0.2627, 0.6780, 0.0593 ],
};

/******************************************************************************
* Rec. 2100 Display (HDR)
*/

export const LinearRec2100DisplayColorSpace = 'rec2100-display-linear';
donmccurdy marked this conversation as resolved.
Show resolved Hide resolved

export const LinearRec2100DisplayColorSpaceImpl = {
transfer: LinearTransfer,
primaries: Rec2020Primaries,
toReference: LINEAR_REC2020_TO_XYZ,
fromReference: XYZ_TO_LINEAR_REC2020,
luminanceCoefficients: [ 0.2627, 0.6780, 0.0593 ],
};
19 changes: 14 additions & 5 deletions examples/webgl_test_wide_gamut.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@

import * as THREE from 'three';

import { DisplayP3ColorSpace, DisplayP3ColorSpaceImpl, LinearDisplayP3ColorSpace, LinearDisplayP3ColorSpaceImpl } from 'three/addons/math/ColorSpaces.js';

import WebGL from 'three/addons/capabilities/WebGL.js';

let container, camera, renderer, loader;
Expand All @@ -98,11 +100,18 @@

const slider = document.querySelector( '.slider' );

const isP3Context = WebGL.isColorSpaceAvailable( THREE.DisplayP3ColorSpace );
const isP3Context = WebGL.isColorSpaceAvailable( DisplayP3ColorSpace );

THREE.ColorManagement.define( {

[ DisplayP3ColorSpace ]: DisplayP3ColorSpaceImpl,
[ LinearDisplayP3ColorSpace ]: LinearDisplayP3ColorSpaceImpl

} );

if ( isP3Context ) {

THREE.ColorManagement.workingColorSpace = THREE.LinearDisplayP3ColorSpace;
THREE.ColorManagement.workingColorSpace = LinearDisplayP3ColorSpace;

}

Expand Down Expand Up @@ -132,7 +141,7 @@

if ( isP3Context && window.matchMedia( '( color-gamut: p3 )' ).matches ) {

renderer.outputColorSpace = THREE.DisplayP3ColorSpace;
renderer.outputColorSpace = DisplayP3ColorSpace;

}

Expand All @@ -149,7 +158,7 @@
textureR = await loader.loadAsync( path.replace( '{colorSpace}', 'p3' ) );

textureL.colorSpace = THREE.SRGBColorSpace;
textureR.colorSpace = THREE.DisplayP3ColorSpace;
textureR.colorSpace = DisplayP3ColorSpace;

sceneL.background = THREE.TextureUtils.contain( textureL, window.innerWidth / window.innerHeight );
sceneR.background = THREE.TextureUtils.contain( textureR, window.innerWidth / window.innerHeight );
Expand Down Expand Up @@ -213,7 +222,7 @@

function onGamutChange( { matches } ) {

renderer.outputColorSpace = isP3Context && matches ? THREE.DisplayP3ColorSpace : THREE.SRGBColorSpace;
renderer.outputColorSpace = isP3Context && matches ? DisplayP3ColorSpace : THREE.SRGBColorSpace;

textureL.needsUpdate = true;
textureR.needsUpdate = true;
Expand Down
3 changes: 1 addition & 2 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,13 @@ export const ObjectSpaceNormalMap = 1;
export const NoColorSpace = '';
export const SRGBColorSpace = 'srgb';
export const LinearSRGBColorSpace = 'srgb-linear';
export const DisplayP3ColorSpace = 'display-p3';
export const LinearDisplayP3ColorSpace = 'display-p3-linear';

export const LinearTransfer = 'linear';
export const SRGBTransfer = 'srgb';

export const Rec709Primaries = 'rec709';
export const P3Primaries = 'p3';
export const Rec2020Primaries = 'rec2020';
donmccurdy marked this conversation as resolved.
Show resolved Hide resolved

export const ZeroStencilOp = 0;
export const KeepStencilOp = 7680;
Expand Down
109 changes: 47 additions & 62 deletions src/math/ColorManagement.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
import { SRGBColorSpace, LinearSRGBColorSpace, DisplayP3ColorSpace, LinearDisplayP3ColorSpace, Rec709Primaries, P3Primaries, SRGBTransfer, LinearTransfer, NoColorSpace, } from '../constants.js';
import { SRGBColorSpace, LinearSRGBColorSpace, Rec709Primaries, SRGBTransfer, LinearTransfer, NoColorSpace, } from '../constants.js';
import { Matrix3 } from './Matrix3.js';

/**
* Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping
* or clipping. Based on W3C specifications for sRGB and Display P3,
* and ICC specifications for the D50 connection space. Values in/out
* are _linear_ sRGB and _linear_ Display P3.
*
* Note that both sRGB and Display P3 use the sRGB transfer functions.
*
* Reference:
* - http://www.russellcottrell.com/photo/matrixCalculator.htm
*/

const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set(
0.8224621, 0.177538, 0.0,
0.0331941, 0.9668058, 0.0,
0.0170827, 0.0723974, 0.9105199,
const LINEAR_REC709_TO_XYZ = /*@__PURE__*/ new Matrix3().set(
0.4123908, 0.3575843, 0.1804808,
0.2126390, 0.7151687, 0.0721923,
0.0193308, 0.1191948, 0.9505322
);

const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().set(
1.2249401, - 0.2249404, 0.0,
- 0.0420569, 1.0420571, 0.0,
- 0.0196376, - 0.0786361, 1.0982735
const XYZ_TO_LINEAR_REC709 = /*@__PURE__*/ new Matrix3().set(
3.2409699, - 1.5373832, - 0.4986108,
- 0.9692436, 1.8759675, 0.0415551,
0.0556301, - 0.2039770, 1.0569715
);

/**
Expand All @@ -33,83 +21,66 @@ const COLOR_SPACES = {
[ LinearSRGBColorSpace ]: {
transfer: LinearTransfer,
primaries: Rec709Primaries,
toReference: LINEAR_REC709_TO_XYZ,
fromReference: XYZ_TO_LINEAR_REC709,
luminanceCoefficients: [ 0.2126, 0.7152, 0.0722 ],
toReference: ( color ) => color,
fromReference: ( color ) => color,
},
[ SRGBColorSpace ]: {
transfer: SRGBTransfer,
primaries: Rec709Primaries,
toReference: LINEAR_REC709_TO_XYZ,
fromReference: XYZ_TO_LINEAR_REC709,
luminanceCoefficients: [ 0.2126, 0.7152, 0.0722 ],
toReference: ( color ) => color.convertSRGBToLinear(),
fromReference: ( color ) => color.convertLinearToSRGB(),
},
[ LinearDisplayP3ColorSpace ]: {
transfer: LinearTransfer,
primaries: P3Primaries,
luminanceCoefficients: [ 0.2289, 0.6917, 0.0793 ],
toReference: ( color ) => color.applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),
fromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ),
},
[ DisplayP3ColorSpace ]: {
transfer: SRGBTransfer,
primaries: P3Primaries,
luminanceCoefficients: [ 0.2289, 0.6917, 0.0793 ],
toReference: ( color ) => color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),
fromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(),
},
};

const SUPPORTED_WORKING_COLOR_SPACES = new Set( [ LinearSRGBColorSpace, LinearDisplayP3ColorSpace ] );

export const ColorManagement = {

enabled: true,

_workingColorSpace: LinearSRGBColorSpace,

get workingColorSpace() {
workingColorSpace: LinearSRGBColorSpace,

return this._workingColorSpace;
convert: function ( color, sourceColorSpace, targetColorSpace ) {

},
if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {

set workingColorSpace( colorSpace ) {
return color;

if ( ! SUPPORTED_WORKING_COLOR_SPACES.has( colorSpace ) ) {
}

throw new Error( `Unsupported working color space, "${ colorSpace }".` );
const sourceTransfer = COLOR_SPACES[ sourceColorSpace ].transfer;
const sourceToReference = COLOR_SPACES[ sourceColorSpace ].toReference;
const targetTransfer = COLOR_SPACES[ targetColorSpace ].transfer;
const targetFromReference = COLOR_SPACES[ targetColorSpace ].fromReference;

}
if ( sourceTransfer === SRGBTransfer ) {

this._workingColorSpace = colorSpace;
SRGBToLinear( color );

},
}

convert: function ( color, sourceColorSpace, targetColorSpace ) {
color.applyMatrix3( sourceToReference );
color.applyMatrix3( targetFromReference );

if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
if ( targetTransfer === SRGBTransfer ) {

return color;
LinearToSRGB( color );

}

const sourceToReference = COLOR_SPACES[ sourceColorSpace ].toReference;
const targetFromReference = COLOR_SPACES[ targetColorSpace ].fromReference;

return targetFromReference( sourceToReference( color ) );
return color;

},

fromWorkingColorSpace: function ( color, targetColorSpace ) {

return this.convert( color, this._workingColorSpace, targetColorSpace );
return this.convert( color, this.workingColorSpace, targetColorSpace );

},

toWorkingColorSpace: function ( color, sourceColorSpace ) {

return this.convert( color, sourceColorSpace, this._workingColorSpace );
return this.convert( color, sourceColorSpace, this.workingColorSpace );

},

Expand All @@ -127,12 +98,26 @@ export const ColorManagement = {

},

getLuminanceCoefficients: function ( target, colorSpace = this._workingColorSpace ) {
getLuminanceCoefficients: function ( target, colorSpace = this.workingColorSpace ) {

return target.fromArray( COLOR_SPACES[ colorSpace ].luminanceCoefficients );

},

getMatrix: function ( sourceColorSpace, targetColorSpace, targetMatrix ) {

return targetMatrix
.copy( COLOR_SPACES[ sourceColorSpace ].toReference )
.multiply( COLOR_SPACES[ targetColorSpace ].fromReference );

},

define: function ( colorSpaces ) {

Object.assign( COLOR_SPACES, colorSpaces );

},

};


Expand Down
11 changes: 6 additions & 5 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ import {
UnsignedInt248Type,
UnsignedShort4444Type,
UnsignedShort5551Type,
WebGLCoordinateSystem,
DisplayP3ColorSpace,
LinearDisplayP3ColorSpace
WebGLCoordinateSystem
} from '../constants.js';
import { Color } from '../math/Color.js';
import { Frustum } from '../math/Frustum.js';
Expand Down Expand Up @@ -2827,9 +2825,12 @@ class WebGLRenderer {

this._outputColorSpace = colorSpace;

// TODO: Generalize this!
const gl = this.getContext();
gl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? 'display-p3' : 'srgb';
gl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? 'display-p3' : 'srgb';
gl.drawingBufferColorspace = colorSpace; // TODO: handle linear
gl.unpackColorSpace = colorSpace; // TODO: handle linear
// gl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? 'display-p3' : 'srgb';
// gl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? 'display-p3' : 'srgb';

}

Expand Down
Loading
Loading