Quickstart

Vendor one header, route your foliage through it, and do the same in your shadow pass. On a conventional pack this takes about ten minutes and you will see grass move on the first reload.

spec v6 CC0 1.0 no mod required

Note

You do not need WindLink, or any mod, to integrate MCWIND. With no provider installed the header runs its own analytic field (a prevailing heading that veers over minutes, curl-noise eddies, travelling gust fronts, a diurnal cycle) plus a grass response and a canopy response. A provider adds live weather, presets and terrain shelter on top, and your pack does not change to receive them.

1 · Vendor the file

Drop mcwind.glsl into your pack, conventionally at shaders/lib/mcwind.glsl, and include it with a pack-root-absolute path. The leading slash matters.

your pack
your-pack/
  shaders/
    lib/
      mcwind.glsl      ← here
    gbuffers_terrain.vsh
    shadow.vsh

It is guarded by MCWIND_GLSL, so double-including is safe. The file is the intersection of GLSL 1.20 and 3.30 core, so it compiles on Iris and OptiFine, in gbuffers and shadow alike.

2 · Check four things

Each of these has a silent failure mode: nothing errors, you just get the wrong picture and no clue why.

worldPos must be ABSOLUTE

Chocapic-lineage packs (Chocapic13, BSL, Complementary, Eclipse and most descendants) carry a camera-relative position through the vertex shader. Pass worldPos + cameraPosition. Check what your own waving function is fed. If its call sites add cameraPosition, so must yours.

symptom: the whole field slides with the camera

Declare at_midBlock yourself, in both passes

MCWIND cannot declare it: a duplicate attribute declaration is a hard compile error. Guard on IRIS_FEATURE_BLOCK_EMISSION_ATTRIBUTE; .xyz covers both shapes. If your pack already declares it behind a feature guard, declare yours under the exact negation of that guard.

symptom: grass translates off its block, or a duplicate-declaration error

Three uniforms: check, do not assume

frameTimeCounter, worldTime, rainStrength. Grep the actual file you are editing, not the pack in general. One well-maintained pack we integrated declared exactly one of the three in the stage being edited.

symptom: compile error, or a field that never changes

Turn OFF your own wave

MCWIND is not a layer on top. This means more than deleting a calcMovePlants call. Disable any competing grass generation, including geometry-stage systems a vertex displacement cannot reach. Confirm the effective value, including any user override file.

symptom: doubled motion, an un-planted base, or nothing changing at all

3 · Add the lines

Find where your pack decides a block is waving foliage, and replace that branch. Five lines for grass, two more for canopy.

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);
position.xz += push;

// leaves
float weld = mcw_leafWeld(worldPos, blockCentre);
position.xyz += mcw_leafSway(worldPos,
                     blockCentre, weld);

The clamp is ±2.0, not ±1.0

at_midBlock is a signed byte in 1/64-block units, so it carries ±2 blocks. Bushy-leaf resource packs build leaf blocks from plane fronds whose free ends sit about 1.3 blocks from the block centre, and a ±1.0 clamp truncates those vertices by different amounts per vertex, which destroys the one property every consumer of blockCentre depends on: that it is constant for every vertex of a block. The symptom is not a crash, it is plants shearing for reasons nobody can find. Earlier drafts of mcwind.glsl shipped ±1.0.

isUpperHalf is 1.0 for the top block of a two-tall plant (tall grass, large fern) and 0.0 otherwise. That single float is what welds the two halves into one continuous blade.

Optional, v5. Debris the provider is blowing about (torn leaves, grass flecks, dust) drafts the grass it skims past. The line goes in the grass branch, between mcw_grassPush and position.xz += push, with the same h. It needs a v5 provider: without one it returns zero, so calling it unconditionally is safe and does nothing.

optional · v5
push += mcw_gustWake(blockCentre,
                     cameraPosition, h);

Grass first

If mcw_grassPush is not working in your pack yet, get that right before you touch leaves. The canopy reuses the same prerequisites, and debugging both at once is how people conclude MCWIND is broken. When you are ready, the canopy has its own guide: Integrating the canopy →. It covers finding your leaf branch, dropping your lightmap weight, why Minecraft's own leaf distance blockstate does not work, and the eight canopy dials.

4 · Repeat in the shadow pass

Shadow maps are rendered by a separate program. If foliage bends in gbuffers but not here, it sways in and out of its own static shadow and the contact lighting crawls. Depending on sun angle this reads as “the shadow moves but the grass doesn't”, and the moving shadow can mask the real motion entirely.

Paste the same lines, with the same numbers. Two traps: at_midBlock and the three uniforms are declared per-file, so the shadow pass needs its own copies; and your pack's distance gate on waving may differ between the two passes. One pack we integrated gates geometry waving at 64 blocks and shadow waving at 24.

5 · Verify

If something is wrong

SymptomLikely cause
Nothing moves at allYour own wave is still running and cancelling, or a competing geometry-stage grass system generates the blades downstream of your edit.
The whole field slides with the cameraworldPos is camera-relative. Add cameraPosition.
Foliage translates off its block instead of bendingat_midBlock is missing or wrongly signed, so blockCentre is not a block centre.
Plants or leaves shear apart for no visible reasonYour blockCentre clamp is ±1.0. It must be ±2.0.
Shadow flickers, or the shadow moves and the grass doesn'tShadow pass not patched. Shadow maps are a separate program.
Everything judders at ~20 HzA provider is publishing a per-tick phase. Not fixable in the pack. Report it.
Presets from a wind mod do nothingThe provider is not reaching your program: mcw_windProvider reads 0.0. Ask the mod author to verify their uniforms register on the terrain and shadow programs.
mcw_gustWake does nothingNo v5 provider. It returns zero below mcw_providerVersion 4.0, by design, and there is nothing to fix in the pack.
Compile error: duplicate declarationYou declared a uniform or attribute MCWIND already has, or your pack declares it behind a guard you did not negate.

Fast way to prove the provider seam

Add position.y += mcw_windProvider * 0.5; temporarily. Grass floating half a block means the uniforms are arriving and the problem is downstream; grass at normal height means they are not, and it is the mod side. It is a position offset on purpose: no varying to add, no fragment-shader change, and it cannot be confused with a lighting or tint difference. Remove it afterwards.

Download mcwind.glsl API reference