Color Models
In the previous two sections, we combed through two workflows to ensure that the color data fed into the shader is as expected. The shader will process the input color data. In the next chapters, we will talk about some color expressions and common processing methods.
RGB Color Model
Humans have three main types of cone cells, which are most sensitive to red (wavelength around 600nm), green (wavelength around 550nm), and blue (wavelength around 450nm) when performing color recognition. This determines the area of color we can perceive. Based on this theory, the RGB color model was created. The RGB color model is the color model we are most exposed to, using a linear combination of three channels to represent a color. The threecolor channels are Red, Green, and Blue.
(Unity RGB color adjustment interface)
Any color is associated with these three components, making it unintuitive to continuously transform colors. Because these three variables are all related to brightness, when the brightness is changed, all three components will change accordingly. Moreover, the human eye has different degrees of sensitivity to the components of the three colors. The human eye is the least sensitive to red and the most sensitive to blue. This makes it difficult for us to more accurately infer the specific value of color under the RGB model. Based on this defect, when artists develop, they will not use the RGB color model but will use the more intuitive HSV color model.
HSV color model
Before explaining the HSV color model, we first need to understand a concept: spectral color.
After the polychromatic light is split by a dispersive system (such as a prism and a grating), the dispersed monochromatic light is arranged in order of wavelength. It is spectral color.
Compared to RGB, HSV is a more intuitive color model. The meaning of each part is:
H: Hue, Hue (Hue), measured in angle, the value range is 0°~360°, and it is calculated counterclockwise from red. It can be understood that the corresponding spectral color is selected according to the wavelength.
S: Saturation, color purity (Saturation), the value range is 0%~100%, indicating the degree of closeness to the spectral color. Any color can be seen as the result of mixing a certain spectral color with white. The white light component of the spectral color is 0, and the saturation is the highest.
V: Brightness (Value), indicating the brightness of the color, the value range is 0% (black) ~ 100%
The HSV color model is represented by the model shown in the figure above. The crosssection of the cylinder can be regarded as a polar coordinate system. The H parameter is represented by the polar angle of polar coordinates, the S parameter is represented by the polar axis length of polar coordinates, and the V parameter is represented by the height of the cylinder axis.
In the RGB color model, red is determined by RGB=(255, 0, 0). In the HSV model, the parameter H=0 can be represented.
The halfside crosssection when parameter H=0 is as follows:
It can be clearly seen from the figure that:
The horizontal direction is from left to right. As the value of parameter S increases, the higher the saturation, the darker the color, and the closer the color is to the spectral color. On the contrary, as the value of parameter S decreases, the smaller the saturation, the lighter the color, and the closer the color is to white. A saturation of 0 is pure white.
From bottom to top in the vertical direction, as the value of parameter V increases, the higher the brightness, the brighter the color. On the contrary, as the value of parameter V decreases, the lower the brightness, the darker the color. A lightness of 0 means pure black.
Through the HSV color model, we can easily get a single color and adjust the brightness and saturation of the specified spectral color.
Mutual Conversion
Basic Content
There are specific conversion formulas between the RGB color model and the HSV color model.
There is a corresponding API in Unity for conversion:
public static Color HSVToRGB(float H, float S, float V);
public static void RGBToHSV(Color rgbColor, out float H, out float S, out float V);
There are corresponding blueprint nodes in Unreal:
However, there is no related function in Shader that can directly complete such conversion, and it is necessary to understand the conversion relationship between the two color models.
The RGB color model can be represented based on the Cartesian coordinate system. The components of the three channels R, G, and B are the same, and the geometric model shown in the figure above is constructed through the Cartesian coordinate system. The color represented by the origin of the coordinates is black, and the color represented by the coordinates (1, 1, 1) is white. The vector (1, 1, 1) can represent the enhancement of lightness, and its meaning is equivalent to the coordinate axis of the parameter V in the HSV color model.
In the HSV color model, it can be considered that the points of the RGB color model in the Cartesian coordinate system are represented in the cylindrical coordinate system. There are many conversion schemes for the two coordinate systems, which also produce different color models.
As shown in the figure above, first take the extension line of the vector from the origin (0,0,0) to the white point (1,1,1) in the RGB color model as the Z axis in the cylindrical coordinate system, and take the origin (black) as The origin of the cylindrical coordinate system. Next, create different mappings according to different rules. The HSV model is to project the seven colors of Red, Green, Blue, Cyan (cyan), Magenta (magenta), Yellow (yellow), and White (white) into the plane composed of RGB, thereby obtaining a regular hexagonal pyramid. Geometric model, and then expand this regular hexagonal pyramid into a regular hexagonal prism, and then expand it into a cylinder, and the obtained model is the HSV geometric model. As a result, a onetoone mapping relationship is formed between the RGB color model and the colors in the HSV color model.
Conversion from RGB color model to HSV color model
Let (r, g, b) be the RGB coordinates of color in the geometric model of the RGB color model, and their values are real numbers between (0, 1). Let max be the maximum value among r, g, and b, and min be the minimum value among r, g, and b. Convert it to points in the HSV color model, calculated as:
Where the value of h is normalized to lie between 0° and 360°.
Building shader code in Unity based on the above mathematical model. First normalize the data in the formula to the range of 0 to 1, which is equivalent to dividing all by 360°. Rearrange the formula and get the data needed for each function:
The required data is filtered through the combination of the lerp function and the step function. The step function is used to compare the size of the three components of r, g, and b, and the corresponding vector is selected by the lerp function according to the size result. Use 1.0e10 to participate in the operation to prevent the divisor from being 0. The calculation of the remaining s and v is relatively simple.
Get the Shader code:
float3 RGBToHSV(float3 c)
{
float4 K = float4(0.0, 1.0 / 3.0, 2.0 / 3.0, 1.0);
float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
float d = q.x – min(q.w, q.y);
float e = 1.0e10;
return float3(abs(q.z + (q.w – q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
Conversion from HSV color model to RGB color model
Let (h, s, v) be the coordinates of color in the cylindrical coordinate system of the HSV color model. The value of h is normalized to lie between 0° and 360°; the value of s is normalized to be between 0 and 1 for saturation; the value of v is normalized to be between 0 and 1 for lightness. Convert it to points in the RGB color model, calculated as:
Likewise, the value of the variable H is normalized between (0,1).
According to the above mathematical model, the Shader code is constructed in Unity: The calculation formulas of the three variables r, g, and b are disassembled. The calculation formula table according to the different choices of hi is as follows:
The calculation of the three variables can be classified under the same formula by means of offset. Based on the formula calculation of the variable R, the offsets of the two variables G and B are 2/3 and 1/3 respectively. That is, k.xyz in the code.
It can be seen from the observation that each calculation method can be expressed in the form of the lerp function:
For the variable p, it can be converted to v*lerp(1, 0, s).
For variable q, it can be converted to v*lerp(1, 1f, s).
For variable t, it can be converted to v*lerp(1, f, s).
For the variable v, it can be converted to v*lerp(1, 1, s).
From this, the shader code is easier to understand. By subtracting 3 from the variable and then finding the absolute value, the operation of the variable q and the variable t can be distinguished. The operation of the variable p and the variable v can be distinguished by the saturate function.
Get the code:
float3 HSVToRGB(float3 c)
{
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(c.xxx + K.xyz) * 6.0 – K.www);
return c.z * lerp(K.xxx, saturate(p – K.xxx), c.y);
}
Color Grading
After understanding the color model and how to convert each other, we do some color grading on the final picture in postprocessing. Color Grading is a relatively broad topic, the main purpose is to improve the performance of the picture, including adjusting the hue, saturation, brightness, contrast, white balance and many other operations. Next, we list some common Color Grading operations.
Saturation & Brightness (Value)
The variable S and variable V in the HSV model are saturation and brightness, respectively, so adjusting saturation and brightness is a relatively simple operation. After converting the RGB color model to the HSV color model, adjust the values of S and V.
Adjust Saturation
Code:
float4 SetPixelShader(float4 vertex:POSITION,float2 uv : TEXCOORD0) : SV_TARGET
{
float4 color = tex2D(_MainTex, uv * _MainTex_ST.xy + _MainTex_ST.zw);
float3 source = RGBToHSV(color.rgb);
source = float3(source.r, source.g * _saturation, source.b);
color.rgb = HSVToRGB(saturate(source));
return color;
}
Original Image:
Multiply each pixel’s respective saturation (S value in HSV) by 1.8:
One of the more special ones is to adjust the saturation to 0 to get the effect of the black and white filter:
Adjust the Brightness
Code:
float4 SetPixelShader(float4 vertex:POSITION,float2 uv : TEXCOORD0) : SV_TARGET
{
float4 color = tex2D(_MainTex, uv * _MainTex_ST.xy + _MainTex_ST.zw);
float3 source = RGBToHSV(color.rgb);
source = float3(source.r, source.g, source.b*_value);
color.rgb = HSVToRGB(saturate(source));
return color;
}
Decrease the Brightness:
Original Image:
Turn up the brightness:
Contrast
Contrast refers to the measurement of different brightness levels between the brightest white and the darkest black in the light and dark areas of an image. The larger the difference range, the greater the contrast, the smaller the difference range, the smaller the contrast. A good contrast ratio is 120. :1 can easily display vivid and rich colors, and when the contrast ratio is as high as 300:1, it can support all levels of color.
For the HSV color model, the contrast can be adjusted by increasing the difference in brightness (V value):
float4 SetPixelShader(float4 vertex:POSITION,float2 uv : TEXCOORD0) : SV_TARGET
{
float4 color = tex2D(_MainTex, uv * _MainTex_ST.xy + _MainTex_ST.zw);
float3 source = RGBToHSV(color.rgb);
source = float3(source.r, source.g, ((source.b – 0.5)* _value + 0.5));
color.rgb = HSVToRGB(saturate(source));
return color;
}
Reduce contrast:
Enhance Contrast:
Inverse
An inverse color is a color that can become white when superimposed on a primary color, that is, a color that subtracts the primary color from white. Therefore, the implementation of this filter is relatively simple. Under the RGB color model, just use white minus the primary color:
It’s easier to implement using the RGB color model:
float4 SetPixelShader(float4 vertex:POSITION,float2 uv : TEXCOORD0) : SV_TARGET
{
float4 color = tex2D(_MainTex, uv * _MainTex_ST.xy + _MainTex_ST.zw);
color.rgb = float3(1 – color.r, 1 – color.g, 1 – color.b);
return color;
}
The effect of Inverse:
Screen Post Processing Effects Series
Screen Postprocessing Effects: Radial Blur and Its Implementation in Unity
Screen Postprocessing Effects: Silhouette Rendering and Its Implementation in Unity
Screen Postprocessing Effects: Depth of Field (DOF) and Its Implementation
Screen Postprocessing Effects: Streak effect in the Lens Flare effect and Its Implementation
Screen Postprocessing Effects: Realtime Glow and Bloom and Its Implementation
Screen Post Processing Effects Chapter 6: Dual Blur and Its Implementation
Screen Post Processing Effects Chapter 5: Kawase Blur and Its Implementation
Screen Post Processing Effects Chapter 4: Box Blur and Its Implementation
Screen Post Processing Effects Chapter 1 – Basic Algorithm of Gaussian Blur and Its Implementation
That’s all for today’s sharing. Of course, life is boundless but knowing is boundless. In the long development cycle, these problems you see maybe just the tip of the iceberg. We have already prepared more technical topics on the UWA Q&A website, waiting for you to explore and share them together. You are welcome to join us, who love progress. Maybe your method can solve the urgent needs of others, and the “stone” of other mountains can also attack your “jade”.
YOU MAY ALSO LIKE!!!
UWA Website: https://en.uwa4d.com
UWA Blogs: https://blog.en.uwa4d.com
UWA Product: https://en.uwa4d.com/feature/got
You may also like

Dynamic Resolution in Games from Principle to Application
January 4, 2023 
Introduction of the Raytracing Technology Part 2
December 21, 2022