sky_pattern¶
type: "sky_pattern"
Paints a figure (or a texture) on the sky dome. It is the sky sibling of
surface_pattern - the same structural pattern block, shape library and
texture addressing - but it only ever touches far-depth sky pixels, so it can never paint over
terrain, the first-person hand or the GUI. It renders on all supported lines (Fabric and NeoForge)
and must run at screen_layer: 0, the layer where the scene depth buffer is intact.
anchor - follow the sun, the moon or the stars¶
By default the pattern sits at the world-fixed dome position you author with
anchor_yaw/anchor_pitch. The optional top-level anchor field makes it follow a vanilla sky
body instead:
anchor |
The pattern follows | How |
|---|---|---|
(absent) / "dome" |
nothing - the literal anchor_yaw/anchor_pitch (default) |
today's behaviour, unchanged |
"sun" |
the vanilla sun | anchor_yaw/anchor_pitch are written from the sun's live dome direction |
"moon" |
the vanilla moon | anchor_yaw/anchor_pitch are written from the moon's live dome direction |
"stars" |
the rotating star sphere | the sampled direction is taken into the star sphere's own frame (star_angle is the live star angle), so the pattern is a real lock, not an axis approximation |
The sun/moon angles are read from the client's own sky render state and converted to the same
[yaw, pitch] degrees the anchor_yaw/anchor_pitch params use, entirely on the CPU; the sun
and moon therefore keep writing only the three existing uniforms. The stars anchor needs one small
extra step: vanilla draws the star sphere with the pose Ry(-90°) · Rx(starAngle) (verified with
javap in SkyRenderer.renderSunMoonAndStars on 26.2, 26.1.2 and 1.21.11), and the shader takes the
sampled direction into that pose's local frame with the exact inverse, so the pattern is rigidly
attached to the star material as the field rotates. This is the only shader path the anchor adds,
and it is gated on the stars anchor - sun/moon/dome do not touch it. anchor is structural (a
string, like pattern/sky_mode), so it cannot be keyframed; an unknown value is a parse error
naming the accepted ones, and anchor on a non-sky_pattern type is also a parse error.
Why it is Iris-safe. A shaderpack may draw the sky (and the sun/moon/stars) itself, but the
world time - and therefore the body angles - is still computed by the game. Because the anchor is
resolved on the CPU from that state and only writes the existing uniforms (plus the two internal
stars fields), the pattern tracks the same body the pack shows. Like every post effect it composes
over the composited frame, so it is an overlay.
Fail-closed. If the sky state cannot be read (no overworld sky - the End or a sky-less dimension - or the render state is not ready this frame), the effect contributes nothing rather than painting at a guessed spot, and warns once through the logger. It never falls back to the literal anchor, which could be wrong.
{
"type": "sky_pattern",
"duration": -1, "persistent": true, "loop": true,
"anchor": "sun",
"params": { "screen_layer": 0, "sky_mode": "patch", "tile_scale": 0.55, "opacity": 0.9,
"color_r": 1.0, "color_g": 0.85, "color_b": 0.35 },
"pattern": { "figure": "circle", "fill": "stroke", "radius": 0.42, "stroke_width": 0.05, "softness": 0.02 }
}
Honest limits.
- Overlay, not replacement. The vanilla sun/moon are still drawn underneath, so size the pattern
to cover them: the vanilla sun is ≈33° apparent width, the moon ≈24°. To have the vanilla body
cover your effect instead, you would need an effect drawn under the celestial draws - a
SkyRenderermixin, deliberately not implemented here (below). "stars"positions, it does not recolour. The vanilla star colour cannot be changed without aSkyRenderermixin (the stars are a baked vertex buffer drawn with a brightness uniform)."stars"locks a pattern to the rotating star sphere; it does not make the vanilla stars red."stars"is a real lock. Vanilla rotates the star sphere with a fixed pose (Ry(-90°) · Rx(starAngle)); the shader inverts it exactly, so astarspattern stays put relative to the star material as the field turns. This does not hijackdome_rotation, which stays the world-Y author spin described below.
Compatibility note (deliberately not implemented). Two parts of the sky design are not shipped because they would be Iris no-ops and would need a mixin or a frame-graph change:
- drawing an effect under the vanilla sun/moon/stars (the "sky-layer selector", S4b) - a
SkyRenderermixin; under a pack the vanilla body is not drawn by the game'sSkyRenderer, so the injection would silently do nothing; and - recolouring the vanilla stars (S6) - the same
SkyRenderermixin and the same no-op under a pack.
So a sky_pattern always sits over the sun and the moon, and the vanilla star colour cannot be
changed. That is a design decision, not a bug.
sky_mode - how the sky is addressed¶
How a pixel's view ray is turned into a pattern coordinate is chosen by the sky_mode field.
There are two modes:
sky_mode |
Projection | Use it for |
|---|---|---|
patch (default) |
One gnomonic (tangent-plane) decal at the authored anchor - a local chart, no built-in clip | A figure or image at one spot in the sky |
fill |
The whole sphere by three orthographic charts blended with a sharpened partition of unity ("triplanar on a sphere") | Content that must cover the whole sky (cracks, a full-sky fill) |
There is no equirectangular mode. A single global chart of a sphere is topologically unable to
tile it cleanly: u is undefined at the poles, so tiling there has infinite frequency and a
whole-sky pattern winds into a funnel at the zenith - the worst place, because that is where
players look - and u has to wrap somewhere (±180° yaw), leaving a mandatory visible seam /
mirror axis. Those are properties of the chart, not formula bugs, and no formula fixes them. The
two modes therefore differ in which local chart addresses the ray; neither wraps a global chart.
patchprojects the ray onto the tangent plane at the anchor and evaluates the figure on the resulting cell;tile_scaleis the cell scale on that plane. The decal is a flat sign and has no built-in clip - it runs out to the tangent-plane horizon, and a pixel behind the decal (dot(dir, anchor) <= 0) is discarded as a hard backstop. That horizon can read as a straight cut across a large patch; if you want a clean edge, restrict the effect with a mask. In-plane distortion is1/cos(angle from the anchor)(~1.41 at 45°, ~2 at 60°), so keeptile_scale <= 1.0for clean decals.filltiles each of the three orthographic charts and blends them with a sharpened partition of unity (fixed sharpness 8). Within about 40° of a chart pole - the zenith included - a single chart has weight > 0.9, so there is no pole convergence and no seam anywhere. The charts' coverage and colour are blended (never their UVs - they are incomparable frames), and the colour is renormalised by the blended coverage so texture texels stay saturated inside the crossfade ribbons.repeattiling lives inside each chart.
A full skybox cube (six authored faces, not a tiling) is a separate future feature; fill is
the whole-sky tiling mode today.
The patch decal is a flat sign, not a whole-sky fill: it is one tangent plane projected onto
the dome, so it does not follow the dome's curvature and its in-plane scale grows toward the rim.
It has no built-in clip - add a mask for a clean edge. Use it for a
figure or image at a spot; use fill for anything that must wrap the whole sky.
Fields¶
Every screen effect also accepts
screen_layerand the shared definition fields (duration,easing,loop,persistent,fade_ticks,sound).
| Field | Type | Default | Meaning |
|---|---|---|---|
screen_layer |
float | 0 | Must be 0 (at layer 1+ the depth buffer no longer covers the scene) |
anchor |
string | (absent) | Which vanilla sky body the effect follows: sun, moon, stars, or dome/absent for the literal anchor. Structural (not animatable); an unknown value is a parse error, and it is only valid on a sky_pattern. See anchor |
sky_mode |
string | patch |
Projection: patch (gnomonic decal) or fill (three-chart whole sphere). Case-insensitive; an unknown value - including the removed dome - is a parse error naming the accepted values. Set it explicitly |
anchor_yaw |
float | 0 | Dome yaw of the pattern centre, in degrees (yaw 0 = south, 90 = west, ±180 = north). Animatable. In fill mode it is a tiling phase shift, not a position |
anchor_pitch |
float | 0 | Dome pitch of the pattern centre, in degrees (pitch -90 = straight up, 0 = horizon, 90 = straight down). Animatable. In fill mode a tiling phase shift |
dome_rotation |
float | 0 | Rotates the whole image about the world Y axis, in degrees, before it is projected. In patch mode it moves the decal along its latitude; the in-plane rotation spins the figure in place. Animatable. To lock a pattern to the rotating star sphere use anchor: "stars", not this |
tile_scale |
float | 1 | Size of one cell. In patch mode it is the cell scale on the tangent plane (the decal is a flat sign with no built-in clip, so keep it modest - <= 1.0 for clean decals); in fill mode it is the tile half-size in units where 1.0 = 90° from the chart pole. Larger = bigger cell |
line_width |
float | — | Numeric override of the structural pattern.stroke_width (cell units) |
color_r / color_g / color_b |
float | 1 / 1 / 1 | Pattern colour |
opacity |
float | 1 (fades to 0) | Overall strength |
distort |
float | 0 | Sine warp of the pattern coordinate (0 = off) |
rotation |
float | — | Numeric override of the structural pattern.rotation, in degrees; spins the figure and a texture together |
frame |
float | 0 | Sprite-sheet frame index when pattern.texture.sheet is set (rounded, wrapped into 0..cols*rows-1); literal, keyframe, expr or graph-driven |
texture_tint |
float | 0 | 0 = draw the texture's own RGB; 1 = multiply it by color_r/g/b. opacity always scales coverage |
The mapping is world-fixed. Animating dome_rotation spins the sampled direction about world
Y before it is projected, so in patch mode it carries the decal along its latitude - around
the sky. For a true sky-lock use anchor: "stars", which takes the sampled direction into the star
sphere's own frame instead of spinning about Y; the in-plane rotation spins the figure in place.
The anchor frame follows the authoring convention (u+ = increasing yaw, v+ = increasing pitch).
pattern (structural)¶
The figure lives in a top-level structural pattern block - the same shared shape library as
surface_pattern and Per-pixel fields. All the
figure fields are identical (figure, radius, radius_x/radius_y, half_width/half_height,
corner_radius, sides, rotation, fill, stroke_width, softness, repeat); see the
surface_pattern pattern table for the full list.
The one difference: a pattern.center world anchor is ignored by sky_pattern - the anchor is
the dome position anchor_yaw/anchor_pitch, not a point in the world.
pattern.texture (structural, optional)¶
Replaces the procedural figure with a real texture drawn on the same dome cell. The texture is
the figure; an authored figure becomes its mask (coverage = texture channel x shape coverage).
Every field is the same as surface_pattern
(id, source, atlas, channel, sheet, aspect); an atlas source (block/item/atlas)
samples the sprite's own sub-rect, a standalone source samples a resource-pack texture over
0..1. A sheet selects a cell and the numeric frame param animates it.
Examples¶
Nine red dots in one small spot - a patch decal (tile_scale ~0.3) with a
circle figure and repeat: [3, 3] (the repeat tiles inside the one decal, so the dots stay
together; no texture needed):
{
"type": "sky_pattern",
"duration": 120, "persistent": true, "fade_ticks": 20,
"params": {
"screen_layer": 0, "sky_mode": "patch", "anchor_pitch": -25.0,
"tile_scale": 0.3, "opacity": { "start": 0.0, "end": 1.0 },
"color_r": 1.0, "color_g": 0.0, "color_b": 0.0
},
"pattern": {
"figure": "circle", "radius": 0.12, "fill": "solid", "softness": 0.01, "repeat": [3, 3]
}
}
A texture tiled across the whole sky - fill mode, where repeat tiles inside each chart:
{
"type": "sky_pattern",
"duration": 200, "persistent": true, "loop": true,
"params": {
"screen_layer": 0, "sky_mode": "fill", "tile_scale": 1.5, "opacity": 0.9, "color_b": 0.8,
"rotation": { "keyframes": [ { "time": 0, "value": 0 }, { "time": 200, "value": 360, "easing": "linear" } ] }
},
"pattern": {
"repeat": [6, 3],
"texture": { "id": "minecraft:block/nether_portal", "source": "block", "channel": "alpha" }
}
}
An animated sheet (frame keyframes) as a decal:
{
"type": "sky_pattern",
"duration": -1, "persistent": true, "loop": true,
"params": {
"screen_layer": 0, "sky_mode": "patch", "anchor_pitch": -40.0, "tile_scale": 0.8, "opacity": 0.95,
"frame": { "keyframes": [ { "time": 0, "value": 0 }, { "time": 120, "value": 15, "easing": "linear" } ] }
},
"pattern": {
"repeat": [2, 2],
"texture": { "id": "mypack:textures/vfx/cracks", "source": "standalone", "sheet": [4, 4], "channel": "alpha" }
}
}
To shoot a beam from a point on the sky, pair the pixels with
light_beam aimed at that dome direction (dir_x/dir_y/dir_z) - the
pixels are a layer-0 post pass, the beam is world geometry.
Notes¶
- Sky only. The pass is gated on the depth sky test: a non-sky pixel is passed through
untouched, so a
sky_patterncan never tint terrain, the hand or the GUI. A pack that leaves no trustworthy far depth makes the gate never match - the effect no-ops (fail-closed), never a full-screen fill. patchlimits. Keeptile_scale <= 1.0for clean decals. The decal has no built-in clip: it is a flat sign (one tangent plane), so it does not follow the dome's curvature and its in-plane scale grows toward the rim, ending at the tangent-plane horizon (a pixel behind the decal horizon is discarded). For a clean edge, restrict the effect with a mask. Usefillfor content that must cover the whole sky.filllimits. The per-chart density varies by up to ~1.7x between a chart pole and a chart diagonal, and there are soft crossfade ribbons along the|x|=|y|,|y|=|z|,|z|=|x|great circles (roughly ±8°). For chaotic content such as cracks the ribbons read as slightly denser cracks, not as a cut or a seam.- No equirectangular mode. A single global chart of a sphere has a pole singularity and a
mandatory seam - topological, not fixable - so it is not offered. Use
patchfor a figure at a spot andfillfor the whole sky. - No depth reconstruction, no normals. Unlike
surface_patternthere is no surface selection:normal_mask, asurfaceblock andfade_radiusdo not apply. Restrict the region with a mask (space: "dome"or theskyleaf) if needed. - Textures. Same resolver as
surface_pattern: a missing sprite in a valid atlas draws the vanilla missing texture and warns once (fail-visible); an unknown atlas or unreadable standalone PNG draws nothing and warns once (fail-closed). The sampler is NEAREST with no mipmaps, so a pixel texture stays crisp. The texture is re-resolved every frame, so/reloadtakes effect. - Parse errors. The same
pattern/pattern.texturevalidation assurface_pattern(unknown key, badsource/channel/aspect/sheet, blank or invalidid/atlas), plus an unknownsky_modestring (naming the acceptedpatch/fill) and an unknownanchorvalue (oranchoron a non-sky_patterntype).
Code¶
A soft stroked ring at a spot in the sky (patch), pulsing in size and brightness. It stays at
its spot: the animation is tile_scale and opacity only - animating dome_rotation would carry
the decal along its latitude, around the sky. Its datapack definition:
{
"type": "sky_pattern",
"duration": 200, "easing": "linear", "persistent": true, "loop": true, "fade_ticks": 12,
"params": {
"screen_layer": 0,
"sky_mode": "patch", "anchor_yaw": 0.0, "anchor_pitch": -30.0,
"tile_scale": { "keyframes": [ { "time": 0, "value": 0.4 }, { "time": 100, "value": 0.55 }, { "time": 200, "value": 0.4 } ] },
"color_r": 0.35, "color_g": 0.85, "color_b": 1.0,
"opacity": { "keyframes": [ { "time": 0, "value": 0.4 }, { "time": 100, "value": 0.9 }, { "time": 200, "value": 0.4 } ] }
},
"pattern": {
"figure": "ellipse", "fill": "stroke", "radius_x": 0.42, "radius_y": 0.42,
"stroke_width": 0.03, "softness": 0.03, "repeat": [1, 1]
}
}
Variants¶
show_sky_pattern_texture¶
A vanilla block sprite tiled across the whole sky (fill):
{
"type": "sky_pattern",
"duration": 200, "easing": "linear", "persistent": true, "loop": true, "fade_ticks": 12,
"params": {
"screen_layer": 0, "sky_mode": "fill", "anchor_yaw": 0.0, "anchor_pitch": 0.0,
"tile_scale": { "keyframes": [ { "time": 0, "value": 0.7 }, { "time": 100, "value": 1.0 }, { "time": 200, "value": 0.7 } ] },
"opacity": 0.9, "distort": 0.0,
"rotation": { "keyframes": [ { "time": 0, "value": 0.0 }, { "time": 200, "value": 360.0, "easing": "linear" } ] }
},
"pattern": {
"repeat": [4, 2],
"texture": { "id": "minecraft:block/nether_portal", "source": "block", "channel": "alpha", "aspect": "preserve" }
}
}
sky_cracks¶
The whole-sky case: a fill with an integer repeat and an animated sprite sheet, so the cracks
cover the entire sky with no pole funnel:
{
"type": "sky_pattern",
"duration": 120, "easing": "linear", "persistent": true, "loop": true, "fade_ticks": 10,
"params": {
"screen_layer": 0, "sky_mode": "fill", "tile_scale": 1.5, "opacity": 0.95,
"color_r": 0.9, "color_g": 0.9, "color_b": 1.0, "texture_tint": 1.0,
"frame": { "keyframes": [ { "time": 0, "value": 0 }, { "time": 120, "value": 15, "easing": "linear" } ] }
},
"pattern": {
"repeat": [6, 3],
"texture": { "id": "minecraft:block/cracked_stone_bricks", "source": "block", "sheet": [4, 4], "channel": "luminance", "aspect": "preserve" }
}
}
sky_anchor_sun, sky_anchor_moon, sky_anchor_stars¶
sky_anchor_sun
sky_anchor_moon
sky_anchor_stars
/vfx play vfx_demos:sky_anchor_sun
/vfx play vfx_demos:sky_anchor_moon
/vfx play vfx_demos:sky_anchor_stars
A glowing ring that tracks the sun through the day (anchor: "sun", a patch):
{
"type": "sky_pattern",
"duration": -1, "persistent": true, "loop": true, "fade_ticks": 12,
"anchor": "sun",
"params": { "screen_layer": 0, "sky_mode": "patch", "tile_scale": 0.55, "opacity": 0.9,
"color_r": 1.0, "color_g": 0.85, "color_b": 0.35 },
"pattern": { "figure": "circle", "fill": "stroke", "radius": 0.42, "stroke_width": 0.05, "softness": 0.02 }
}
The same ring on the moon (anchor: "moon"), and a whole-sky texture locked to the stars
(anchor: "stars", a fill whose sampled direction is taken into the star sphere's own frame):
{
"type": "sky_pattern",
"duration": -1, "persistent": true, "loop": true, "fade_ticks": 12,
"anchor": "stars",
"params": { "screen_layer": 0, "sky_mode": "fill", "tile_scale": 1.6, "opacity": 0.55,
"color_r": 0.5, "color_g": 0.7, "color_b": 1.0, "texture_tint": 1.0 },
"pattern": { "repeat": [6, 3], "texture": { "id": "minecraft:block/cracked_stone_bricks",
"source": "block", "sheet": [4, 4], "channel": "luminance", "aspect": "preserve" } }
}
Code¶
Its datapack definition:
{
"type": "sky_pattern",
"duration": 200,
"easing": "linear",
"persistent": true,
"loop": true,
"fade_ticks": 12,
"params": {
"screen_layer": 0,
"anchor_yaw": 0.0,
"anchor_pitch": -30.0,
"sky_mode": "patch",
"tile_scale": {
"keyframes": [
{
"time": 0,
"value": 0.4
},
{
"time": 100,
"value": 0.55
},
{
"time": 200,
"value": 0.4
}
]
},
"color_r": 0.35,
"color_g": 0.85,
"color_b": 1.0,
"opacity": {
"keyframes": [
{
"time": 0,
"value": 0.4
},
{
"time": 100,
"value": 0.9
},
{
"time": 200,
"value": 0.4
}
]
}
},
"pattern": {
"figure": "ellipse",
"fill": "stroke",
"radius_x": 0.42,
"radius_y": 0.42,
"stroke_width": 0.03,
"softness": 0.03,
"repeat": [
1,
1
]
}
}