Browse Source

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

Don McCurdy 1 year ago
parent
commit
076ff07681

+ 1 - 2
examples/jsm/loaders/KTX2Loader.js

@@ -18,14 +18,12 @@ import {
 	CompressedCubeTexture,
 	Data3DTexture,
 	DataTexture,
-	DisplayP3ColorSpace,
 	FileLoader,
 	FloatType,
 	HalfFloatType,
 	NoColorSpace,
 	LinearFilter,
 	LinearMipmapLinearFilter,
-	LinearDisplayP3ColorSpace,
 	LinearSRGBColorSpace,
 	Loader,
 	RedFormat,
@@ -72,6 +70,7 @@ import {
 	KHR_DF_PRIMARIES_DISPLAYP3
 } from '../libs/ktx-parse.module.js';
 import { ZSTDDecoder } from '../libs/zstddec.module.js';
+import { DisplayP3ColorSpace, LinearDisplayP3ColorSpace } from '../math/ColorSpaces.js';
 
 const _taskCache = new WeakMap();
 

+ 75 - 0
examples/jsm/math/ColorSpaces.js

@@ -0,0 +1,75 @@
+import { LinearTransfer, Matrix3, SRGBTransfer } from 'three';
+
+// Reference: http://www.russellcottrell.com/photo/matrixCalculator.htm
+
+const P3_PRIMARIES = [ 0.680, 0.320, 0.265, 0.690, 0.150, 0.060 ];
+const P3_LUMINANCE_COEFFICIENTS = [ 0.2289, 0.6917, 0.0793 ];
+const REC2020_PRIMARIES = [ 0.708, 0.292, 0.170, 0.797, 0.131, 0.046 ];
+const REC2020_LUMINANCE_COEFFICIENTS = [ 0.2627, 0.6780, 0.0593 ];
+const D65 = [ 0.3127, 0.3290 ];
+
+/******************************************************************************
+ * Display P3 definitions
+ */
+
+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 = {
+	primaries: P3_PRIMARIES,
+	whitePoint: D65,
+	transfer: SRGBTransfer,
+	toXYZ: LINEAR_DISPLAY_P3_TO_XYZ,
+	fromXYZ: XYZ_TO_LINEAR_DISPLAY_P3,
+	luminanceCoefficients: P3_LUMINANCE_COEFFICIENTS,
+	outputColorSpaceConfig: { drawingBufferColorSpace: DisplayP3ColorSpace }
+};
+
+export const LinearDisplayP3ColorSpaceImpl = {
+	primaries: P3_PRIMARIES,
+	whitePoint: D65,
+	transfer: LinearTransfer,
+	toXYZ: LINEAR_DISPLAY_P3_TO_XYZ,
+	fromXYZ: XYZ_TO_LINEAR_DISPLAY_P3,
+	luminanceCoefficients: P3_LUMINANCE_COEFFICIENTS,
+	workingColorSpaceConfig: { unpackColorSpace: DisplayP3ColorSpace }
+};
+
+/******************************************************************************
+ * Rec. 2020 definitions
+ */
+
+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 = {
+	primaries: REC2020_PRIMARIES,
+	whitePoint: D65,
+	transfer: LinearTransfer,
+	toXYZ: LINEAR_REC2020_TO_XYZ,
+	fromXYZ: XYZ_TO_LINEAR_REC2020,
+	luminanceCoefficients: REC2020_LUMINANCE_COEFFICIENTS,
+};

+ 14 - 5
examples/webgl_test_wide_gamut.html

@@ -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;
@@ -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;
 
 			}
 
@@ -132,7 +141,7 @@
 
 				if ( isP3Context && window.matchMedia( '( color-gamut: p3 )' ).matches ) {
 
-					renderer.outputColorSpace = THREE.DisplayP3ColorSpace;
+					renderer.outputColorSpace = DisplayP3ColorSpace;
 
 				}
 
@@ -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 );
@@ -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;

+ 0 - 5
src/constants.js

@@ -163,15 +163,10 @@ 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 ZeroStencilOp = 0;
 export const KeepStencilOp = 7680;
 export const ReplaceStencilOp = 7681;

+ 117 - 84
src/math/ColorManagement.js

