1 Color
This appendix collects techniques for mapping scalar values to colors. These are tools you copy, paste, and tweak as needed.
1.1 RGB Basics
A color in GLSL is a vec3 with red, green, and blue components, each ranging from 0.0 to 1.0:
vec3 red = vec3(1.0, 0.0, 0.0);
vec3 white = vec3(1.0, 1.0, 1.0);
vec3 gray = vec3(0.5, 0.5, 0.5);
vec3 yellow = vec3(1.0, 1.0, 0.0);Mixing colors linearly often produces muddy results. For smooth color gradients, HSV or cosine palettes usually work better.
1.2 HSV
HSV (hue, saturation, value) separates color into intuitive components:
- Hue: the color itself, as an angle around a circle (0 = red, 0.33 = green, 0.67 = blue, 1 = red again)
- Saturation: intensity, from gray (0) to vivid (1)
- Value: brightness, from black (0) to full (1)
This is useful when you want to cycle through colors smoothly. Incrementing hue gives a rainbow; keeping hue fixed while varying value gives natural shading.
vec3 hsv2rgb(vec3 c) {
vec3 p = abs(fract(c.xxx + vec3(0.0, 2.0/3.0, 1.0/3.0)) * 6.0 - 3.0);
return c.z * mix(vec3(1.0, 1.0, 1.0), clamp(p - 1.0, 0.0, 1.0), c.y);
}Usage:
float hue = 0.6; // blue
float saturation = 1.0; // vivid
float value = 0.8; // fairly bright
vec3 color = hsv2rgb(vec3(hue, saturation, value));To cycle through colors based on a scalar t:
vec3 color = hsv2rgb(vec3(t, 1.0, 1.0)); // rainbowCommon Patterns
Rainbow based on angle (color wheel):
float angle = atan(p.y, p.x); // -π to π
float hue = angle / 6.28318 + 0.5; // normalize to [0, 1]
vec3 color = hsv2rgb(vec3(hue, 1.0, 1.0));Rainbow based on distance (concentric rings):
float hue = fract(length(p) * 0.5); // repeating bands
vec3 color = hsv2rgb(vec3(hue, 0.8, 0.9));Animated color cycling:
vec3 color = hsv2rgb(vec3(fract(iTime * 0.2), 0.8, 1.0));Escape-time coloring (for fractals):
float hue = fract(float(iterations) * 0.05);
vec3 color = hsv2rgb(vec3(hue, 0.8, 0.9));1.3 Cosine Palettes
A cosine palette generates smooth color gradients from a simple formula:
vec3 palette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.00, 0.33, 0.67);
return a + b * cos(6.28318 * (c * t + d));
}Each color channel is a cosine wave. The parameters control:
a: the center (vertical offset) of each waveb: the amplitude of each wavec: the frequency (how many cycles as t goes 0 to 1)d: the phase offset for each channel
The default values above produce a palette cycling through blue, pink, orange, and back. Changing d shifts which colors appear:
d |
Character |
|---|---|
vec3(0.0, 0.1, 0.2) |
Cool blues and cyans |
vec3(0.3, 0.2, 0.2) |
Warm oranges and reds |
vec3(0.0, 0.5, 0.5) |
Greens and magentas |
For more palettes and an interactive explorer, see Inigo Quilez’s article: https://iquilezles.org/articles/palettes/
1.4 Colormaps for Scalar Fields
When visualizing data like temperature or wave amplitude, the choice of colormap matters.
Sequential colormaps go from dark to light (or vice versa). Good for data with a natural ordering:
// Simple black-to-white
vec3 color = vec3(t, t, t);
// Black through blue to white (cold-to-hot feel)
vec3 color = mix(vec3(0.0, 0.0, 0.0), vec3(0.5, 0.7, 1.0), t);Diverging colormaps have a neutral center and different colors at each extreme. Good for data with a meaningful midpoint (zero velocity, equilibrium temperature):
// Blue (negative) - white (zero) - red (positive)
// Assumes t is remapped so 0.5 = zero
vec3 color;
if (t < 0.5) {
color = mix(vec3(0.0, 0.0, 1.0), vec3(1.0, 1.0, 1.0), t * 2.0);
} else {
color = mix(vec3(1.0, 1.0, 1.0), vec3(1.0, 0.0, 0.0), (t - 0.5) * 2.0);
}For scientific visualization, perceptually uniform colormaps (like viridis) are preferable, but harder to implement in a shader. The cosine palette is a reasonable compromise.
1.5 Smooth Transitions
Use mix and smoothstep to blend between colors:
// Linear blend
vec3 color = mix(colorA, colorB, t);
// Smooth blend (ease in/out)
vec3 color = mix(colorA, colorB, smoothstep(0.0, 1.0, t));
// Blend based on distance from boundary
float t = smoothstep(-0.05, 0.05, signedDistance);
vec3 color = mix(insideColor, outsideColor, t);This eliminates hard edges and creates anti-aliased boundaries between regions.