Wikis / Unreal Wiki / Legacy:HLS To RGB Conversion

This set of functions can convert HLS values to RGB. hls2rgb takes the byte values of Hue, Luminosity, and Saturation and returns the Color it represents. (HueToRGB is a support function for the main conversion.)

Very liberally adapted from http://plaza27.mbn.or.jp/~satomii/design/win32/hls2rgb.html

How to convert HLS value to RGB value?

This is an useful function extracted from Microsoft's old sample code, modified a little by Rei for her own product. Note that this code is copyrighted to Microsoft Corporation.

Quoted from that page.
const HLSMAX = 240;
const RGBMAX = 255;
 
 
final simulated function float HueToRGB(float n1, float n2, float hue)
{
	if ( hue < 0 ) hue += HLSMAX;
	if ( hue > HLSMAX ) hue -= HLSMAX;
 
	/* return r,g, or b value from this tridrant */
	if ( hue < (HLSMAX/6) )
		return ( n1 + (((n2-n1)*hue + (HLSMAX/12))/(HLSMAX/6)) );
	if ( hue < (HLSMAX/2) )
		return n2;
	if ( hue < ((HLSMAX*2)/3) )
		return ( n1 + (((n2-n1)*(((HLSMAX*2)/3)-hue) + (HLSMAX/12))/(HLSMAX/6)) );
	else
		return n1;
}
 
 
final simulated function Color hls2rgb(byte hue, byte lum, byte sat)
{
	local Color C;
	local float Magic1, Magic2;
	local int R,G,B;
 
	if ( sat==0)
	{     /* achromatic case */
		C.R = (lum*RGBMAX) / HLSMAX;
		C.G = C.R;
		C.B = C.R;
 
		/* Small fix */
		R = C.R;
		G = C.G;
		B = C.B;
	}
	else
	{       /* chromatic case */
		/* set up magic numbers */
		if (lum <= (HLSMAX/2))
			Magic2 = (lum*(HLSMAX+sat)+(HLSMAX/2)) / HLSMAX;
		else
			Magic2 = lum+sat - ((lum*sat)+(HLSMAX/2)) / HLSMAX;
 
		Magic1 = 2*lum - Magic2;
 
		/* get RGB, change units from HLSMAX to RGBMAX */
		R = (HueToRGB(Magic1, Magic2, hue+(HLSMAX/3))
				* RGBMAX + (HLSMAX/2)) / HLSMAX;
		G = (HueToRGB(Magic1, Magic2, hue)
				* RGBMAX + (HLSMAX/2)) / HLSMAX;
		B = (HueToRGB(Magic1, Magic2, hue-(HLSMAX/3))
				* RGBMAX + (HLSMAX/2)) / HLSMAX;
	}
 
	C.R = R;
	C.G = G;
	C.B = B;
	C.A = 255;
	return C;
}
// ============================================================================
// HLStoRGB
//
// Converts the given HLS color value to its RGB equivalent and returns it.
//
//   H   Hue          from 0 (red) over 80 (green) and 160 (blue) to 240 (red)
//   L   Lightness    from 0 (black) over 120 (full color) to 240 (white)
//   S   Saturation   from 0 (gray) to 240 (full color)
//
// Due to its use of the color scaling operator this function expects to be
// implemented in an Actor subclass.
// ============================================================================
 
static function Color HLStoRGB(int H, int L, int S) {
 
  local int OffsetLightness;
  local int OffsetSaturation;
  local Color Color;
 
  if (H < 0) H = (240 - H) % 240;
        else H =        H  % 240;
 
       if (H <  80) Color.R = Min(255, 255 * (80 - H)  / 40);
  else if (H > 160) Color.R = Min(255, 255 * (H - 160) / 40);
 
  if (H < 160) Color.G = Min(255, 255 * (80 - Abs(H -  80)) / 40);
  if (H >  80) Color.B = Min(255, 255 * (80 - Abs(H - 160)) / 40);
 
  if (S < 240) {
    Color = Color * (float(S) / 240);
    OffsetSaturation = 128 * (240 - S) / 240;
    Color.R += OffsetSaturation;
    Color.G += OffsetSaturation;
    Color.B += OffsetSaturation;
    }
 
  L = Min(240, L);
  Color = Color * ((120 - Abs(L - 120)) / 120);
 
  if (L > 120) {
    OffsetLightness = 256 * (L - 120) / 120;
    Color.R += OffsetLightness;
    Color.G += OffsetLightness;
    Color.B += OffsetLightness;
    }
 
  return Color;
  }

