How GLSL packSnorm4x8 Converts Floats to uint
The packSnorm4x8 function in GLSL converts a
four-component floating-point vector (vec4) with values
normalized between -1.0 and 1.0 into an unpacked 32-bit unsigned integer
(uint). It achieves this by clamping each float to the
valid range, scaling the values by 127.0, rounding them to the nearest
integer, and packing each resulting 8-bit signed integer into
consecutive byte positions within a single 32-bit scalar.
1. Clamping the Input Values
Before conversion, each component of the input vector
v = (v.x, v.y, v.z, v.w) is clamped to the range \([-1.0, 1.0]\). This ensures that values
outside the normalized range do not overflow the 8-bit signed integer
format.
\[\text{clamped}_i = \min(\max(v_i, -1.0), 1.0)\]
2. Scaling and Rounding to 8-Bit Integers
The clamped floating-point values are multiplied by \(127.0\) and rounded to the nearest integer:
\[i_k = \text{round}(\text{clamped}_k \times 127.0)\]
The multiplier \(127.0\) is used
because standard signed normalized (snorm) representations
in OpenGL map the range \([-1.0, 1.0]\)
symmetrically to \([-127, 127]\). The
minimum value of an 8-bit two's-complement integer, \(-128\), is intentionally unused to preserve
symmetry around zero.
- \(1.0\) scales to \(+127\) (
0x7F) - \(0.0\) scales to \(0\) (
0x00) - \(-1.0\) scales to \(-127\) (
0x81in two's complement)
3. Bitwise Packing into 32 Bits
Each resulting 8-bit signed value is interpreted as an unsigned 8-bit
byte (0x00 to 0xFF). These four bytes are
positioned into a single 32-bit integer according to their vector
component order:
- Bits 0–7: The
xcomponent (byte 0) - Bits 8–15: The
ycomponent (byte 1) - Bits 16–23: The
zcomponent (byte 2) - Bits 24–31: The
wcomponent (byte 3)
Equivalent Mathematical Logic
The entire operation performs the equivalent of the following GLSL logic:
uint packSnorm4x8_equivalent(vec4 v) {
ivec4 iv = ivec4(round(clamp(v, -1.0, 1.0) * 127.0));
uvec4 uv = uvec4(iv) & 0xFFu;
return uv.x | (uv.y << 8u) | (uv.z << 16u) | (uv.w << 24u);
}The resulting uint contains the packed signed normalized
data ready for memory-efficient storage, buffer packing, or transmission
across pipeline stages.