simvx.core.colour¶
Backend-agnostic sRGB colour-space conversion helpers.
The exact piecewise sRGB electro-optical transfer function (EOTF) and its inverse, in a numpy form for buffers and a cached scalar form for the 2D vertex path. They live in core (no rendering deps) so the Vulkan desktop backend and the WebGPU web runtime decode constant material colours identically, matching the hardware sRGB texture decode bit-for-bit.
This module is the single home of the EOTF, and that is the point: colour crosses from author space to GPU space at a lot of seams, and a seam that hand-rolls its own decode (or forgets one) is only findable if every other seam is greppable from here.
Use these for COLOUR inputs (albedo / base-colour, emissive). Data maps (normal, metallic-roughness, ambient occlusion) carry no colour and must stay linear.
The piecewise curve (not the pow(2.2) approximation) is required so a flat
constant colour matches a same-valued sRGB-decoded texture sample exactly.
Module Contents¶
Functions¶
Decode sRGB-encoded values in [0, 1] to linear light (exact piecewise EOTF). |
|
Decode one sRGB-encoded colour channel to linear light. |
|
Encode linear-light values in [0, 1] to sRGB (exact piecewise inverse EOTF). |
|
Decode the RGB channels of an (R, G, B[, A]) tuple, leaving any 4th component as-is. |
Data¶
API¶
- simvx.core.colour.__all__¶
[‘srgb_to_linear’, ‘srgb_channel_to_linear’, ‘linear_to_srgb’, ‘srgb_to_linear_rgb’]
- simvx.core.colour.srgb_to_linear(c: numpy.ndarray | float) numpy.ndarray | float[source]¶
Decode sRGB-encoded values in [0, 1] to linear light (exact piecewise EOTF).
Scalar in / scalar out, or numpy array in / numpy array out (element-wise).
- simvx.core.colour.srgb_channel_to_linear(c: float) float[source]¶
Decode one sRGB-encoded colour channel to linear light.
The same curve :func:
srgb_to_linearimplements, scalar and cached because the 2D drawing surfaces decode the same handful of palette values every frame and going through numpy per channel is not worth it. Values above 1.0 (HDR) follow the extended curve; values at or below the 0.04045 knee use the linear segment, which also covers negatives.2D vertex colours are authored in sRGB space (
Colour.hexcomponents), but everything downstream of the vertex buffer works in linear light: the shaders pass vertex colour through, blending happens in linear, and the sRGB render target encodes on write. Uploading the authored value verbatim therefore encodes an already-encoded colour, which is exactly one sRGB encode too bright. Decoding here makes a solid fill of a given hex match a textured sprite of that hex, whose sRGB texture the sampler decodes in hardware.
- simvx.core.colour.linear_to_srgb(c: numpy.ndarray | float) numpy.ndarray | float[source]¶
Encode linear-light values in [0, 1] to sRGB (exact piecewise inverse EOTF).
- simvx.core.colour.srgb_to_linear_rgb(colour: tuple[float, ...]) tuple[float, ...][source]¶
Decode the RGB channels of an (R, G, B[, A]) tuple, leaving any 4th component as-is.
The 4th component is alpha (linear) for albedo or emissive intensity for emissive packing: neither is a colour and both pass through untouched.