Просмотр исходного кода

ColorUtils: Add setKelvin(). (#33381)

Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
Nedunchezhiyan-M 1 день назад
Родитель
Сommit
f0e7d5ef96

+ 76 - 0
examples/jsm/utils/ColorUtils.js

@@ -0,0 +1,76 @@
+import { MathUtils, SRGBColorSpace } from 'three';
+
+/**
+ * @module ColorUtils
+ * @three_import import * as ColorUtils from 'three/addons/utils/ColorUtils.js';
+ */
+
+/**
+ * Sets the given color from a color temperature in Kelvin.
+ *
+ * Converts a correlated color temperature (CTT) to an approximate sRGB color
+ * using Tanner Helland's algorithm. Useful for physically-based lighting
+ * setups — e.g. candle flame (~1900K), tungsten bulb (~3200K), daylight
+ * (~6500K), or clear blue sky (~10000K). Values outside [1000, 40000] are
+ * clamped.
+ *
+ * Reference: https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html
+ *
+ * @param {Color} color - The color to set.
+ * @param {number} kelvin - Color temperature in Kelvin. Clamped to [1000, 40000].
+ * @return {Color} The updated color.
+ */
+function setKelvin( color, kelvin ) {
+
+	// Algorithm by Tanner Helland (2012). Inputs are divided by 100.
+	const temp = MathUtils.clamp( kelvin, 1000, 40000 ) / 100;
+
+	let r, g, b;
+
+	// Red channel
+	if ( temp <= 66 ) {
+
+		r = 255;
+
+	} else {
+
+		r = 329.698727446 * Math.pow( temp - 60, - 0.1332047592 );
+
+	}
+
+	// Green channel
+	if ( temp <= 66 ) {
+
+		g = 99.4708025861 * Math.log( temp ) - 161.1195681661;
+
+	} else {
+
+		g = 288.1221695283 * Math.pow( temp - 60, - 0.0755148492 );
+
+	}
+
+	// Blue channel
+	if ( temp >= 66 ) {
+
+		b = 255;
+
+	} else if ( temp <= 19 ) {
+
+		b = 0;
+
+	} else {
+
+		b = 138.5177312231 * Math.log( temp - 10 ) - 305.0447927307;
+
+	}
+
+	return color.setRGB(
+		MathUtils.clamp( r, 0, 255 ) / 255,
+		MathUtils.clamp( g, 0, 255 ) / 255,
+		MathUtils.clamp( b, 0, 255 ) / 255,
+		SRGBColorSpace
+	);
+
+}
+
+export { setKelvin };

+ 53 - 0
test/unit/addons/utils/ColorUtils.tests.js

@@ -0,0 +1,53 @@
+import { Color } from '../../../../src/math/Color.js';
+import { ColorManagement } from '../../../../src/math/ColorManagement.js';
+import * as ColorUtils from '../../../../examples/jsm/utils/ColorUtils.js';
+
+export default QUnit.module( 'Addons', () => {
+
+	QUnit.module( 'Utils', () => {
+
+		QUnit.module( 'ColorUtils', () => {
+
+			const colorManagementEnabled = ColorManagement.enabled;
+
+			QUnit.testDone( () => {
+
+				ColorManagement.enabled = colorManagementEnabled;
+
+			} );
+
+			QUnit.test( 'setKelvin', ( assert ) => {
+
+				ColorManagement.enabled = false; // TODO: Update and enable.
+
+				const c = new Color();
+
+				// ~1900K candle flame — warm reddish-orange, no blue
+				ColorUtils.setKelvin( c, 1900 );
+				assert.ok( c.r > c.g && c.g > c.b && c.b === 0, 'Candle (~1900K): r > g > b, b = 0' );
+
+				// ~6500K daylight — roughly white, all channels near 1
+				ColorUtils.setKelvin( c, 6500 );
+				assert.ok( c.r > 0.9 && c.g > 0.9 && c.b > 0.9, 'Daylight (~6500K): all channels near 1' );
+
+				// clamping: below 1000K should equal 1000K
+				const atMin = ColorUtils.setKelvin( new Color(), 1000 );
+				ColorUtils.setKelvin( c, 500 );
+				assert.ok( c.equals( atMin ), 'Values below 1000K are clamped to 1000K' );
+
+				// clamping: above 40000K should equal 40000K
+				const atMax = ColorUtils.setKelvin( new Color(), 40000 );
+				ColorUtils.setKelvin( c, 50000 );
+				assert.ok( c.equals( atMax ), 'Values above 40000K are clamped to 40000K' );
+
+				// ~10000K cool blue sky — blue channel above red
+				ColorUtils.setKelvin( c, 10000 );
+				assert.ok( c.b > c.r, 'Blue sky (~10000K): b > r' );
+
+			} );
+
+		} );
+
+	} );
+
+} );

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

@@ -1,6 +1,7 @@
 
 //addons/utils
 import './addons/utils/BufferGeometryUtils.tests.js';
+import './addons/utils/ColorUtils.tests.js';
 import './addons/math/ColorSpaces.tests.js';
 import './addons/curves/NURBSCurve.tests.js';
 import './addons/loaders/HDRLoader.tests.js';

粤ICP备19079148号