acron.shaderlib

GLSL sources shared by the environment modules and by any game’s own shaders.

The sky, clouds and trees (acron.sky, acron.clouds, acron.trees) write their per-frame state once, into one place (acron.sky.FrameUniforms), and every shader that wants to agree with them — a game’s lit shader, a water shader, a post pass — includes the functions here instead of re-deriving them:

  • frame.glsl — frame_value() and the slot numbers of the frame values (FRAME_SUN_DIR …), generated from the live slot layout;

  • atmosphere.glsl — the sky’s LUTs: sky_radiance(dir), key_light_at(world_pos), apply_atmosphere(color, world_pos) (aerial perspective plus the fog: fog.glsl’s analytic height fog, or acron.fog’s froxel volume with FROXEL_FOG), apply_atmosphere_sky and sky_ambient_irradiance(n);

  • local_lights.glsl — acron.fog.LocalLights’ point lights and spotlights: local_light_irradiance(world_pos, n);

  • shadows.glsl — sun_visibility(world_pos, n): the sun’s cascaded shadow map and, once clouds run, their shadow;

  • clouds.glsl, cloud_density.glsl, cloud_fog.glsl, light_shafts.glsl — acron.clouds: the sky’s cloud composite, the density (shared with the cloud passes), apply_clouds(color, world_pos) (geometry in or behind the layer) and apply_light_shafts (crepuscular rays through cloud gaps and trees);

  • common.glsl — constants and small helpers (hashes, remaps, Henyey- Greenstein, the Purkinje shift).

Sources carry no #version line. source() adds it — 450 core where the context offers GLSL 4.5 (the Windows and Linux targets), 410 core otherwise (macOS, which the environment modules support for development only) — followed by defines and, in place, every #include "name" (each file once; later includes of the same file are dropped). Everything here is plain text: assemble_sources() needs no GL, so a test can check the sources resolve, and make() compiles them.

Texture units: a fragment shader may read only GL_MAX_TEXTURE_IMAGE_UNITS samplers — 16 on macOS, and the least GL 4 guarantees anywhere. A program over it fails to link, and Acron then draws the node without any shader (the plain vertex colour): no exception. make() counts the samplers the fragment stage reads (fragment_samplers(): after the defines, through the functions main calls) and warns when a shader goes over MAX_FRAGMENT_SAMPLERS.

Global Functions

assemble_sources(vertex: str, fragment: str, defines: dict | None = None, *, version: int | None = None, search: tuple | list = ()) → tuple[str, str]

The vertex and fragment sources make() would compile (no GL).

check_samplers(fragment_source: str, label: str = 'shader', limit: int | None = None) → list[str]

Warn (RuntimeWarning) when fragment_source reads more samplers than limit (default :data:`MAX_FRAGMENT_SAMPLERS`); returns them.

define_lines(defines: dict | None) → list[str]

#define lines: True defines the bare name, False/None leaves it out, floats keep a decimal point, tuples become vecN(...).

fragment_samplers(source_text: str) → list[str]

The sampler uniforms a (complete, source()-assembled) GLSL source reads: declared under the active defines and named in a function that main reaches (or in a macro such a function uses). An estimate of what the driver counts as active, erring high only where the source is too clever for it; arrays count their length.

glsl_version(gsg=None) → int

The #version to write for gsg (None: the target, 450).

4.5 where the driver offers it, else 4.1 (macOS). Older contexts are refused: the environment passes need core-profile GLSL (texelFetch, float targets, layout qualifiers).

make(vertex: str, fragment: str, defines: dict | None = None, *, gsg=None, search: tuple | list = ())

Compile a shader. gsg picks the version (glsl_version()); Shader.make returns the same object for the same text, so switching an option back and forth does not recompile. Warns when the fragment stage reads more samplers than :data:`MAX_FRAGMENT_SAMPLERS` (check_samplers()).

read(name: str, search: tuple | list = ()) → str

The text of name, looked up in search first, then here, then in the engine’s ShaderLibrary (pbr_tonemap.glsl …).

resolve(text: str, search: tuple | list = (), _seen: set | None = None) → str

text with every #include "name" replaced by that file (each file once) and any #version line dropped.

source(text_or_name: str, defines: dict | None = None, *, version: int | None = None, search: tuple | list = ()) → str

A complete GLSL source: a file name (*.glsl, *.vert, *.frag, looked up as read() does) or literal text, with the version line, defines and its includes resolved.