Real-time grass techniques: sub-agent research (fetched 2026-10-01)
Nothing needed from you. One correction to the brief first: the iPad (A16) has a 4-core GPU, not 5-core; the 5-core A16 GPU is in the iPhone 14 Pro and 15 [source: https://en.wikipedia.org/wiki/IPad_(A16), https://www.notebookcheck.com/Apple-A16-GPU-5-Core-Grafikkarte-Benchmarks-und-Spezifikationen.652743.0.html].
All pages were read through WebFetch, which summarises with a small model, so treat quoted text as close paraphrase. The Wohllaib talk, the Horizon slides and the CEDEC graphics talk could not be opened; those topics rest on secondary sources (section C).
A. Findings per topic
1. Ghost of Tsushima and reimplementations
- Official abstract: blades are generated individually on the GPU, each with procedural appearance and animation. [source: https://gdcvault.com/play/1027033/Advanced-Graphics-Summit-Procedural-Grass]
- Secondary write-up: "roughly 83,000 blades on screen at once" in "about 2.5 milliseconds per frame". [source: https://tigerabrodi.blog/what-we-can-learn-from-grass-in-ghost-of-tsushima-renders]
- Each blade is a cubic bezier with 4 control points; near blades have 15 vertices, far blades 7. Far tiles are "twice as big but have the same number of blades". For short grass, the spare 8 vertices build a second blade beside the first. [source: same]
- Five culling stages, cheapest first: distance, frustum, occlusion, type, height. [source: same]
- Clumping uses voronoi cells; each cell has a clump id with artist parameters (pull to centre, facing, height, width, tilt, bend). [source: https://github.com/cainrademan/Unity-Grass]
- Wind is "scrolling 2D perlin noise" fed into a sine function that moves the bezier control points and the facing direction. [source: same]
- Normals are curved and tilted outward for a rounded look, and blended toward the terrain normal at distance to reduce aliasing. Blades seen edge-on are widened in view space. [source: same]
- Very far away the blades are replaced by a single texture on the terrain. [unconfirmed: this appeared only in search snippets of tigerabrodi.blog/grass-in-ghost-of-tsushima; my fetch of that page did not return the sentence]
- Tile size in metres, blades per tile, gloss, translucency and shadow method are [unconfirmed].
- Wind talk (Rockenbeck): "The main wind vector has a constant direction, but we varied the magnitude a bit from place to place using time-varying Perlin noise", and this is what shows as gusts on large fields. [source: https://www.gamedeveloper.com/design/using-vorticles-to-simulate-wind-in-i-ghost-of-tsushima-i-]
- SimonDev Quick_Grass (three.js, MIT) constants: 3,072 blades per patch, patch size 10, 6 segments (14 vertices) near, 1 segment (4 vertices) beyond 15 units, max distance 100, blade width 0.1, height 1.5. One
InstancedBufferGeometryper patch, with frustum and distance culling per patch. [source: https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/main/src/base/render/grass-component.js] - SimonDev vertex shader:
- Wind is two noise layers:
xz*0.05 + 0.05*timefor direction andxz*0.25 + timefor strength. - Two normals per vertex are blended for a rounded blade, then mixed toward UP with distance.
- The player pushes blades with
smoothstep(2.5, 1.0, distToPlayer)and a lean of up to 0.2 rad. - [source: https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/main/public/shaders/grass-lighting-model-vsh.glsl]
- Wind is two noise layers:
- SimonDev fragment shader: no discard (opaque geometry), root darkening
mix(1.0, 0.25, density), and wrapped backlight withwrap = 0.5. [source: https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/main/public/shaders/grass-lighting-model-fsh.glsl] - GodotGrass: scrolling Perlin texture sampled at several frequencies for wind, cellular noise for clumps, normals bent in the fragment shader, tile LOD with two meshes. [source: https://github.com/2Retr0/GodotGrass]
2. Breath of the Wild / Tears of the Kingdom
- [official] I found no CEDEC text on grass rendering. The translated CEDEC 2017 notes I could open cover level design, sound and project management only. [source: https://gist.github.com/idbrii/e39fe96279aa1670319bfa521d907399, https://gigazine.net/gsc_news/en/20171004-breath-of-the-wild-design-secret/]
- [community, data-mined]
grass.extmis a 64×64 grid per tile with 4 bytes per vertex: blade height, r, g, b. [source: https://zeldamods.org/wiki/Grass.extm] - [community, data-mined] "When up close, the grass height automatically matches the HGHT tiles, and its color automatically matches the ground textures." Terrain blends at most 2 textures per point and has 9 LOD levels. [source: https://zeldamods.org/wiki/Terrain]
- [community, visual analysis] "Close up grass is polygonal, mid-grass grass uses masked materials, hills in the distance use clever shading." [source: https://www.resetera.com/threads/zelda-breath-of-the-wild-the-technical-analysis.8197/page-4]
- [community] Blades are very low-poly and sharp-edged, so probably not alpha-mapped; lit tips go "almost white" against the sun. [source: https://forums.unrealengine.com/t/botw-grass-style/437647]
- three.js recreation by James Smyth: 5 vertices per blade, vertex colour as bend weight, sine wind offset by field UV, and a scrolling cloud texture for moving shadow. [source: https://smythdesign.com/blog/stylized-grass-webgl/]
- Daniel Ilett's "BotW-style" tutorial uses base and tip colours and a scrolling wind map; it makes no claim about Nintendo's method. [source: https://danielilett.com/2021-08-24-tut5-17-stylised-grass/]
- Cel bands, cutting, draw distance and TotK are [unconfirmed].
3. Genshin and the "Ghibli" recipe
- The miHoYo GDC 2021 talk "'Genshin Impact': Crafting an Anime Style Open World" (Haoyu Cai) exists but is members-only. It is a GDC talk, not Unite 2020. [source: https://www.gdcvault.com/play/1027538/-Genshin-Impact-Crafting-an]
- An unattributed frame analysis claims 48–114 triangles per grass mesh by LOD and up to 32 grass groups per draw call. [source: https://parsers.vc/news/250124-the-art-of-game-rendering--a-deep-dive-into/] [unconfirmed: the article names no method or author]
- The write-up of miHoYo's console rendering talk covers shadows and lighting only, not grass. [source: https://nugglet.github.io/posts/2022/12/console_graphics_rendering_pipeline_genshin_impact]
- Recipe pieces, each from a tutorial:
- Normals forced to (0,1,0) or taken from the terrain; grass colour from the terrain layer texture. [source: https://www.cyanilux.com/tutorials/gpu-instanced-grass-breakdown/]
- Two colours interpolated root to tip; wind from a scrolling distortion texture; 3 segments per blade. [source: https://roystan.net/articles/grass-shader/]
- A large-scale colour variation texture (clouds plus Perlin through a gradient), kept subtle; flat look from one or two colours and no roughness. [source: https://80.lv/articles/meadows-creating-stylized-nature-in-ue4]
- Slow "wind wave" plus fast "wind noise", with a small colour shift tied to it. [source: https://trungduyng.substack.com/p/anime-grass-tutorial-blender]
- Billboard quads with a scrolling noise texture for wind, chunked buffers, chunk LOD. [source: https://github.com/GarrettGunnell/Grass]
- three.js examples:
- al-ro: 100,000 instances, 8 triangles each, two sine waves. [source: https://al-ro.github.io/projects/grass/]
- Nitash-Biswas: 400,000+ blades, 7 segments near and 3 far, LOD hysteresis of 1 unit. [source: https://github.com/Nitash-Biswas/grass-shader-glsl]
- Gjoreski (TSL plus compute): about 1.18 million blades in a 130×130 patch, 4 segments, 120 fps on an M2 MacBook Pro. Stochastic thinning keeps full density to 10 units and falls to 10% at 60. Camera-facing sprites halved fragment work. [source: https://aleksandargjoreski.dev/blog/growing-my-grass-shader/]
- [inference] Wind that brightens the grass is a tutorial convention; I found no shipped-game source for it.
4. Horizon Zero Dawn
- Alpha-tested vegetation is drawn twice: "First as early occluders; after that, we render them normally. In this initial depth-only pass, we do our alpha test." The main pass then uses a plain depth compare. [source: https://80.lv/articles/horizon-zero-dawn-interview-with-the-team]
- Placement is procedural from "a couple of texture lookups". [source: same]
- Normals, vertex budget, LODs, grass colour and wind spring settings are [unconfirmed]; the slide PDF exceeds the 10 MB fetch limit.
5. Other stylised games
- Sable (GDC 2022, Kythreotis): flat shading, then layers of light, fog and lines. Fog "had the biggest impact overall"; outline opacity fades with distance to hide pop-in. [source: https://www.gamedeveloper.com/marketing/how-shedworks-refined-the-art-of-sable-in-pursuit-of-readability]
- The Witness (Casey Muratori): grass patches placed with blue noise of varying radius, a sphere test against geometry, and distance grids for edges. Placement only, no shading detail. [source: https://caseymuratori.com/blog_0013]
- Sea of Thieves (crossed planes, clover under-layer, flowers painted into the grass texture), Alba (grass spawned from the terrain splat) and Firewatch (23 tree models, distance-simplifying shader) appeared in search snippets only. [unconfirmed]
- Ori, Kena, Tunic, Eastshade and A Short Hike: no technical source found.
6. Wind
- Cheapest documented form: constant direction with Perlin-modulated magnitude (topic 1).
- Scrolling noise texture sampled at world xz: Roystan, Acerola, GodotGrass, and
textureLod(wind_noise, world_uv, 0.0)in the vertex shader. [source: https://godotshaders.com/shader/stylized-grass-with-wind-and-deformation/] - God of War uses a "full 3D GPU fluid simulation". [source: https://gdcvault.com/play/1026404/Wind-Simulation-in-God-of] [inference: out of budget for this target]
- One TSL demo layers noise-advected gusts, ripples, rolling sine fronts and per-blade flutter with "gust sheens". [source: https://github.com/hieudeptrai196/grass]
7. Interaction
- One uniform (xyz plus radius): push =
normalize(diff.xz) * (1 - saturate(length(diff)/radius)). The author suggests an array or a render texture for more objects. [source: https://gamedev.center/tutorial-how-to-make-an-interactive-grass-shader-in-unity/] - Same pattern in Godot:
character_position,character_radius3.0, and a falloff curve texture. [source: https://godotshaders.com/shader/stylized-grass-with-wind-and-deformation/] - Render target: an orthographic camera above the player renders only particles into a texture that the grass samples. [source: https://www.karthikchandran.com/project/grass-project/]
- God of War's "ground vegetation character interaction with swaying settle" is named in the abstract only. [source: https://gdcvault.com/play/1026036/Interactive-Wind-and-Vegetation-in]
- [inference] One uniform costs a few vertex instructions; the render target adds a small pass and a vertex texture fetch.
8. Alpha
- Apple: "HSR is both pixel perfect and submission order independent." Draw "First, opaque. Second, alpha test, discard or depth feedback. And third, and finally, translucent meshes." Also: "if we discard or update the depth, we will need to go back to HSR." [source: https://developer.apple.com/videos/play/wwdc2020/10602/]
- Apple defines overdraw as fragment shader invocations divided by pixels stored. [source: https://developer.apple.com/videos/play/wwdc2020/10603/]
- Apple on MSAA: pixels without edges blend per pixel, and resolve happens from tile memory. [source: https://developer.apple.com/videos/play/wwdc2020/10602/]
- Apple engineer on the forum: alpha test and discard cost the same; "alpha blending... can be faster." [source: https://developer.apple.com/forums/thread/85318]
- Imagination PowerVR: avoid discard, since it forces depth work into the texture stage. Order: opaque, alpha-tested, blended. [source: https://docs.imgtec.com/starter-guides/powervr-architecture/html/topics/rules/do-not-use-discard.html]
- Arm: a search snippet said discard and alpha-to-coverage both force a late depth test. [unconfirmed: the Arm pages returned empty]
- Golus:
- Sharpened coverage:
a = (a - cutoff) / max(fwidth(a), 0.0001) + 0.5. - Mip fix:
a *= 1 + mipLevel * 0.25. - 4x MSAA gives 5 coverage levels.
- [source: https://bgolus-medium-com.translate.goog/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f?_x_tr_sl=en&_x_tr_tl=es&_x_tr_hl=es&_x_tr_pto=tc (translation proxy; Medium returned 403)]
- Sharpened coverage:
- [inference] On an Apple GPU, opaque blades get full hidden-surface removal with no sorting. The flower cards use discard, so they shade every layer; draw them after the grass and keep them few. I found no measurement that compares the two on Apple hardware.
9. Distance
- Godot series: blades fade out over 10–20 units while an impostor plane fades in over 5–10. The high LOD is about 10,000 blades (90,000 triangles) per 5×5 m chunk, the low LOD one triangle per blade, the impostor 4 triangles. Whole effect "just under 2 ms" on the author's desktop. From above, the real meadow looks darker because the ground shows through, so the impostor adds angle-dependent noise spots. [source: https://hexaquo.at/pages/grass-rendering-series-part-4-level-of-detail-tricks-for-infinite-plains-of-grass-in-godot/]
- 3DWorld: six LODs of 1500, 800, 450, 250, 150 and 100 blades per patch, where a merged blade takes the summed width. Distant tiles use the green terrain texture; blades sink under the mesh to hide the switch. [source: http://3dworldgen.blogspot.com/2015/05/infinite-fields-of-grass.html]
- Doubling the tile at constant blade count (GoT) and BotW's ground-matched colour both let the terrain carry the far field. [inference from topics 1 and 2]
10. Budgets, three.js, A16
- A16 5-core GPU: 3DMark Wild Life Extreme Unlimited 2,901, Aztec Ruins High 45.93 fps, M1 7-core about 31% faster. [source: notebookcheck URL above] A desktop equivalent is [unconfirmed].
- iPad (A16): 2360×1640, 6 GB RAM. [source: Wikipedia URL above]
- [inference] 2360×1640 with 4x MSAA is about 15.5 million samples, so fill rate is the likely limit before vertices; a pixel-ratio cap is the first lever.
- General three.js guidance: about 100 draw calls on mobile; half resolution "can roughly double frame rate in fill-rate-bound scenes". [source: https://www.utsubo.com/blog/threejs-best-practices-100-tips]
- Author claims, untested by me:
- 50K–100K blades of 3 segments on mobile. [source: https://github.com/CK42BB/procedural-grass-threejs]
- Tiers from about 54k to 803k blades on 12 m tiles with three LOD rings on one lattice. [source: https://github.com/hieudeptrai196/grass]
- three.js forum (manthrax): about 3 million triangles is a safe budget; sort front to back if you can. [source: https://discourse.threejs.org/t/performance-optimizing-3m-instanced-grass-in-three-js/81286]
- [inference] The current 66,000 blades (22,000 clumps × 3) and 155k triangles sit below GoT's 83,000 blades and inside the mobile ranges above.
- TSL:
varying(node, name)andvertexStage(node)compute in the vertex stage.positionNodesets the local position.- Texture sampling in the vertex shader is transpiled so it works.
- [source: https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language]
- Per-instance data:
instancedBufferAttribute(array, type, stride, offset),instancedDynamicBufferAttribute,instancedArray(count, type)(storage buffer) andinstanceIndex. [source: https://threejs.org/docs/pages/TSL.html]
11. Flowers and air
- GoT leaves and petals are particles that sample "vorticles" (spheres of wind), summed brute-force "up to hundreds". Particles take height-map samples ahead to rise over obstacles. [source: gamedeveloper vorticles URL above]
- Flowers as hand-painted cut-outs assembled into clusters. [source: https://80.lv/articles/meadows-creating-stylized-nature-in-ue4]
- Variable-radius blue noise suits drifts. [source: https://caseymuratori.com/blog_0013] [inference]
- Below the Grassland draws grass and flowers in three.js r185 WebGPU and TSL, with five quality tiers down to a phone tier; it gives no numbers. [source: https://below-the-grassland.ameen-abdullah.dev/]
- Turning or bobbing flower heads and pollen: no source found.
B. Sources (all fetched 2026-10-01)
- https://gdcvault.com/play/1027033/Advanced-Graphics-Summit-Procedural-Grass — GDC Vault, Wohllaib — abstract
- https://tigerabrodi.blog/what-we-can-learn-from-grass-in-ghost-of-tsushima-renders — Tiger Abrodi — GoT numbers
- https://tigerabrodi.blog/grass-in-ghost-of-tsushima — Tiger Abrodi — vertex counts, culling
- https://github.com/cainrademan/Unity-Grass — Cain Rademan — clumps, wind, normals
- https://github.com/2Retr0/GodotGrass — 2Retr0 — wind, clumps, normals
- https://github.com/harlan0103/Grass-Rendering-in-Modern-Game-Engine — features only
- https://haoranliang.com/grass-rendering — no numbers
- https://github.com/simondevyoutube/Quick_Grass — SimonDev — README
- https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/main/src/base/render/grass-component.js — constants
- https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/main/public/shaders/grass-lighting-model-vsh.glsl — wind, normals, push
- https://raw.githubusercontent.com/simondevyoutube/Quick_Grass/main/public/shaders/grass-lighting-model-fsh.glsl — shading
- https://www.gamedeveloper.com/design/using-vorticles-to-simulate-wind-in-i-ghost-of-tsushima-i- — Rockenbeck quotes
- https://advances.realtimerendering.com/s2021/jpatry_advances2021/index.html — Patry — nothing on grass
- https://zeldamods.org/wiki/Grass.extm — grass data format
- https://zeldamods.org/wiki/Terrain — terrain data
- https://gist.github.com/idbrii/e39fe96279aa1670319bfa521d907399 — CEDEC 2017 translation — no grass
- https://gigazine.net/gsc_news/en/20171004-breath-of-the-wild-design-secret/ — CEDEC report — no grass
- https://www.resetera.com/threads/zelda-breath-of-the-wild-the-technical-analysis.8197/page-4 — community LOD claim
- https://forums.unrealengine.com/t/botw-grass-style/437647 — community observations
- https://smythdesign.com/blog/stylized-grass-webgl/ — James Smyth — three.js BotW grass
- https://danielilett.com/2021-08-24-tut5-17-stylised-grass/ — Daniel Ilett
- https://roystan.net/articles/grass-shader/ — Roystan
- https://www.cyanilux.com/tutorials/gpu-instanced-grass-breakdown/ — Cyanilux
- https://github.com/GarrettGunnell/Grass — Acerola
- https://halisavakis.com/my-take-on-shaders-grass-shader-part-i/ — gradient map, displacement wind (background, not cited)
- https://trungduyng.substack.com/p/anime-grass-tutorial-blender — anime grass
- https://80.lv/articles/meadows-creating-stylized-nature-in-ue4 — David Holland
- https://www.gdcvault.com/play/1027538/-Genshin-Impact-Crafting-an — miHoYo talk metadata
- https://parsers.vc/news/250124-the-art-of-game-rendering--a-deep-dive-into/ — unattributed Genshin numbers
- https://nugglet.github.io/posts/2022/12/console_graphics_rendering_pipeline_genshin_impact — Genshin console notes
- https://discussions.unity.com/threads/genshin-impact-grass-rendering-how.984026/ — speculation only
- https://80.lv/articles/horizon-zero-dawn-interview-with-the-team — Horizon two-pass alpha test
- https://www.gamedeveloper.com/art/video-how-guerrilla-crafted-the-lush-vegetation-of-i-horizon-zero-dawn-i- — abstract
- https://www.gamedeveloper.com/marketing/how-shedworks-refined-the-art-of-sable-in-pursuit-of-readability — Sable
- https://caseymuratori.com/blog_0013 — The Witness placement
- https://gdcvault.com/play/1026404/Wind-Simulation-in-God-of — abstract
- https://gdcvault.com/play/1026036/Interactive-Wind-and-Vegetation-in — abstract
- https://gamedev.center/tutorial-how-to-make-an-interactive-grass-shader-in-unity/ — trample uniform
- https://godotshaders.com/shader/stylized-grass-with-wind-and-deformation/ — wind texture, push
- https://www.karthikchandran.com/project/grass-project/ — shells, render-target interaction
- https://developer.apple.com/videos/play/wwdc2020/10602/ — Apple — HSR, draw order, MSAA
- https://developer.apple.com/videos/play/wwdc2020/10603/ — Apple — overdraw
- https://developer.apple.com/forums/thread/85318 — Apple engineer on discard
- https://docs.imgtec.com/starter-guides/powervr-architecture/html/topics/rules/do-not-use-discard.html — Imagination
- Golus via translate.goog (URL in topic 8) — formulas
- https://hexaquo.at/pages/grass-rendering-series-part-4-level-of-detail-tricks-for-infinite-plains-of-grass-in-godot/ — impostor LOD
- http://3dworldgen.blogspot.com/2015/05/infinite-fields-of-grass.html — LOD merging
- https://aleksandargjoreski.dev/blog/growing-my-grass-shader/ — TSL grass numbers
- https://github.com/hieudeptrai196/grass — TSL tile and LOD design
- https://github.com/CK42BB/procedural-grass-threejs — mobile blade claims
- https://al-ro.github.io/projects/grass/ — al-ro
- https://github.com/Nitash-Biswas/grass-shader-glsl — Nitash Biswas
- https://discourse.threejs.org/t/performance-optimizing-3m-instanced-grass-in-three-js/81286 — budgets
- https://discourse.threejs.org/t/real-time-grass-simulation-in-the-browser-over-1-million-blades-at-60-fps/82808 — 1M merged blades, M1 (background, not cited)
- https://www.utsubo.com/blog/threejs-best-practices-100-tips — mobile guidance
- https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language — TSL
- https://threejs.org/docs/pages/TSL.html — TSL signatures
- https://www.notebookcheck.com/Apple-A16-GPU-5-Core-Grafikkarte-Benchmarks-und-Spezifikationen.652743.0.html — A16 benchmarks
- https://en.wikipedia.org/wiki/IPad_(A16) — iPad specs
- https://below-the-grassland.ameen-abdullah.dev/ — r185 WebGPU meadow
- https://github.com/tougenrip/thirdfold/issues/308 — a project's opaque-blade policy (background, not cited)
C. Not found or not opened
- Wohllaib talk: the Vault page is members-only, YouTube returned only a footer, and I found no slide PDF. The Vault's PDF link for sid 1027545 is the streaming talk.
- Horizon slides and Rockenbeck slides: both PDFs exceed the 10 MB fetch limit (media.gdcvault.com).
- CEDEC 2017 "layered world" graphics talk: no readable report; the Medium translation returned 403.
- Medium (Golus original, Alba environment art): 403. Golus was read through the translation proxy.
- God of War slides: sms.playstation.com returned an empty page, jianshu.com 403.
- Arm early-Z and alpha-test docs: empty body on support.arm.com.
- Other blocked pages: Codrops "False Earth" (403), Class Central Genshin summary (403), Unreal's Kena interview (403), cohost Sea of Thieves post (web.archive.org blocked), PC Gamer Firewatch (body missing), Minions Art site and Patreon (empty or 403), confreaksandgeeks and quicks.ai (403, 404).
- Not searched: Ned Makes Games, MangoButtermilch, Brackeys, thebenezer's "fluffy grass".
- Searched without result: Ori, Tunic, Eastshade, A Short Hike, Hades-style grass; a published grass budget measured on an Apple mobile GPU or in Safari WebGPU; a desktop GPU equivalent for the A16.