@@ -1,121 +1,82 @@
-import { SRGBColorSpace, LinearSRGBColorSpace, DisplayP3ColorSpace, LinearDisplayP3ColorSpace, Rec709Primaries, P3Primaries, SRGBTransfer, LinearTransfer, NoColorSpace, } from '../constants.js';
+import { SRGBColorSpace, LinearSRGBColorSpace, 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_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
-);
-
-/**
- * Defines supported color spaces by transfer function and primaries,
- * and provides conversions to/from the Linear-sRGB reference space.
- */
-const COLOR_SPACES = {
-	[ LinearSRGBColorSpace ]: {
-		transfer: LinearTransfer,
-		primaries: Rec709Primaries,
-		luminanceCoefficients: [ 0.2126, 0.7152, 0.0722 ],
-		toReference: ( color ) => color,
-		fromReference: ( color ) => color,
-	},
-	[ SRGBColorSpace ]: {
-		transfer: SRGBTransfer,
-		primaries: Rec709Primaries,
-		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,
+	workingColorSpace: LinearSRGBColorSpace,
+
+	/**
+	 * Implementations of supported color spaces.
+	 *
+	 * Required:
+	 *	- primaries: chromaticity coordinates [ rx ry gx gy bx by ]
+	 *	- whitePoint: reference white [ x y ]
+	 *	- transfer: transfer function (pre-defined)
+	 *	- toXYZ: Matrix3 RGB to XYZ transform
+	 *	- fromXYZ: Matrix3 XYZ to RGB transform
+	 *	- luminanceCoefficients: RGB luminance coefficients
+	 *
+	 * Optional:
+	 *  - outputColorSpaceConfig: { drawingBufferColorSpace: ColorSpace }
+	 *  - workingColorSpaceConfig: { unpackColorSpace: ColorSpace }
+	 *
+	 * Reference:
+	 * - https://www.russellcottrell.com/photo/matrixCalculator.htm
+	 */
+	spaces: {},
 
-	get workingColorSpace() {
+	convert: function ( color, sourceColorSpace, targetColorSpace ) {
 
-		return this._workingColorSpace;
+		if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
 
-	},
+			return color;
 
-	set workingColorSpace( colorSpace ) {
+		}
 
-		if ( ! SUPPORTED_WORKING_COLOR_SPACES.has( colorSpace ) ) {
+		if ( this.spaces[ sourceColorSpace ].transfer === SRGBTransfer ) {
 
-			throw new Error( `Unsupported working color space, "${ colorSpace }".` );
+			color.r = SRGBToLinear( color.r );
+			color.g = SRGBToLinear( color.g );
+			color.b = SRGBToLinear( color.b );
 
 		}
 
-		this._workingColorSpace = colorSpace;
+		if ( this.spaces[ sourceColorSpace ].primaries !== this.spaces[ targetColorSpace ].primaries ) {
 
-	},
+			color.applyMatrix3( this.spaces[ sourceColorSpace ].toXYZ );
+			color.applyMatrix3( this.spaces[ targetColorSpace ].fromXYZ );
 
-	convert: function ( color, sourceColorSpace, targetColorSpace ) {
+		}
 
-		if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
+		if ( this.spaces[ targetColorSpace ].transfer === SRGBTransfer ) {
 
-			return color;
+			color.r = LinearToSRGB( color.r );
+			color.g = LinearToSRGB( color.g );
+			color.b = LinearToSRGB( color.b );
 
 		}
 
-		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 );
 
 	},
 
 	getPrimaries: function ( colorSpace ) {
 
-		return COLOR_SPACES[ colorSpace ].primaries;
+		return this.spaces[ colorSpace ].primaries;
 
 	},
 
@@ -123,16 +84,44 @@ export const ColorManagement = {
 
 		if ( colorSpace === NoColorSpace ) return LinearTransfer;
 
-		return COLOR_SPACES[ colorSpace ].transfer;
+		return this.spaces[ colorSpace ].transfer;
+
+	},
+
+	getLuminanceCoefficients: function ( target, colorSpace = this.workingColorSpace ) {
+
+		return target.fromArray( this.spaces[ colorSpace ].luminanceCoefficients );
+
+	},
+
+	define: function ( colorSpaces ) {
+
+		Object.assign( this.spaces, colorSpaces );
+
+	},
+
+	// Internal APIs
+
+	_getMatrix: function ( targetMatrix, sourceColorSpace, targetColorSpace ) {
+
+		return targetMatrix
+			.copy( this.spaces[ sourceColorSpace ].toXYZ )
+			.multiply( this.spaces[ targetColorSpace ].fromXYZ );
 
 	},
 
-	getLuminanceCoefficients: function ( target, colorSpace = this._workingColorSpace ) {
+	_getDrawingBufferColorSpace: function ( colorSpace ) {
 
-		return target.fromArray( COLOR_SPACES[ colorSpace ].luminanceCoefficients );
+		return this.spaces[ colorSpace ].outputColorSpaceConfig.drawingBufferColorSpace;
 
 	},
 
+	_getUnpackColorSpace: function ( colorSpace = this.workingColorSpace ) {
+
+		return this.spaces[ colorSpace ].workingColorSpaceConfig.unpackColorSpace;
+
+	}
+
 };
 
 
@@ -147,3 +136,47 @@ export function LinearToSRGB( c ) {
 	return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
 
 }
+
+/******************************************************************************
+ * sRGB definitions
+ */
+
+const REC709_PRIMARIES = [ 0.640, 0.330, 0.300, 0.600, 0.150, 0.060 ];
+const REC709_LUMINANCE_COEFFICIENTS = [ 0.2126, 0.7152, 0.0722 ];
+const D65 = [ 0.3127, 0.3290 ];
+
+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 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
+);
+
+ColorManagement.define( {
+
+	[ LinearSRGBColorSpace ]: {
+		primaries: REC709_PRIMARIES,
+		whitePoint: D65,
+		transfer: LinearTransfer,
+		toXYZ: LINEAR_REC709_TO_XYZ,
+		fromXYZ: XYZ_TO_LINEAR_REC709,
+		luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
+		workingColorSpaceConfig: { unpackColorSpace: SRGBColorSpace }
+	},
+
+	[ SRGBColorSpace ]: {
+		primaries: REC709_PRIMARIES,
+		whitePoint: D65,
+		transfer: SRGBTransfer,
+		toXYZ: LINEAR_REC709_TO_XYZ,
+		fromXYZ: XYZ_TO_LINEAR_REC709,
+		luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
+		outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace }
+	},
+
+} );

