From 5c94e668c50d647b46f515eab156f07fec873bbe Mon Sep 17 00:00:00 2001
From: Don McCurdy [method:this fromBufferAttribute]( [param:BufferAttribute attribute], [param
Sets this color's components from the [page:BufferAttribute attribute].
Returns the hexadecimal value of this color.
-Returns the hexadecimal value of this color as a string (for example, 'FFFFFF').
-
[page:Object target] — the result will be copied into this Object. Adds h, s and l keys to the object (if not already present).
@@ -180,7 +180,7 @@
Returns the value of this color as a CSS style string. Example: 'rgb(255,0,0)'.
[page:Integer hex] — [link:https://en.wikipedia.org/wiki/Web_colors#Hex_triplet hexadecimal triplet] format.
Sets this color from a hexadecimal value.
[page:Float h] — hue value between 0.0 and 1.0
[page:Float s] — saturation value between 0.0 and 1.0
@@ -253,7 +253,7 @@
[page:Float r] — Red channel value between 0.0 and 1.0.
[page:Float g] — Green channel value between 0.0 and 1.0.
@@ -269,7 +269,7 @@
[page:String style] — color as a CSS-style string.
@@ -288,7 +288,7 @@
[page:String style] — color name ( from [link:https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart X11 color names] ).
diff --git a/docs/api/zh/math/Color.html b/docs/api/zh/math/Color.html
index e7cb495186cb51..7c76c88121c3dc 100644
--- a/docs/api/zh/math/Color.html
+++ b/docs/api/zh/math/Color.html
@@ -161,13 +161,13 @@
返回此颜色的十六进制值。
-将此颜色的十六进制值作为字符串返回 (例如, 'FFFFFF')。
-
[page:Object target] — 结果将复制到这个对象中。向对象添加h、s和l键(如果不存在)。
@@ -179,7 +179,7 @@
以CSS样式字符串的形式返回该颜色的值。例如:“rgb(255,0,0)”。
[page:Integer hex] — [link:https://en.wikipedia.org/wiki/Web_colors#Hex_triplet hexadecimal triplet] 格式。
@@ -239,7 +239,7 @@
[page:Float h] — 色相值处于0到1之间。hue value between 0.0 and 1.0
[page:Float s] — 饱和度值处于0到1之间。
@@ -248,7 +248,7 @@
[page:Float r] — 红色通道的值在0到1之间。
[page:Float g] — 绿色通道的值在0到1之间。
@@ -264,7 +264,7 @@
[page:String style] — 颜色css样式的字符串
@@ -283,7 +283,7 @@
[page:String style] — 颜色名字的英文单词 ( 具体请查阅 [link:https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart X11 color names] )
diff --git a/src/Three.js b/src/Three.js
index 6af1dec2990343..605a974c9b6fe7 100644
--- a/src/Three.js
+++ b/src/Three.js
@@ -127,6 +127,7 @@ export { Vector3 } from './math/Vector3.js';
export { Vector2 } from './math/Vector2.js';
export { Quaternion } from './math/Quaternion.js';
export { Color } from './math/Color.js';
+export { ColorManagement } from './math/ColorManagement.js';
export { SphericalHarmonics3 } from './math/SphericalHarmonics3.js';
export { SpotLightHelper } from './helpers/SpotLightHelper.js';
export { SkeletonHelper } from './helpers/SkeletonHelper.js';
diff --git a/src/constants.js b/src/constants.js
index cccad2ddc33a7c..6291ba78fd65ad 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -145,6 +145,11 @@ export const RGBADepthPacking = 3201;
export const TangentSpaceNormalMap = 0;
export const ObjectSpaceNormalMap = 1;
+// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available.
+export const NoColorSpace = '';
+export const SRGBColorSpace = 'srgb';
+export const LinearSRGBColorSpace = 'srgb-linear';
+
export const ZeroStencilOp = 0;
export const KeepStencilOp = 7680;
export const ReplaceStencilOp = 7681;
diff --git a/src/math/Color.js b/src/math/Color.js
index 70a5a3a614083c..2b682b9b5a6bc1 100644
--- a/src/math/Color.js
+++ b/src/math/Color.js
@@ -1,4 +1,6 @@
import { clamp, euclideanModulo, lerp } from './MathUtils.js';
+import { ColorManagement, SRGBToLinear, LinearToSRGB } from './ColorManagement.js';
+import { SRGBColorSpace, LinearSRGBColorSpace } from '../constants.js';
const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
@@ -25,6 +27,7 @@ const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua'
'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
+const _rgb = { r: 0, g: 0, b: 0 };
const _hslA = { h: 0, s: 0, l: 0 };
const _hslB = { h: 0, s: 0, l: 0 };
@@ -39,15 +42,13 @@ function hue2rgb( p, q, t ) {
}
-function SRGBToLinear( c ) {
+function toComponents( source, target ) {
- return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
+ target.r = source.r;
+ target.g = source.g;
+ target.b = source.b;
-}
-
-function LinearToSRGB( c ) {
-
- return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
+ return target;
}
@@ -96,7 +97,7 @@ class Color {
}
- setHex( hex ) {
+ setHex( hex, colorSpace = SRGBColorSpace ) {
hex = Math.floor( hex );
@@ -104,21 +105,25 @@ class Color {
this.g = ( hex >> 8 & 255 ) / 255;
this.b = ( hex & 255 ) / 255;
+ ColorManagement.toWorkingColorSpace( this, colorSpace );
+
return this;
}
- setRGB( r, g, b ) {
+ setRGB( r, g, b, colorSpace = LinearSRGBColorSpace ) {
this.r = r;
this.g = g;
this.b = b;
+ ColorManagement.toWorkingColorSpace( this, colorSpace );
+
return this;
}
- setHSL( h, s, l ) {
+ setHSL( h, s, l, colorSpace = LinearSRGBColorSpace ) {
// h,s,l ranges are in 0.0 - 1.0
h = euclideanModulo( h, 1 );
@@ -140,11 +145,13 @@ class Color {
}
+ ColorManagement.toWorkingColorSpace( this, colorSpace );
+
return this;
}
- setStyle( style ) {
+ setStyle( style, colorSpace = SRGBColorSpace ) {
function handleAlpha( string ) {
@@ -181,6 +188,8 @@ class Color {
this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
+ ColorManagement.toWorkingColorSpace( this, colorSpace );
+
handleAlpha( color[ 4 ] );
return this;
@@ -194,6 +203,8 @@ class Color {
this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
+ ColorManagement.toWorkingColorSpace( this, colorSpace );
+
handleAlpha( color[ 4 ] );
return this;
@@ -214,7 +225,7 @@ class Color {
handleAlpha( color[ 4 ] );
- return this.setHSL( h, s, l );
+ return this.setHSL( h, s, l, colorSpace );
}
@@ -236,6 +247,8 @@ class Color {
this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
+ ColorManagement.toWorkingColorSpace( this, colorSpace );
+
return this;
} else if ( size === 6 ) {
@@ -245,6 +258,8 @@ class Color {
this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
+ ColorManagement.toWorkingColorSpace( this, colorSpace );
+
return this;
}
@@ -253,7 +268,7 @@ class Color {
if ( style && style.length > 0 ) {
- return this.setColorName( style );
+ return this.setColorName( style, colorSpace );
}
@@ -261,7 +276,7 @@ class Color {
}
- setColorName( style ) {
+ setColorName( style, colorSpace = SRGBColorSpace ) {
// color keywords
const hex = _colorKeywords[ style.toLowerCase() ];
@@ -269,7 +284,7 @@ class Color {
if ( hex !== undefined ) {
// red
- this.setHex( hex );
+ this.setHex( hex, colorSpace );
} else {
@@ -334,23 +349,27 @@ class Color {
}
- getHex() {
+ getHex( colorSpace = SRGBColorSpace ) {
+
+ ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace );
- return clamp( this.r * 255, 0, 255 ) << 16 ^ clamp( this.g * 255, 0, 255 ) << 8 ^ clamp( this.b * 255, 0, 255 ) << 0;
+ return clamp( _rgb.r * 255, 0, 255 ) << 16 ^ clamp( _rgb.g * 255, 0, 255 ) << 8 ^ clamp( _rgb.b * 255, 0, 255 ) << 0;
}
- getHexString() {
+ getHexString( colorSpace = SRGBColorSpace ) {
- return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
+ return ( '000000' + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 );
}
- getHSL( target ) {
+ getHSL( target, colorSpace = LinearSRGBColorSpace ) {
// h,s,l ranges are in 0.0 - 1.0
- const r = this.r, g = this.g, b = this.b;
+ ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace );
+
+ const r = _rgb.r, g = _rgb.g, b = _rgb.b;
const max = Math.max( r, g, b );
const min = Math.min( r, g, b );
@@ -389,9 +408,30 @@ class Color {
}
- getStyle() {
+ getRGB( target, colorSpace = LinearSRGBColorSpace ) {
+
+ ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace );
+
+ target.r = _rgb.r;
+ target.g = _rgb.g;
+ target.b = _rgb.b;
+
+ return target;
+
+ }
+
+ getStyle( colorSpace = SRGBColorSpace ) {
+
+ ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace );
+
+ if ( colorSpace !== SRGBColorSpace ) {
+
+ // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/).
+ return `color(${ colorSpace } ${ _rgb.r } ${ _rgb.g } ${ _rgb.b })`;
+
+ }
- return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
+ return `rgb(${( _rgb.r * 255 ) | 0},${( _rgb.g * 255 ) | 0},${( _rgb.b * 255 ) | 0})`;
}
diff --git a/src/math/ColorManagement.js b/src/math/ColorManagement.js
new file mode 100644
index 00000000000000..e0b3f68da5a2d1
--- /dev/null
+++ b/src/math/ColorManagement.js
@@ -0,0 +1,74 @@
+import { SRGBColorSpace, LinearSRGBColorSpace } from '../constants.js';
+
+export function SRGBToLinear( c ) {
+
+ return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
+
+}
+
+export function LinearToSRGB( c ) {
+
+ return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
+
+}
+
+// JavaScript RGB-to-RGB transforms, defined as
+// FN[InputColorSpace][OutputColorSpace] callback functions.
+const FN = {
+ [ SRGBColorSpace ]: { [ LinearSRGBColorSpace ]: SRGBToLinear },
+ [ LinearSRGBColorSpace ]: { [ SRGBColorSpace ]: LinearToSRGB },
+};
+
+export const ColorManagement = {
+
+ legacyMode: true,
+
+ get workingColorSpace() {
+
+ return LinearSRGBColorSpace;
+
+ },
+
+ set workingColorSpace( colorSpace ) {
+
+ console.warn( 'THREE.ColorManagement: .workingColorSpace is readonly.' );
+
+ },
+
+ convert: function ( color, sourceColorSpace, targetColorSpace ) {
+
+ if ( this.legacyMode || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
+
+ return color;
+
+ }
+
+ if ( FN[ sourceColorSpace ] && FN[ sourceColorSpace ][ targetColorSpace ] !== undefined ) {
+
+ const fn = FN[ sourceColorSpace ][ targetColorSpace ];
+
+ color.r = fn( color.r );
+ color.g = fn( color.g );
+ color.b = fn( color.b );
+
+ return color;
+
+ }
+
+ throw new Error( 'Unsupported color space conversion.' );
+
+ },
+
+ fromWorkingColorSpace: function ( color, targetColorSpace ) {
+
+ return this.convert( color, this.workingColorSpace, targetColorSpace );
+
+ },
+
+ toWorkingColorSpace: function ( color, sourceColorSpace ) {
+
+ return this.convert( color, sourceColorSpace, this.workingColorSpace );
+
+ },
+
+};