Thermal Vision False-Color Palette Mapping in GLSL
Thermal vision systems visualize temperature differences by mapping single-channel scalar values into a dynamic spectrum of colors known as a false-color palette. This article examines how to implement thermal vision false-color palette mapping in OpenGL Shading Language (GLSL), detailing the mechanics of converting raw thermal intensity into color coordinates through both texture-based gradient lookups and procedural interpolation techniques.
Data Normalization
Thermal mapping begins by converting input thermal readings—whether
sourced from a radiometric sensor texture or calculated from scene
luminance—into a normalized scalar value \(t\) ranging strictly from 0.0
to 1.0.
In a GLSL fragment shader, this normalization maps the minimum
detectable temperature to 0.0 (typically represented by
cool hues like deep blue or black) and the maximum detectable
temperature to 1.0 (typically hot hues like bright yellow
or white). Clamping is necessary to ensure out-of-bounds artifacts do
not occur during lookup evaluation:
float normalizedTemp = clamp((rawTemp - minTemp) / (maxTemp - minTemp), 0.0, 1.0);Gradient Evaluation Using 1D Texture Lookups
The most efficient and flexible method for thermal false-color
evaluation uses a precomputed Look-Up Table (LUT) stored as a 1D texture
(sampler1D) or a narrow 2D texture
(sampler2D).
The thermal palette (such as Ironbow, Rainbow, or White Hot) is encoded horizontally across the texture. When evaluating the pixel color, the normalized scalar acts directly as the \(u\)-coordinate for sampling:
uniform sampler2D u_paletteTexture;
in vec2 v_texCoord;
out vec4 fragColor;
void main() {
float temp = texture(u_thermalSource, v_texCoord).r;
float normalizedTemp = clamp(temp, 0.0, 1.0);
// Sample the 1D gradient stored in a 2D strip
vec4 thermalColor = texture(u_paletteTexture, vec2(normalizedTemp, 0.5));
fragColor = thermalColor;
}Hardware configuration plays a crucial role in gradient evaluation:
- Filtering: Setting
GL_TEXTURE_MIN_FILTERandGL_TEXTURE_MAG_FILTERtoGL_LINEARallows the GPU's fixed-function hardware to automatically interpolate between palette colors, providing smooth transitions across scalar steps without extra shader computations. - Clamping: Setting
GL_TEXTURE_WRAP_StoGL_CLAMP_TO_EDGEprevents color bleeding between the extreme hot and cold ends of the gradient.
Procedural Gradient Evaluation
When external texture samplers are undesirable due to memory bandwidth limits or binding constraints, gradients can be evaluated mathematically directly within the fragment shader.
A common thermal mapping standard is the multi-stop "Ironbow"
gradient, which passes sequentially through black, blue/purple, red,
orange, yellow, and white. This is evaluated using piecewise linear
interpolation via GLSL's mix() and
smoothstep() or clamp():
vec3 evaluateThermalGradient(float t) {
// Define color stops
const vec3 c0 = vec3(0.0, 0.0, 0.0); // Black
const vec3 c1 = vec3(0.19, 0.04, 0.56); // Dark Blue/Purple
const vec3 c2 = vec3(0.78, 0.17, 0.28); // Red
const vec3 c3 = vec3(0.98, 0.65, 0.05); // Orange/Yellow
const vec3 c4 = vec3(1.0, 1.0, 1.0); // White
// Piecewise segment selection and interpolation
if (t < 0.25) {
return mix(c0, c1, t / 0.25);
} else if (t < 0.5) {
return mix(c1, c2, (t - 0.25) / 0.25);
} else if (t < 0.75) {
return mix(c2, c3, (t - 0.5) / 0.25);
} else {
return mix(c3, c4, (t - 0.75) / 0.25);
}
}Branches can be eliminated for improved GPU vector pipeline execution by defining continuous polynomial approximations or calculating step factors via vector arithmetic:
vec3 evaluateContinuousHeat(float t) {
// Procedural heat approximation without branching
vec3 color;
color.r = clamp((t - 0.25) * 4.0, 0.0, 1.0);
color.g = clamp((t - 0.5) * 4.0, 0.0, 1.0);
color.b = clamp((t - 0.75) * 4.0, 0.0, 1.0) + clamp(1.0 - t * 4.0, 0.0, 1.0);
return color;
}Architectural Trade-offs
Texture-based gradient evaluation is preferred when supporting user-customizable palettes, as swapping gradients only requires binding a different texture without recompiling GLSL programs. It offloads color blending to dedicated texture-sampling units.
Procedural gradient evaluation is ideal for self-contained post-processing passes where texture units are limited or high register availability favors arithmetic computation over memory latency.