+ 3 - 5
src/renderers/WebGLRenderer.js

@@ -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';
@@ -2851,8 +2849,8 @@ class WebGLRenderer {
 		this._outputColorSpace = colorSpace;
 
 		const gl = this.getContext();
-		gl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? 'display-p3' : 'srgb';
-		gl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? 'display-p3' : 'srgb';
+		gl.drawingBufferColorspace = ColorManagement._getDrawingBufferColorSpace( colorSpace );
+		gl.unpackColorSpace = ColorManagement._getUnpackColorSpace();
 
 	}
 

+ 0 - 24
src/renderers/shaders/ShaderChunk/colorspace_pars_fragment.glsl.js

@@ -1,29 +1,5 @@
 export default /* glsl */`
 
-// http://www.russellcottrell.com/photo/matrixCalculator.htm
-
-// Linear sRGB => XYZ => Linear Display P3
-const mat3 LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = mat3(
-	vec3( 0.8224621, 0.177538, 0.0 ),
-	vec3( 0.0331941, 0.9668058, 0.0 ),
-	vec3( 0.0170827, 0.0723974, 0.9105199 )
-);
-
-// Linear Display P3 => XYZ => Linear sRGB
-const mat3 LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = mat3(
-	vec3( 1.2249401, - 0.2249404, 0.0 ),
-	vec3( - 0.0420569, 1.0420571, 0.0 ),
-	vec3( - 0.0196376, - 0.0786361, 1.0982735 )
-);
-
-vec4 LinearSRGBToLinearDisplayP3( in vec4 value ) {
-	return vec4( value.rgb * LINEAR_SRGB_TO_LINEAR_DISPLAY_P3, value.a );
-}
-
-vec4 LinearDisplayP3ToLinearSRGB( in vec4 value ) {
-	return vec4( value.rgb * LINEAR_DISPLAY_P3_TO_LINEAR_SRGB, value.a );
-}
-
 vec4 LinearTransferOETF( in vec4 value ) {
 	return value;
 }

+ 23 - 28
src/renderers/webgl/WebGLProgram.js

@@ -1,9 +1,10 @@
 import { WebGLUniforms } from './WebGLUniforms.js';
 import { WebGLShader } from './WebGLShader.js';
 import { ShaderChunk } from '../shaders/ShaderChunk.js';
-import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, AgXToneMapping, ACESFilmicToneMapping, NeutralToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearSRGBColorSpace, SRGBColorSpace, LinearDisplayP3ColorSpace, DisplayP3ColorSpace, P3Primaries, Rec709Primaries } from '../../constants.js';
+import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, AgXToneMapping, ACESFilmicToneMapping, NeutralToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearTransfer, SRGBTransfer } from '../../constants.js';
 import { ColorManagement } from '../../math/ColorManagement.js';
 import { Vector3 } from '../../math/Vector3.js';
+import { Matrix3 } from '../../math/Matrix3.js';
 
 // From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/
 const COMPLETION_STATUS_KHR = 0x91B1;
@@ -29,40 +30,25 @@ function handleSource( string, errorLine ) {
 
 }
 
-function getEncodingComponents( colorSpace ) {
-
-	const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );
-	const encodingPrimaries = ColorManagement.getPrimaries( colorSpace );
-
-	let gamutMapping;
-
-	if ( workingPrimaries === encodingPrimaries ) {
+const _m0 = /*@__PURE__*/ new Matrix3();
 
-		gamutMapping = '';
-
-	} else if ( workingPrimaries === P3Primaries && encodingPrimaries === Rec709Primaries ) {
-
-		gamutMapping = 'LinearDisplayP3ToLinearSRGB';
-
-	} else if ( workingPrimaries === Rec709Primaries && encodingPrimaries === P3Primaries ) {
+function getEncodingComponents( colorSpace ) {
 
-		gamutMapping = 'LinearSRGBToLinearDisplayP3';
+	ColorManagement._getMatrix( _m0, ColorManagement.workingColorSpace, colorSpace );
 
-	}
+	const encodingMatrix = `mat3( ${ _m0.elements.map( ( v ) => v.toFixed( 4 ) ) } )`;
 
-	switch ( colorSpace ) {
+	switch ( ColorManagement.getTransfer( colorSpace ) ) {
 
-		case LinearSRGBColorSpace:
-		case LinearDisplayP3ColorSpace:
-			return [ gamutMapping, 'LinearTransferOETF' ];
+		case LinearTransfer:
+			return [ encodingMatrix, 'LinearTransferOETF' ];
 
-		case SRGBColorSpace:
-		case DisplayP3ColorSpace:
-			return [ gamutMapping, 'sRGBTransferOETF' ];
+		case SRGBTransfer:
+			return [ encodingMatrix, 'sRGBTransferOETF' ];
 
 		default:
-			console.warn( 'THREE.WebGLProgram: Unsupported color space:', colorSpace );
-			return [ gamutMapping, 'LinearTransferOETF' ];
+			console.warn( 'THREE.WebGLProgram: Unsupported color space: ', colorSpace );
+			return [ encodingMatrix, 'LinearTransferOETF' ];
 
 	}
 
@@ -95,7 +81,16 @@ function getShaderErrors( gl, shader, type ) {
 function getTexelEncodingFunction( functionName, colorSpace ) {
 
 	const components = getEncodingComponents( colorSpace );
-	return `vec4 ${functionName}( vec4 value ) { return ${components[ 0 ]}( ${components[ 1 ]}( value ) ); }`;
+
+	return [
+
+		`vec4 ${functionName}( vec4 value ) {`,
+
+		`	return ${components[ 1 ]}( vec4( value.rgb * ${components[ 0 ]}, value.a ) );`,
+
+		'}',
+
+	].join( '\n' );
 
 }
 

+ 89 - 0
test/unit/addons/math/ColorSpaces.tests.js

@@ -0,0 +1,89 @@
+import { Color } from '../../../../src/math/Color.js';
+import { ColorManagement } from '../../../../src/math/ColorManagement.js';
+import { LinearSRGBColorSpace } from '../../../../src/constants.js';
+import {
+	DisplayP3ColorSpace,
+	DisplayP3ColorSpaceImpl,
+	LinearDisplayP3ColorSpace,
+	LinearDisplayP3ColorSpaceImpl,
+	LinearRec2020ColorSpace,
+	LinearRec2020ColorSpaceImpl
+} from '../../../../examples/jsm/math/ColorSpaces.js';
+
+// Reference: https://apps.colorjs.io/convert/
+
+export default QUnit.module( 'Maths', () => {
+
+	QUnit.module( 'ColorSpaces', () => {
+
+		ColorManagement.define( {
+
+			[ DisplayP3ColorSpace ]: DisplayP3ColorSpaceImpl,
+			[ LinearDisplayP3ColorSpace ]: LinearDisplayP3ColorSpaceImpl,
+			[ LinearRec2020ColorSpace ]: LinearRec2020ColorSpaceImpl
+
+		} );
+
+		QUnit.test( 'DisplayP3ColorSpace', ( assert ) => {
+
+			const c = new Color().setRGB( 0.3, 0.5, 0.7 );
+
+			ColorManagement.convert( c, LinearSRGBColorSpace, DisplayP3ColorSpace );
+
+			assert.equal( c.r.toFixed( 3 ), 0.614, 'Red: ' + c.r + ' (display-p3, in gamut)' );
+			assert.equal( c.g.toFixed( 3 ), 0.731, 'Green: ' + c.g + ' (display-p3, in gamut)' );
+			assert.equal( c.b.toFixed( 3 ), 0.843, 'Blue: ' + c.b + ' (display-p3, in gamut)' );
+
+			c.setRGB( 1.0, 0.5, 0.01, DisplayP3ColorSpace );
+
+			assert.equal( c.r.toFixed( 3 ), 1.177, 'Red: ' + c.r + ' (srgb-linear, out of gamut)' );
+			assert.equal( c.g.toFixed( 3 ), 0.181, 'Green: ' + c.g + ' (srgb-linear, out of gamut)' );
+			assert.equal( c.b.toFixed( 3 ), - 0.036, 'Blue: ' + c.b + ' (srgb-linear, out of gamut)' );
+
+			assert.equal( c.getStyle( DisplayP3ColorSpace ), 'color(display-p3 1.000 0.500 0.010)', 'style: display-p3' );
+
+		} );
+
+		QUnit.test( 'LinearDisplayP3ColorSpace', ( assert ) => {
+
+			const c = new Color().setRGB( 0.3, 0.5, 0.7 );
+
+			ColorManagement.convert( c, LinearSRGBColorSpace, LinearDisplayP3ColorSpace );
+
+			assert.equal( c.r.toFixed( 3 ), 0.336, 'Red: ' + c.r + ' (display-p3-linear, in gamut)' );
+			assert.equal( c.g.toFixed( 3 ), 0.493, 'Green: ' + c.g + ' (display-p3-linear, in gamut)' );
+			assert.equal( c.b.toFixed( 3 ), 0.679, 'Blue: ' + c.b + ' (display-p3-linear, in gamut)' );
+
+			c.setRGB( 1.0, 0.5, 0.01, LinearDisplayP3ColorSpace );
+
+			assert.equal( c.r.toFixed( 3 ), 1.112, 'Red: ' + c.r + ' (srgb-linear, out of gamut)' );
+			assert.equal( c.g.toFixed( 3 ), 0.479, 'Green: ' + c.g + ' (srgb-linear, out of gamut)' );
+			assert.equal( c.b.toFixed( 3 ), - 0.048, 'Blue: ' + c.b + ' (srgb-linear, out of gamut)' );
+
+			assert.equal( c.getStyle( LinearDisplayP3ColorSpace ), 'color(display-p3-linear 1.000 0.500 0.010)', 'style: display-p3-linear' );
+
+		} );
+
+		QUnit.test( 'LinearRec2020ColorSpace', ( assert ) => {
+
+			const c = new Color().setRGB( 0.3, 0.5, 0.7 );
+
+			ColorManagement.convert( c, LinearSRGBColorSpace, LinearRec2020ColorSpace );
+
+			assert.equal( c.r.toFixed( 3 ), 0.383, 'Red: ' + c.r + ' (rec2020-linear, in gamut)' );
+			assert.equal( c.g.toFixed( 3 ), 0.488, 'Green: ' + c.g + ' (rec2020-linear, in gamut)' );
+			assert.equal( c.b.toFixed( 3 ), 0.676, 'Blue: ' + c.b + ' (rec2020-linear, in gamut)' );
+
+			c.setRGB( 1.0, 0.5, 0.01, LinearRec2020ColorSpace );
+
+			assert.equal( c.r.toFixed( 3 ), 1.366, 'Red: ' + c.r + ' (srgb-linear, out of gamut)' );
+			assert.equal( c.g.toFixed( 3 ), 0.442, 'Green: ' + c.g + ' (srgb-linear, out of gamut)' );
+			assert.equal( c.b.toFixed( 3 ), - 0.057, 'Blue: ' + c.b + ' (srgb-linear, out of gamut)' );
+
+			assert.equal( c.getStyle( LinearRec2020ColorSpace ), 'color(rec2020-linear 1.000 0.500 0.010)', 'style: rec2020-linear' );
+
+		} );
+
+	} );
+
+} );

+ 0 - 1
test/unit/src/constants.tests.js

@@ -179,7 +179,6 @@ export default QUnit.module( 'Constants', () => {
 		assert.equal( Constants.NoColorSpace, '', 'NoColorSpace is equal to ""' );
 		assert.equal( Constants.SRGBColorSpace, 'srgb', 'SRGBColorSpace is equal to srgb' );
 		assert.equal( Constants.LinearSRGBColorSpace, 'srgb-linear', 'LinearSRGBColorSpace is equal to srgb-linear' );
-		assert.equal( Constants.DisplayP3ColorSpace, 'display-p3', 'DisplayP3ColorSpace is equal to display-p3' );
 
 		assert.equal( Constants.ZeroStencilOp, 0, 'ZeroStencilOp is equal to 0' );
 		assert.equal( Constants.KeepStencilOp, 7680, 'KeepStencilOp is equal to 7680' );

+ 1 - 20
test/unit/src/math/Color.tests.js

@@ -4,7 +4,7 @@ import { Color } from '../../../../src/math/Color.js';
 import { ColorManagement } from '../../../../src/math/ColorManagement.js';
 import { eps } from '../../utils/math-constants.js';
 import { CONSOLE_LEVEL } from '../../utils/console-wrapper.js';
-import { DisplayP3ColorSpace, SRGBColorSpace } from '../../../../src/constants.js';
+import { SRGBColorSpace } from '../../../../src/constants.js';
 
 export default QUnit.module( 'Maths', () => {
 
@@ -127,18 +127,6 @@ export default QUnit.module( 'Maths', () => {
 			assert.equal( c.g.toFixed( 3 ), 0.214, 'Green: ' + c.g + ' (srgb)' );
 			assert.equal( c.b.toFixed( 3 ), 0.448, 'Blue: ' + c.b + ' (srgb)' );
 
-			c.setRGB( 0.614, 0.731, 0.843, DisplayP3ColorSpace );
-
-			assert.numEqual( c.r.toFixed( 2 ), 0.3, 'Red: ' + c.r + ' (display-p3, in gamut)' );
-			assert.numEqual( c.g.toFixed( 2 ), 0.5, 'Green: ' + c.g + ' (display-p3, in gamut)' );
-			assert.numEqual( c.b.toFixed( 2 ), 0.7, 'Blue: ' + c.b + ' (display-p3, in gamut)' );
-
-			c.setRGB( 1.0, 0.5, 0.0, DisplayP3ColorSpace );
-
-			assert.numEqual( c.r.toFixed( 3 ), 1.179, 'Red: ' + c.r + ' (display-p3, out of gamut)' );
-			assert.numEqual( c.g.toFixed( 3 ), 0.181, 'Green: ' + c.g + ' (display-p3, out of gamut)' );
-			assert.numEqual( c.b.toFixed( 3 ), - 0.036, 'Blue: ' + c.b + ' (display-p3, out of gamut)' );
-
 		} );
 
 		QUnit.test( 'setHSL', ( assert ) => {
@@ -343,12 +331,6 @@ export default QUnit.module( 'Maths', () => {
 			assert.equal( t.g.toFixed( 3 ), ( 160 / 255 ).toFixed( 3 ), 'g (srgb)' );
 			assert.equal( t.b.toFixed( 3 ), ( 221 / 255 ).toFixed( 3 ), 'b (srgb)' );
 
-			c.getRGB( t, DisplayP3ColorSpace );
-
-			assert.equal( t.r.toFixed( 3 ), 0.831, 'r (display-p3)' );
-			assert.equal( t.g.toFixed( 3 ), 0.637, 'g (display-p3)' );
-			assert.equal( t.b.toFixed( 3 ), 0.852, 'b (display-p3)' );
-
 		} );
 
 		QUnit.test( 'getStyle', ( assert ) => {
@@ -358,7 +340,6 @@ export default QUnit.module( 'Maths', () => {
 			const c = new Color( 'plum' );
 
 			assert.equal( c.getStyle(), 'rgb(221,160,221)', 'style: srgb' );
-			assert.equal( c.getStyle( DisplayP3ColorSpace ), 'color(display-p3 0.831 0.637 0.852)', 'style: display-p3' );
 
 		} );
 

+ 1 - 0
test/unit/three.addons.unit.js

@@ -1,3 +1,4 @@
 
 //addons/utils
 import './addons/utils/BufferGeometryUtils.tests.js';
+import './addons/math/ColorSpaces.tests.js';

粤ICP备19079148号