UArch slight modification to the above script

ok changed it now, im using this custom version for a script im doing

input HLS values range from 0-255, output vector with ranges 0-1 x=R y=G z=B

simulated final function vector HLStoRGB(float H,float L,float S)
{
	local float OffsetLightness;
	local float OffsetSaturation;
	local vector cout;
 
	H = fclamp(H,0,255);
	L = fclamp(L,0,255);
	S = fclamp(S,0,255);
	S = 255 - S;
 
	if(H < 85)
		cout.x = fMin(1, (85 - H)  / 42.5);
 
	else if(H > 170)
		cout.x = fMin(1, (H - 170) / 42.5);
 
	if (H < 170)
		cout.y = fMin(1, (85 - Abs(H -  85)) / 42.5);
 
	if (H > 85)
		cout.z = fMin(1, (85 - Abs(H - 170)) / 42.5);
 
	if (S < 255)
	{
		cout = cout * (S / 255);
		OffsetSaturation = /*128 **/ (255 - S) / 255;
		cout.x += OffsetSaturation;
		cout.y += OffsetSaturation;
		cout.z += OffsetSaturation;
	}
 
	cout = cout * ((255 - Abs(L - 255)) / 255);
 
	return cout;
}

Here's what Sourced Player Shadows uses to determine the light color when casting colored shadows. It's based on direct measurements of the color of white walls in a zone with various ambient colors set. You'll have to add your own code to scale for light brightness. This isn't anything like the scientifically correct way to do the conversion, but my guess is they were just kinda guessing when they wrote it the first time.

// Convert the color (not brightness) of an Unreal light to RGB values from 0 to 1
// inputs are float so I don't have to worry about casting them.
simulated function Chroma2RGB(float hue, float saturation,
                     out float r, out float g, out float b) {
  hue /= 85;
  if (hue <= 1)      { g = hue  ; r = 1-g; b = 0;}
  else if (hue <= 2) { b = hue-1; g = 1-b; r = 0;}
  else               { r = hue-2; b = 1-r; g = 0;}
  saturation = FClamp(saturation/255,0,1);
  r = lerp(saturation,r,1);
  g = lerp(saturation,g,1);
  b = lerp(saturation,b,1);
}

Comments

capt.k: HLSMAX and RGBMAX are variables (which can obviously be hard-coded into the script) because I wasn't sure at the time of writing what values would be appropriate. The UEd color picker uses 240 as the max for HLS values, but the slider has 255.

Wormbo: Actor.LightSaturation goes from 0 to 255, where 0 means full saturation and 255 indicates a pure grey color. LightHue also goes from 0 to 255 and LightBrightness is an unlimited float value. The color picker in UEd is somwhat useless for light colors, except for the hue value.

Mychaeel: Since in Windows the "saturation" property usually goes from 0 for "no saturation" (grayscale) to 255 for "full saturation" (full color), that could be the reason that the algorithm posted above doesn't work here without adaptation.

Mychaeel: I've posted an alternative implementation created from scratch that also uses Windows' HLS definition (see comment header). If Unreal's really differs from it, adapt the parameters before passing them to that function.

Please test it. If it works as advertised, let's get rid of the first Microsoft-derived implementation (which is longer and not designed for UnrealScript).

inio: Added what I came up with for Sourced Player Shadows.

Sixpack-Shambler Am I wrong or in the first HLStoRGB function on this page, if Sat == 0 (Achromatic case) will the result not turn out with no colours at all? Look at the code, at the end of it the local variables R G B are applied to C.R, C.G and C.B but if Sat == 0 then the R G B variables are not modifed?

I've changed the code a bit to 'fix' that

Related Topics

Page Information

2022-11-18T09:46:05.662474Z 2006-07-10T08:52:25Z 82-43-89-237.cable.ubr08.croy.blueyonder.co.uk * https://wiki.beyondunreal.com/Legacy:HLS To RGB Conversion Attribution-NonCommercial-ShareAlike 3.0