With the switch, the game draws 0.2 packs ([frame] blend = "linear" is still refused). A renderer made with a 0.2 pack picked draws a float frame where the device can, else the 8-bit one; a pack picked or dropped later runs on the frame there is, logged once. The renderer's material, sky, weather, vertex and shadow entries decode a 0.2 stage's colours and encode its results; scene_color, opaque_color and the water mirror are decoded copies, shadow_color is float, and Frame's sky and fog colours are decoded. The post chain ends in a window-size picture, and the pack's [tonemap] (or Horizon's clip) draws it onto the frame, encoded at alpha 1. With HDR on, HDR output lifts nothing of a 0.2 pack's world. Co-Authored-By: Claude Opus 5.5 <[email protected]>
29 KiB
Shader API reference (0.2)
Generated from crates/horizon-shader-api (HORIZON_WRITE_DOCS=1 cargo test -p horizon-shader-api --test reference
rewrites it; the test fails when it drifts). The API's design and pack format: shaders.md.
Experimental (stability.md): horizon-shader check takes 0.2 packs, the game draws them only
with HORIZON_HDR_FRAME=float set, the registry doesn't list them, and anything here may change in any release.
The design: hdr-linear-light.md.
Colour
Every colour a 0.2 stage reads or returns is linear light: BT.709 (sRGB) primaries, scene-referred, 1.0 is
vanilla's white at paper white, more is brighter. Horizon decodes vanilla's values with a 2.2 power (vanilla's, not
the piecewise sRGB curve), replacing negatives and NaN with 0 first, and encodes a stage's result the same way where
the frame stores gamma ([frame] blend, below).
- Linear:
MaterialInputalbedo,tint,lightmapandcolored_light;VertexInput.tint;SkyInput.color;WeatherInput.color;Framesky_colorandfog_color;block_texture,block_texture_levelandblock_light_color; every colour texture a stage samples (scene_color,previous,history, the pack's targets,opaque_color,reflection,shadow_color). - Data, as in 0.1: depth, normals,
lightlevels,emission, every channel of the LabPBR maps, and the pack's[[textures]]. [[lights]]and[[particle_lights]]colours are written as in 0.1, in vanilla's gamma, and Horizon decodes them:block_light_colorandcolored_lightare linear.block_light_colorsholds them as written; read it throughblock_light_color.- Alpha is coverage only. A post pass's alpha is ignored.
The display
frame.hdr, headroom, paper_white, peak and black_level describe the display. Until HDR output is served for
0.2 packs (a later step), a 0.2 pack sees SDR values with HDR on too: hdr 0, headroom 1, paper_white and peak
80, black_level 0, and its picture is shown at paper white with nothing lifted.
[frame] blend
blend = "gamma" (the default) keeps the frame in vanilla's gamma, values above 1 kept: Horizon encodes the material,
sky and weather stages' results before they are stored and blended, so glass, water, clouds and particles blend as
vanilla's. blend = "linear" stores linear light, so see-through draws blend as light does. A stage sees the same
values either way. "linear" passes the check; the game doesn't serve it yet.
A stage is a function your WGSL defines, with the signature below; Horizon puts the prelude (these structs, the
resources and helpers) before your files and calls the function from its own pass. Your files declare no
bindings and no entry points, and no names starting with horizon_.
Stages
| Stage | In horizon-shaders.toml |
Your function |
|---|---|---|
| sky | [sky] |
fn sky(input: SkyInput) -> vec4<f32> |
| vertex | [vertex] |
fn vertex(input: VertexInput) -> vec3<f32> |
| material | [material] |
fn material(input: MaterialInput) -> Surface |
| weather | [weather] |
fn weather(input: WeatherInput) -> vec4<f32> |
| post | [[post]] |
fn post(input: PostInput) -> vec4<f32> |
| tonemap | [tonemap] |
fn tonemap(input: TonemapInput) -> vec3<f32> |
sky
fn sky(input: SkyInput) -> vec4<f32>
SkyInput
One direction of the sky, for fn sky(input: SkyInput) -> vec4<f32>. The sun, moon, stars and clouds are drawn over the result.
| Field | Type | Meaning |
|---|---|---|
direction |
vec3<f32> |
Unit view ray, camera-relative world axes. |
color |
vec4<f32> |
Vanilla's sky colour along the ray (sky, fog and sunrise). |
Resources (bind group 3):
| Binding | Declaration | Meaning |
|---|---|---|
| 0 | var<uniform> frame: Frame |
The frame (Frame). |
vertex
fn vertex(input: VertexInput) -> vec3<f32>
VertexInput
One corner of a block's surface, for fn vertex(input: VertexInput) -> vec3<f32>, which returns where it is drawn (camera-relative, in blocks; input.position where it stays).
| Field | Type | Meaning |
|---|---|---|
position |
vec3<f32> |
The corner, camera-relative, in blocks (+ frame.camera_position for the world's). |
uv |
vec2<f32> |
The texture coordinate inside the block's sprite. |
material |
u32 |
The [[materials]] id of the block's sprite (0 when none matches). |
light |
vec2<f32> |
Block and sky light, 0..1. |
tint |
vec4<f32> |
The vertex colour (biome tint, face shade, ambient occlusion); alpha 1. |
face |
u32 |
For a fluid, the face of the block the corner is on (as MaterialInput.face), else 0. |
fluid |
u32 |
1 on a fluid's surface (water or lava), else 0. |
Resources (bind group 3):
| Binding | Declaration | Meaning |
|---|---|---|
| 0 | var<uniform> frame: Frame |
The frame (Frame). |
Helpers:
fn shadow_position(position: vec3<f32>) -> vec3<f32>: where the camera-relative pointpositionis stored in the shadow map, fortextureSampleCompareLevel.fn shadow_distort(xy: vec2<f32>) -> vec2<f32>: the map's distortion of a point at shadow clipxy.
material
fn material(input: MaterialInput) -> Surface
MaterialInput
One point of a block's surface, for fn material(input: MaterialInput) -> Surface.
| Field | Type | Meaning |
|---|---|---|
albedo |
vec4<f32> |
The block texture's colour here (alpha 0..1). |
tint |
vec4<f32> |
The vertex colour: biome tint, face shade and ambient occlusion. |
light |
vec2<f32> |
Block and sky light, 0..1, smoothed over the face. |
lightmap |
vec3<f32> |
Vanilla's light colour for that light this frame. |
position |
vec3<f32> |
The point, camera-relative, in blocks. |
uv |
vec2<f32> |
The texture coordinate inside the block atlas. |
translucent |
bool |
Drawn in the translucent pass (water, stained glass, ice). |
tags |
u32 |
The pack's sprite tags this surface's sprite has: bit i for the manifest's i-th [[tags]]. |
material |
u32 |
The [[materials]] id of this surface's sprite (0 when no entry names it). |
screen |
vec2<f32> |
Where the point is on the screen, 0..1 from the top left (opaque_depth and opaque_color at screen * frame.resolution). |
face |
u32 |
For a fluid, which face of the block the point is on, Direction order: 0 down, 1 up, 2 north, 3 south, 4 west, 5 east. 0 elsewhere. |
fluid |
u32 |
0 not a fluid, 1 water, 2 lava. |
emission |
f32 |
How strongly the block itself shines, 0..1, by its kind of light: a redstone torch or a candle 7/15, a torch or lava 14/15, glowstone 1; 0 for most blocks. |
normal |
vec3<f32> |
The surface's geometric normal, a unit vector in world axes, facing the camera. A normal within about 3.6 degrees of an axis is that axis exactly, so distant block faces do not shimmer; flowing water's slopes are steeper and keep theirs. |
tangent |
vec4<f32> |
The surface's tangent: xyz the unit direction in world axes along which uv.x grows, w the sign (1 or -1) that makes cross(normal, tangent.xyz) * tangent.w the direction uv.y grows along (down the texture). With normal it turns a normal map's normals into world axes. |
colored_light |
vec3<f32> |
The frame's coloured lights at this point ([[particle_lights]], held and dropped items in their [[lights]] colour while dynamic lights are on, and mods' visual lights), 0..1, to light the surface with as the pack likes; 0 where none reaches. |
displaced |
f32 |
How much of the relief [displacement] gave this point as geometry, 0..1: 1 near the camera, where the surface already lies where displacement put it (position is that point; a parallax effect would count its depth twice), easing to 0 at [displacement] distance; always 0 without [displacement]. |
Surface
What material returns. Vanilla's own is albedo * tint * vec4(lightmap, 1.0); Horizon then applies the alpha test (cutout and translucent) and fog.
| Field | Type | Meaning |
|---|---|---|
color |
vec4<f32> |
The lit colour, linear light (more than 1 is brighter than white), with its alpha. |
Under [displacement] the stage also defines fn displacement(input: DisplacementInput) -> f32, called for each corner of the grid the faces near the camera are cut into; the corner moves along the face's normal by the depth it gives at the point's height less the depth at the texture's mean height, pinned at the face's edges and easing out over the last quarter of the distance.
DisplacementInput
One corner of the grid a block face near the camera is cut into, for fn displacement(input: DisplacementInput) -> f32 ([displacement]), which returns how deep below the face, in blocks, the surface lies at height.
| Field | Type | Meaning |
|---|---|---|
position |
vec3<f32> |
The corner on the flat face, camera-relative, in blocks. |
normal |
vec3<f32> |
The face's normal, a unit vector in world axes. |
uv |
vec2<f32> |
The texture coordinate inside the block's sprite (as MaterialInput.uv). |
material |
u32 |
The [[materials]] id of the block texture (0 when none matches). |
height |
f32 |
A LabPBR height, 0..1 (1 the top of the relief): the normal map's alpha here, or the texture's average height (what the face's edges are pinned to). |
Resources (bind group 3):
| Binding | Declaration | Meaning |
|---|---|---|
| 0 | var<uniform> frame: Frame |
The frame (Frame). |
| 1 | var reflection: texture_2d<f32> |
The water mirror: the world as a camera mirrored about the horizontal water plane around the camera (frame.water_plane) sees it, at half resolution, mapped like the world's picture, so water samples it at input.screen. Solid terrain, cut away below the plane, over the vanilla sky gradient: not the pack's sky stage, nor the sun, moon and stars. Drawn only for a pack whose material reads it, while frame.has_reflection is 1; else a 1×1 blank. |
| 2 | var reflection_sampler: sampler |
Bilinear filtering, clamped to the edges. |
| 3 | var shadow_map: texture_depth_2d |
This frame's shadow map: the depth of the solid terrain and entities the light sees, 0 at the light, 1 away from it (and where nothing is), stored at shadow_position's mapping. Valid while frame.has_shadow is 1 (the pack asks for it with [shadow]), else a 1×1 map that lights everything. |
| 4 | var shadow_sampler: sampler_comparison |
Depth comparison, bilinear (2×2 percentage-closer filtering): textureSampleCompareLevel(shadow_map, shadow_sampler, p.xy, p.z) is 1 where the point is lit, 0 in shadow. |
| 7 | var shadow_map_translucent: texture_depth_2d |
shadow_map with the translucent terrain (water, stained glass, ice) the light passes through drawn in too, read with shadow_sampler the same way: a point lit in shadow_map but not here lies under such a surface, which shadow_color gives the colour of. |
| 8 | var shadow_color: texture_2d<f32> |
The texture colour times the vertex colour (rgb: the tint, the face's directional shade and ambient occlusion) of the nearest translucent surface the light passes through at each texel of shadow_map_translucent, white where there is none; alpha is 1 on a fluid (water) and the texture's alpha, at most 0.99, on anything else. Read it with textureLoad at shadow_position's xy times textureDimensions(shadow_color). |
| 6 | var opaque_depth: texture_depth_2d |
The world's depth before translucent terrain (reversed, like scene_depth), for the translucent pass: textureLoad(opaque_depth, vec2<i32>(input.screen * frame.resolution), 0) is what lies behind water or glass. Stale while solid terrain draws. |
| 9 | var block_light_colors: texture_3d<f32> |
The colour of the block light around the camera, from the [[lights]] of the light sources nearby: rgb their colours averaged by how near each is, over 4; a how strongly they reach the cell. Read it through block_light_color. |
| 10 | var<uniform> block_light_grid: vec4<f32> |
Where block_light_colors lies: xyz its corner, camera-relative, w the side of one of its cells in blocks. |
| 11 | var opaque_color: texture_2d<f32> |
The world's picture before translucent terrain (sky, solid terrain, entities), the size of opaque_depth, for the translucent pass: what a surface refracts. Read it with textureLoad. Stale while solid terrain draws; black when the world draws straight to the window (a pack without [[post]] passes, at full resolution). |
Helpers:
fn block_texture(uv: vec2<f32>) -> vec4<f32>: the surface's block texture atuv, anywhere on the sprite, without the tint.fn block_texture_level(uv: vec2<f32>, lod: f32) -> vec4<f32>: the same at mip levellod.fn block_texture_size() -> vec2<f32>: its size in texels.fn block_texture_lod() -> f32: the mip levelblock_texturereads at this point.fn block_pbr_maps() -> bool: whether the resource packs ship LabPBR maps (_n,_s).fn block_normal_map(uv: vec2<f32>) -> vec4<f32>: the surface's LabPBR normal map atuv(rg the normal alonginput.tangentand down the texture, b ambient occlusion, a height); (0.5, 0.5, 1, 1) without one.fn block_specular_map(uv: vec2<f32>) -> vec4<f32>: its LabPBR specular map (r smoothness, g F0 or a metal, b porosity or subsurface, a emission, 1 none); (0, 0, 0, 0) without one.fn block_normal_map_level(uv: vec2<f32>, lod: f32) -> vec4<f32>,fn block_specular_map_level(uv: vec2<f32>, lod: f32) -> vec4<f32>: the same at the block texture's mip levellod.fn block_pbr_size() -> vec2<f32>: the LabPBR maps' size in texels.fn set_surface(normal: vec3<f32>, smoothness: f32, data: f32): what the post stage'ssurface_atreads for this point of solid terrain (datathe pack's own; the face's normal and 0, 0 unset).fn shadow_position(position: vec3<f32>) -> vec3<f32>: where the camera-relative pointpositionis stored in the shadow map, fortextureSampleCompareLevel.fn shadow_distort(xy: vec2<f32>) -> vec2<f32>: the map's distortion of a point at shadow clipxy.
weather
fn weather(input: WeatherInput) -> vec4<f32>
WeatherInput
One point of a rain or snow streak, for fn weather(input: WeatherInput) -> vec4<f32>, which returns the colour blended over the world (alpha its opacity).
| Field | Type | Meaning |
|---|---|---|
color |
vec4<f32> |
The rain or snow texture's colour here times the streak's vertex colour (alpha 0.1..1). |
light |
vec2<f32> |
Block and sky light, 0..1. |
position |
vec3<f32> |
The point, camera-relative, in blocks. |
snow |
bool |
A snow streak (rain otherwise). |
Resources (bind group 3):
| Binding | Declaration | Meaning |
|---|---|---|
| 0 | var<uniform> frame: Frame |
The frame (Frame). |
Helpers:
fn shadow_position(position: vec3<f32>) -> vec3<f32>: where the camera-relative pointpositionis stored in the shadow map, fortextureSampleCompareLevel.fn shadow_distort(xy: vec2<f32>) -> vec2<f32>: the map's distortion of a point at shadow clipxy.
post
fn post(input: PostInput) -> vec4<f32>
PostInput
One pixel of the picture, for fn post(input: PostInput) -> vec4<f32>.
| Field | Type | Meaning |
|---|---|---|
uv |
vec2<f32> |
Where the pixel is, 0..1 from the top left. |
pixel |
vec2<f32> |
The pixel's centre, in pixels from the top left of what the pass draws (the world's size; the window's for the last pass). |
color |
vec4<f32> |
The previous pass's colour here (the world's picture for the first pass). |
depth |
f32 |
The world's depth here, reversed: 0 is the sky. |
Resources (bind group 3):
| Binding | Declaration | Meaning |
|---|---|---|
| 0 | var<uniform> frame: Frame |
The frame (Frame). |
| 1 | var scene_color: texture_2d<f32> |
The world's picture before any pass. |
| 2 | var scene_depth: texture_depth_2d |
The world's depth, reversed (1 near, 0 far and sky). |
| 3 | var previous: texture_2d<f32> |
The previous pass's output (scene_color for the first pass). |
| 4 | var linear_sampler: sampler |
Bilinear filtering, clamped to the edges. |
| 5 | var nearest_sampler: sampler |
Nearest texel, clamped to the edges. |
| 6 | var shadow_map: texture_depth_2d |
This frame's shadow map: the depth of the solid terrain and entities the light sees, 0 at the light, 1 away from it (and where nothing is), stored at shadow_position's mapping. Valid while frame.has_shadow is 1 (the pack asks for it with [shadow]), else a 1×1 map that lights everything. |
| 7 | var shadow_sampler: sampler_comparison |
Depth comparison, bilinear (2×2 percentage-closer filtering): textureSampleCompareLevel(shadow_map, shadow_sampler, p.xy, p.z) is 1 where the point is lit, 0 in shadow. |
| 8 | var history: texture_2d<f32> |
Last frame's input to the last pass (what the pass before it wrote), for temporal passes (TAA, denoising): put such a pass second to last and it reads its own previous output; reproject with frame.previous_view_projection. Black on the first frame, after a gap of over half a second, and with a single pass. |
| 9 | var shadow_map_translucent: texture_depth_2d |
shadow_map with the translucent terrain (water, stained glass, ice) the light passes through drawn in too, read with shadow_sampler the same way: a point lit in shadow_map but not here lies under such a surface, which shadow_color gives the colour of. |
| 10 | var shadow_color: texture_2d<f32> |
The texture colour times the vertex colour (rgb: the tint, the face's directional shade and ambient occlusion) of the nearest translucent surface the light passes through at each texel of shadow_map_translucent, white where there is none; alpha is 1 on a fluid (water) and the texture's alpha, at most 0.99, on anything else. Read it with textureLoad at shadow_position's xy times textureDimensions(shadow_color). |
| 11 | var scene_surface: texture_2d<u32> |
What the material stage's set_surface stored for the solid terrain at each pixel, unfiltered (textureLoad; its texels are the bits of floats, so bitcast<vec4<f32>> them): read it through the prelude's surface_at, which knows its layout and where it no longer shows. Blank without a material stage that calls set_surface. |
Helpers:
fn depth_at(uv: vec2<f32>) -> f32: the world's depth atuv.fn view_position(uv: vec2<f32>, depth: f32) -> vec3<f32>: the camera-relative position, in blocks, of the point atuvanddepth.fn surface_at(texel: vec2<i32>) -> SurfaceData: the solid terrain's surface at a pixel, as the material stage'sset_surfaceleft it:present(false where the sky, an entity, water, glass or a particle shows instead),normal(world axes),smoothnessanddata.fn shadow_position(position: vec3<f32>) -> vec3<f32>: where the camera-relative pointpositionis stored in the shadow map, fortextureSampleCompareLevel.fn shadow_distort(xy: vec2<f32>) -> vec2<f32>: the map's distortion of a point at shadow clipxy.
A [[post]] entry with output = "<name>" writes a target of the pack's own, at its scale of the
world's size (1, 0.5 or 0.25), instead of the next pass's previous; the prelude of every pass declares
var <name>: texture_2d<f32> (group 3 from binding 32), which holds last frame's until written again (a
pass reads a blank one in place of the target it writes).
tonemap
fn tonemap(input: TonemapInput) -> vec3<f32>
TonemapInput
One pixel of the window, for fn tonemap(input: TonemapInput) -> vec3<f32>, which returns its colour for the display: linear light, 1.0 at paper white, from 0 to frame.headroom (Horizon clamps it there).
| Field | Type | Meaning |
|---|---|---|
uv |
vec2<f32> |
Where the pixel is, 0..1 from the top left. |
pixel |
vec2<f32> |
The pixel's centre, in pixels from the top left of the window. |
color |
vec3<f32> |
The post chain's picture here (the world's without one), linear light, never negative or NaN. |
Resources (bind group 3):
| Binding | Declaration | Meaning |
|---|---|---|
| 0 | var<uniform> frame: Frame |
The frame (Frame). |
| 3 | var previous: texture_2d<f32> |
The post chain's picture at the window's size (without a post chain, the world's at the world's size), linear light: what input.color samples, for a curve that reads around the pixel. |
| 4 | var linear_sampler: sampler |
Bilinear filtering, clamped to the edges. |
| 5 | var nearest_sampler: sampler |
Nearest texel, clamped to the edges. |
It runs once per pixel at the window's size, after the post chain and before the camera overlays and
the HUD. A pack without it gets Horizon's: a clip at white in SDR, a roll-off into the headroom in HDR.
Horizon holds the result to 0 to frame.headroom.
The frame
Frame
The frame, as frame in every stage. Positions and directions are in world axes relative to the camera (x east, y up, z south), so they stay precise far from the world's origin; depth is reversed (1 near, 0 at the far plane and in the sky).
| Field | Type | Offset | Meaning |
|---|---|---|---|
view |
mat4x4<f32> |
0 | Camera-relative world to view space: the camera's rotation. |
projection |
mat4x4<f32> |
64 | View to clip space (reversed depth, wgpu's clip space: z 0..1). |
view_inverse |
mat4x4<f32> |
128 | View to camera-relative world space. |
projection_inverse |
mat4x4<f32> |
192 | Clip to view space. |
camera_position |
vec3<f32> |
256 | The camera's world position, in blocks (about a millimetre of precision 10 000 blocks out). |
time |
f32 |
268 | Seconds since the pack was loaded, wrapping to 0 every hour. |
sun_direction |
vec3<f32> |
272 | Unit vector toward the sun (below the horizon at night). |
day_time |
f32 |
284 | The time of day, 0..1: 0 sunrise, 0.25 noon, 0.5 sunset, 0.75 midnight. |
moon_direction |
vec3<f32> |
288 | Unit vector toward the moon. |
game_time |
f32 |
300 | Vanilla's GameTime: (game time % 24000 + partial tick) / 24000, 0..1. |
sky_color |
vec3<f32> |
304 | Vanilla's sky colour this frame, 0..1. |
rain |
f32 |
316 | Rain strength, 0..1. |
fog_color |
vec3<f32> |
320 | Vanilla's fog colour this frame (also the clear colour), 0..1. |
thunder |
f32 |
332 | Thunder strength, 0..1. |
resolution |
vec2<f32> |
336 | The size of the world's picture, in pixels (the resolution scale applied). |
eye_light |
vec2<f32> |
344 | Block and sky light at the camera, 0..1 (level / 15). |
fog_start |
f32 |
352 | Where vanilla's fog starts, in blocks from the camera. |
fog_end |
f32 |
356 | Where vanilla's fog is full, in blocks. |
render_distance |
f32 |
360 | The render distance, in blocks. |
far |
f32 |
364 | The far plane, in blocks. |
night_vision |
f32 |
368 | Night vision strength, 0..1. |
blindness |
f32 |
372 | Blindness strength, 0..1. |
darkness |
f32 |
376 | The darkness effect's strength, 0..1. |
brightness |
f32 |
380 | The Brightness option, 0..1 (Moody to Bright). |
eye_in |
u32 |
384 | What the camera is in: 0 air, 1 water, 2 lava, 3 powder snow. |
dimension |
i32 |
388 | 0 the Overworld, -1 the Nether, 1 the End, 2 any other dimension. |
frame_index |
u32 |
392 | Frames drawn since the pack was loaded (wraps). |
hide_gui |
u32 |
396 | 1 while F1 hides the HUD, else 0. |
precipitation |
u32 |
400 | What falls in the biome at the camera while it rains: 0 nothing, 1 rain, 2 snow. |
temperature |
f32 |
404 | The biome at the camera's temperature (vanilla's, -0.7..2.0; it snows below 0.15). |
downfall |
f32 |
408 | The biome at the camera's downfall, 0..1. |
wetness |
f32 |
412 | rain, smoothed: rises over about 30 seconds and dries over about 10, 0..1. |
previous_view_projection |
mat4x4<f32> |
416 | Last frame's camera-relative world to clip space (its projection * view); a point at camera-relative p now was at p + camera_motion relative to last frame's camera. This frame's on the first frame and after a gap of over half a second (a pause, a world load), when last frame's camera says nothing about this one. |
camera_motion |
vec3<f32> |
480 | How far the camera moved since last frame, in blocks (0 after such a gap). |
delta_time |
f32 |
492 | Seconds since last frame (0 on the first and after such a gap). |
eye_light_smooth |
vec2<f32> |
496 | eye_light eased with a half-life of 2 seconds: it follows the light levels' steps without stepping itself (for exposure and indoor fog). |
in_water |
f32 |
504 | 1 while the camera is in water (eye_in 1), eased with a half-life of a quarter second in and out. |
in_lava |
f32 |
508 | The same for lava (eye_in 2). |
water_plane |
f32 |
512 | The y of the horizontal water plane reflection mirrors the world about, in world blocks, while has_reflection is 1. |
has_reflection |
u32 |
516 | 1 while the material stage's reflection holds this frame's water mirror, else 0. |
reflection_resolution |
vec2<f32> |
520 | The size of reflection, in pixels: half the world's picture. |
mods |
array<vec4<f32>, 8> |
528 | Eight values a native mod hands its own pack each frame (native_shader_params, docs/native-mods.md): what they mean is that mod's, such as where and when something happened in the world. All 0 when no mod sets them. |
light_direction |
vec3<f32> |
656 | Unit vector toward the body casting this frame's shadows: the sun by day, the moon by night. |
has_shadow |
u32 |
668 | 1 while shadow_map holds this frame's shadow map, else 0. |
shadow_view_proj |
mat4x4<f32> |
672 | Camera-relative world to the shadow camera's clip space, before the distortion (orthographic, looking along -light_direction; z 0 at the light, 1 away from it). |
shadow_distance |
f32 |
736 | Half the side of the square the shadow map covers, in blocks (the manifest's [shadow] distance); 0 without a shadow map. |
shadow_distortion |
f32 |
740 | How strongly the shadow map is distorted toward its centre, 0..1: a point at shadow clip xy is stored at xy / (length(xy) * k + 1 - k), so texels near the camera cover less ground (shadow_position applies it). |
shadow_resolution |
f32 |
744 | The shadow map's side, in texels; 0 without a shadow map. |
shadow_center_texel |
f32 |
748 | The ground one shadow texel covers at the map's centre, in blocks (for a bias that scales with it); 0 without a shadow map. |
hdr |
u32 |
752 | 1 while the output is HDR, else 0. |
headroom |
f32 |
756 | The peak over paper white: the brightest value the display shows (Apple's EDR headroom, Android's HDR/SDR ratio). 1 in SDR. It changes while the game runs (the system's SDR white, a calibration, another display), so read it every frame. |
paper_white |
f32 |
760 | Paper white in nits; a nominal 80 in SDR. |
peak |
f32 |
764 | The peak brightness in nits; a nominal 80 in SDR, as paper_white. |
black_level |
f32 |
768 | The display's black in nits (HDR's Black Level); 0 in SDR. |
Anti-aliasing
While a pack's [[post]] chain draws the frame, the player's anti-aliasing settings do not run: taa, fxaa
and upscaling in horizon-graphics.txt have no effect, and the frame's projection is never jittered, because the
last post pass draws the picture itself and Horizon's own resolve, which does TAA, FXAA and TAA upscaling, is the
step it replaces. The world is still rendered at resolutionScale before the pack reads it.
A pack that wants smooth edges draws them itself, in a post pass: a spatial filter (FXAA, SMAA and the like) works on
previous as it is. A temporal pass cannot do what TAA does here, even though history and
frame.previous_view_projection give it the last frame: nothing moves the camera by a fraction of a texel from one
frame to the next, so a still camera gives it the same picture twice and there is no sub-pixel detail to gather.
Without a pack the settings apply as the player set them.
Options
Each [[options]] entry sets the WGSL override of the same name when the pipelines are built:
type |
Keys | The override |
|---|---|---|
bool |
default |
override <id>: bool |
float |
default, min, max, step |
override <id>: f32 |
int |
default, min, max |
override <id>: i32 |
choice |
values, default (one of them) |
override <id>: u32, the value's index |
Limits
- 12 post passes, 64 options,
budget.gpu_msup to 16. - 256 KiB per WGSL file, 8 MiB per pack.
- Per stage: 20000 naga expressions over all its functions, 64 texture reads (samples, gathers and loads, the prelude's included).
The check
A pack runs only after it passes horizon_shader_api::check (the game when it loads a pack, the launcher before it
installs one, the registry's CI): each stage is parsed and validated by naga, checked against its signature and
the rules above, and translated for Vulkan (SPIR-V), Metal and DirectX 12 (HLSL) with its options at their
defaults, minimums and maximums. Every option must be an override of its type in some stage; an override
that is no option needs a default.