MCWIND v6CC0 1.0

A wind standard shaders can support.

Vendor one header, route your foliage through it. You get a researched, gust-front wind field with no mod installed, and it silently upgrades to live weather, presets and terrain shelter the moment a player installs a provider.

Re-vendor notice

If you vendored mcwind.glsl before 2026-07-26, replace it. Two normative constants changed: the at_midBlock clamp is now ±2.0 (was ±1.0) and the phase wrap is now 9600.0 (was 3600.0).

Neither breaks loudly: the first shears foliage for no visible reason, the second desynchronises from a provider's own particles after an hour of play. Nothing else in your integration needs to change.

Download mcwind.glsl v6 · CC0 Read the spec Quickstart

GLSL 1.20 ∩ 3.30 core · Iris + OptiFine · gbuffers + shadow · 874 lines · sha256 5748ddaf344e

shaders/gbuffers_terrain.vsh
#include "/lib/mcwind.glsl"

vec3  blockCentre = worldPos +
    clamp(at_midBlock.xyz / 64.0, -2.0, 2.0);

// grass
float h    = mcw_grassHeight(worldPos, blockCentre,
                             isUpperHalf);
vec2  push = mcw_grassPush(blockCentre, h);
push += mcw_gustWake(blockCentre, cameraPosition,
                     h);   // v5, optional
position.xz += push;
objectNormal = mcw_honami(objectNormal, push);

// leaves
float weld = mcw_leafWeld(worldPos, blockCentre);
position.xyz += mcw_leafSway(worldPos,
                     blockCentre, weld);
and the same lines in your shadow pass, not optional

The surface

Twelve public symbols. That is the whole API.

MCWIND standardises the wind and the plant responses to it: grass and canopy. Everything else in the file is mcw_-prefixed and free to use, and none of it is normative except the six things that carry a wire format: MCW_MAG_REF, MCW_LEAF_RANGE, MCW_WAKE_SIZE and the three texture encodings.

SignatureMeaning
vec2 mcw_windDir(vec3 blockCentre)Unit vector, downwind.
float mcw_windMag(vec3 blockCentre)0..1 normalised. Never m/s.
float mcw_windGain()Provider strength multiplier; 1.0 with no provider.
float mcw_windPhaseSeconds, wraps at 9600. Advances per frame, not per tick.
float mcw_windProvider0.0 analytic, 1.0 a live mod is driving the field.
float mcw_grassHeight(...)The height weight: exactly 0.0 at a planted base, 1.0 at a one-block tip.
vec2 mcw_grassPush(...)World-space horizontal displacement, in blocks. Terrain shelter is folded in here as of v3.
vec3 mcw_honami(...)Optional. Tilts the shading normal with the lean, so a gust front reads as a travelling band of light.
float mcw_leafWeld(...)v2. How free a leaf is to move: 0.0 welded to the wood, 1.0 fully free. Returns 1.0 with no provider.
vec3 mcw_leafSway(...)v4. The canopy motion: held lean, breathing, gust spring-back, calm idle. Needs no provider at all.
vec2 mcw_gustWake(...)v5. Blown debris drafts the grass it skims past. Takes the camera position, because the header cannot have one. Returns vec2(0.0) with no provider.
vec3 mcw_fireLean(...)v6. Wind shears a flame downwind. The lean saturates rather than growing without limit, because a flame is buoyant and tears instead of lying flat. Needs no provider at all.

The provider seam

Same pack code. Two data sources.

A provider publishes three uniforms and three texture channels. You do not declare or handle any of them; mcwind.glsl does. Supporting a provider costs you nothing: no second code path, no mod detection, no fallback to write, and nothing added to shaders.properties. New channels arrive by swapping the file.

two version numbers, and they are not the same one

Header version is which symbols mcwind.glsl defines, what this page publishes, currently v6. mcw_providerVersion is what the installed mod supplies at runtime, currently up to 4.0. They move independently, and conflating them is the easiest way to misread the table.

HeaderAddsNeeds a provider?
v1analytic field + grass responseno
v2mcw_leafWeld: canopy anchoringyes · mcw_leafAnchor
v3terrain shelter & steering, folded into mcw_grassPushyes · mcw_windFlow
v4mcw_leafSway: canopy motionno
v5mcw_gustWake: debris drafts the grass it skimsyes · mcw_gustWake_tex
v6mcw_fireLean: wind shears a flame downwindno

Canopy motion needs no mod at all. It is the analytic field plus geometry, so it works at mcw_providerVersion 0.0 and on OptiFine. Only anchoring, terrain shelter and the gust wake need a provider. And terrain shelter is deliberately not a symbol: as of v3 it lives inside mcw_grassPush, so a v1-era integration gained it by swapping the file and changing nothing. The gust wake is the counterpoint, and it is a symbol for one concrete reason: it needs the camera position to fade out before its tile wraps, and the header cannot have one, because declaring a uniform the pack already owns is a hard compile error. So the pack passes it in, on one extra line.

Honest limits

What the header does not do.

  • The field is not continuous across the 9600-second wrap: one visible seam every 2h40m, in exchange for float precision holding everywhere else. Known and specified.
  • No terrain awareness on its own. Lee wakes, crest speed-up and valley funnelling need a provider's solver. The header cannot see the world.
  • The canopy anchor has no vertical structure. mcw_leafAnchor is a column field (distance to the nearest wood in XZ), so the very crown of a tall tree is held stiffer than it should be. Known, and unchanged by v5. It is the first thing a future version should fix.
  • mcw_windMag is 0..1 normalised. Never m/s.
  • It is not a layer on top of your own wave. Run both and the motion doubles and the base un-plants.
  • Budget grass and canopy separately. Grass is ~23 value-noise taps per vertex; mcw_leafSway is ~59, and MCW_EDDY = 0.0 does not gate its three extra curls. That still leaves ~47.

A worked example

What it actually takes, in someone else's pack.

Rather than describe the integration, here it is: MCWIND wired into Eclipse, a large, actively maintained Chocapic13-lineage pack. Grass and canopy, both passes, with every decision documented and a revert path.

Eclipse + MCWIND

3 files · +108 −3
new fileshaders/lib/mcwind.glsl, the vendored CC0 header
gbuffers2 uniforms, 1 include, leaf branch replaced, grass branch added
shadowsame again, plus an at_midBlock declaration
removed3 lines: the two calls being replaced, and one range gate

Vines, fire and water keep Eclipse's own wave, so both responses sit side by side in one scene and can be compared directly. It also fixed a pre-existing bug in passing: Eclipse called two different overloads of its own leaf function in the two passes, so its leaf geometry and leaf shadow had never been computing the same displacement.

See the integration Read the writeup

Stated plainly: that is a fork, offered upstream, not a shipped adoption. It is a demonstration that the integration is small and legible in a pack nobody wrote for this standard, and it has not been reviewed by Eclipse's author. Eclipse is an edit of Bliss, which is an edit of Chocapic13's shaders; the fork carries their credits and Chocapic13's terms.

Nobody owns MCWIND.

mcwind.glsl is dedicated to the public domain under CC0 1.0. No attribution required, no warranty. Vendor it, ship it, fork it, sell it. A standard that one project controls is not a standard, and the namespace is mcw_, deliberately named after no pack and no mod.

Quickstart docs Repository