# Assistant conversation C76FN4

- Canonical URL: <https://m.sheaf.less.rest/C76FN4>
- Resource IRI: <https://m.sheaf.less.rest/C76FN4>
- Type: assistant conversation
- Mode: quick

## Transcript

### 1. User

hey, what insights and stuff can we formulate on the topic of like continuousish LOD rendering of stuff like foliage and grass, especially with the kind of 'physically based' way of thinking

### 2. Assistant tool calls

#### Tool call: `list_documents`

```json
{}
```

### 3. Tool result: list_documents

FOLDERS
- Accounting Algebra & Property Theory
- Assembly & Mechanism Ontology
- Discrete & Finite Element Exterior Calculus
- Graph-Theoretic & Compositional Systems Theory
- Living Structure, Wholeness & Beauty
- Living Structure, Wholeness &amp; Beauty
- ontology
- Procedural Generation & Co-Creation
- Roads, Trails & Movement
- Scenario-Based & Behavioral Programming
- Technology, Scale & Conviviality
- Terrain Modeling
- Terrain, Hydrology & Erosion
- Water Simulation & Rendering

Accounting Algebra & Property Theory (4)
- #GANFP9 A Fundamental Duality in the Mathematical and Natural Sciences: From Logic to Biology - 2024 | David Ellerman | 53 pp.
  Micro abstract: Traces an overlooked subset–partition duality—from logic and category theory through entropy and quantum indefiniteness to selectionist and generative mechanisms in biology.
- #NBH3BE Algebraic Models for Accounting Systems - 2010 | Derek J. S. Robinson, José García Pérez, Robert A. Nehmer, Salvador Cruz Rambaud | World Scientific | 255 pp.
  Micro abstract: Develops accounting theory algebraically: balance vectors as modules, transactions as a group, and whole accounting systems as automata with quotients, homomorphisms, and decision algorithms for audit and control.
- #7ESDBJ Economics, Accounting, and Property Theory - 1982 | David P. Ellerman | Lexington Books | 110 pp.
  Micro abstract: Ellerman's vector-accounting monograph: double entry generalized to property vectors ("accounting without valuation"), grounding a property-theoretic account of appropriation, the firm, and goodwill.
- #C8FHDZ On implication and negation in partition logic - 2025 |  , David Ellerman | Open Journal of Mathematical Sciences | 9 pp. | doi:10.30538/oms2025.0250
  Micro abstract: Develops implication as a refinement-sensitive operation on set partitions, showing how relative negation yields local Boolean cores within the non-distributive algebra of partitions.

Assembly & Mechanism Ontology (4)
- #6PYNP3 Object-Oriented Representation of Electro-Mechanical Assemblies Using UML - 2003 | Fujun Wang, Kevin W. Lyons, National Institute of Standards and Technology, Ram D. Sriram, Shaw C. Feng, Sudarsan Rachuri, Utpal Roy, Young-Hyun Han | NISTIR 7057 | 97 pp.
  Micro abstract: NIST report proposing the Open Assembly Model, a UML-based object-oriented representation for electro-mechanical assembly information exchange.
- #KSLF8E Ontology and Assembly Joint Topology Representation - 2008 | Kyoung-Yun Kim | Computer-Aided Design and Applications | 9 pp. | doi:10.3722/cadaps.2008.630-638
  Micro abstract: Uses mereotopology and SWRL rules to represent assembly joints as ontological relations, illustrated on a fixture assembly case study.
- #NHJEJG Representing Geometric Structures in d Dimensions: Topology and Order - 1993 | Erik Brisson | Proceedings of the fifth annual symposium on Computational geometry | 10 pp.
  Micro abstract: Formalizes geometric structures in d dimensions via combinatorial topology and order relations, a foundation for cell-complex representations used in assembly/geometric modeling.
- #KT87MS Towards an Ontology for Generative Design of Mechanical Assemblies - 2019 | Bahar Aameri, Hyunmin Cheong, J. Christopher Beck | Applied Ontology | 24 pp.
  Micro abstract: Proposes a formal ontology of mechanical assembly structure intended to support generative design systems.

Discrete & Finite Element Exterior Calculus (2)
- #AX2VJD Discrete Exterior Calculus - 2005 | Anil N. Hirani, Jerrold E. Marsden, Mathieu Desbrun, Melvin Leok | arXiv:math/0508341 | 53 pp.
  Micro abstract: Introduces discrete exterior calculus, a coordinate-free discretization of differential forms on simplicial complexes for computational modeling.
- #LTESUF Finite Element Exterior Calculus: From Hodge Theory to Numerical Stability - 2010 | Douglas N. Arnold, Ragnar Winther, Richard S. Falk | arXiv:0906.4325 / Bulletin of the AMS | 74 pp.
  Micro abstract: Surveys finite element exterior calculus, unifying Hodge theory and mixed finite element stability analysis for PDE discretization.

Graph-Theoretic & Compositional Systems Theory (4)
- #TA7A6Y Algebras of Open Dynamical Systems on the Operad of Wiring Diagrams - 2015 | David I. Spivak, Dmitry Vagner, Eugene Lerman | arXiv:1408.1598 | 26 pp.
  Micro abstract: Develops an operad-algebra formalism for open dynamical systems composed via wiring diagrams, underpinning compositional systems theory.
- #PB725Y Graph theoretic foundations of multibody dynamics - 2011 | Abhinandan Jain | Multibody System Dynamics | 35 pp. | doi:10.1007/s11044-011-9267-6
  Micro abstract: Second of a two-part paper deriving O(N) recursive algorithms (Newton-Euler, Lyapunov/Riccati factorizations) for multibody dynamics from the graph-theoretic spatial kernel/operator (SKO/SPO) models built in Part I.
- #RT6CVD Port-Hamiltonian Systems on Graphs - 2013 | A. J. van der Schaft, B. M. Maschke | SIAM Journal on Control and Optimization | 32 pp. | doi:10.1137/110840091
  Micro abstract: Builds port-Hamiltonian systems on open directed graphs via Dirac structures, unifying mass-spring-damper networks, spatial mechanisms, hydraulic networks, and consensus dynamics under one compositional geometric framework.
- #A6DBK8 Seven Sketches in Compositionality: An Invitation to Applied Category Theory - 2018 | Brendan Fong, David I. Spivak | 353 pp.
  Micro abstract: A textbook introducing applied category theory (orders, monoidal categories, operads, sheaves, etc.) through seven worked "sketches" connecting math to systems modeling.

Living Structure, Wholeness & Beauty (9)
- #MH5J8D Beautimeter: Harnessing GPT for Assessing Architectural and Urban Beauty Based on the 15 Properties of Living Structure - 2025 | Bin Jiang | AI | 12 pp. | doi:10.3390/ai6040074
  Micro abstract: Presents Beautimeter, a GPT-based tool that scores buildings and urban scenes against Christopher Alexander’s 15 properties of living structure to assess their coherence and beauty.
- #XW22YY Generative Codes: The Path to Building Welcoming, Beautiful, Sustainable Neighborhoods - 2005 | Brian Hanson, Christopher Alexander, Maggie Moore Alexander, Michael Mehaffy, Randall Schmidt | Center for Environmental Structure | 21 pp.
  Micro abstract: Argues that living neighborhoods arise from generative codes: ordered, participatory steps that let buildings and public spaces unfold from local people, land, and context.
- #SKRF4C Geography as a Science of the Earth’s Surface Founded on the Third View of Space - 2022 | Bin Jiang | Annals of GIS | 14 pp. | doi:10.1080/19475683.2021.1966502
  Micro abstract: Recasts geography around an organismic view of space, using scaling and spatial dependence to understand—and deliberately create—places with greater living structure.
- #PXG56P Harmony-Seeking Computations: A Science of Non-Classical Dynamics Based on the Progressive Evolution of the Larger Whole - 2009 | Christopher Alexander | Unpublished manuscript | 66 pp.
  Micro abstract: Proposes harmony-seeking computation as a creative process that repeatedly strengthens latent centers in a configuration while preserving and deepening the larger whole.
- #MJKTBB Living Images: A Recursive Approach to Computing the Structural Beauty of Images or the Livingness of Space - 2023 | Bin Jiang, Chris de Rijke | Annals of the American Association of Geographers | 19 pp. | doi:10.1080/24694452.2023.2178376
  Micro abstract: Measures an image’s structural beauty by recursively extracting its nested substructures, revealing a compact hierarchy that also captures visual saliency.
- #3XSLTA Structural Beauty: A Structure-Based Computational Approach to Quantifying the Beauty of an Image - 2021 | Bin Jiang, Chris de Rijke | Journal of Imaging | 15 pp. | doi:10.3390/jimaging7050078
  Micro abstract: Proposes a quantitative measure of structural beauty based on how many substructures an image contains and how strongly they form a hierarchy across scales.
- #ZU8GZV Structure-Preserving Transformations - 2002 | Christopher Alexander | The Nature of Order, Book Two: The Process of Creating Life | 4 pp. | doi:10.2307/j.ctv27ftw6c.5
  Micro abstract: Explains structure-preserving transformations: incremental changes that extend the centers and relationships already present in a place rather than weakening its wholeness.
- #AULNWD The Nature of Poetic Order - 1998 | Richard P. Gabriel | Warren Wilson Alumni Conference, Mount Holyoke | 99 pp.
  Micro abstract: Gabriel's slide essay relating poetry's formal order to Christopher Alexander's ideas of generative structure, exploring how constraint and pattern produce living order in creative work.
- #BYG3BQ Wholeness as a Hierarchical Graph to Capture the Nature of Space - 2015 | Bin Jiang | International Journal of Geographical Information Science | 14 pp. | doi:10.1080/13658816.2015.1038542
  Micro abstract: Models spatial wholeness as a hierarchical graph of mutually reinforcing centers, using PageRank and scaling depth to quantify the life of parts and wholes.

ontology (36)
- #LRK6E2 A Taxonomy of Granular Partitions - 2001 | Barry Smith, Thomas Bittner | Spatial Information Theory: Foundations of Geographic Information Science | 16 pp. | doi:10.1007/3-540-45424-1_3
  Micro abstract: Formalizes partitions as tree-structured cognitive devices projected onto reality, then classifies how accurately, structurally, and completely their cells recognize objects, with applications to cadastral and geographic data.
- #FQCWKV A Theory of Granular Partitions - 2003 | Barry Smith, Thomas Bittner | Foundations of Geographic Information Science | 33 pp.
  Micro abstract: Formalizes granular partitions as hierarchical cell systems projected onto reality, combining cognitive selectivity with mereological structure for naming, classifying, mapping, and representation.
- #SF7KYZ About the Unreal - 2025 | Barry Smith, Jim Logan, John Beverley | Proceedings of the Joint Ontology Workshops (JOWO), Episode XI | 14 pp.
  Micro abstract: Models fiction, blueprints, simulations, and other information about unreal entities through logical combinations of actual classes, avoiding commitments to nonexistent dummy instances.
- #CGE2NC Against Fantology - 2005 | Barry Smith | Experience and Analysis | 22 pp.
  Micro abstract: Critiques the idea that first-order logic reveals reality’s ontology, tracing its atomism, timelessness, Booleanism, and reductionism before proposing a six-category ontology and an enhanced Davidsonian formal language.
- #JZG4PM Against Fantology Again - 2016 | Ingvar Johansson | The Theory and Practice of Ontology | 12 pp.
  Micro abstract: Extends the critique of fantology through default ontologization, arguing that Quine’s canonical notation is incoherent about classes and excludes intentional phenomena and distinct modes of existence.
- #E5CLFY Agglomerations - 1999 | Barry Smith | Spatial Information Theory: Cognitive and Computational Foundations of Geographic Information Science | 16 pp. | doi:10.1007/3-540-48384-5_18
  Micro abstract: Defines agglomerations as geographically dispersed yet unified aggregates—populations, cultures, organizations, and diasporas—and develops a realist mereotopology for their boundaries, identity, and change.
- #3CCZ4A Bodily Systems and the Spatial-Functional Structure of the Human Body - 2004 | Barry Smith, Igor Papakin, Katherine Munn | Ontologies in Medicine | 26 pp. | doi:10.3233/978-1-60750-945-5-39
  Micro abstract: Integrates anatomy and physiology by modeling the body as a nested spatial-functional hierarchy whose parts are demarcated as system elements through the functions they bear and realize.
- #7YZU95 Boundaries: An Essay in Mereotopology - 1997 | Barry Smith | The Philosophy of Roderick Chisholm | 32 pp.
  Micro abstract: Reconstructs and extends the Brentano–Chisholm mereotopology in which dependent, coincident boundaries account for contact and the continuum across points, lines, surfaces, and bodies.
- #3TZK66 Capabilities: An Ontology - 2024 | Barry Smith, David Limbaugh, Eric Merrell, John Beverley, Peter M. Koch | Proceedings of the Joint Ontology Workshops (JOWO), Episode X | 14 pp.
  Micro abstract: Defines a capability as a disposition in whose realization an organism or group has or had an interest, placing capabilities between dispositions and functions in Basic Formal Ontology.
- #XYERFR Carving Up Reality - 2004 | Barry Smith | Categories: Historical and Systematic Essays | 14 pp.
  Micro abstract: Explains how context-sensitive, coarse-grained partitions guide reference and perception while preserving transitive parthood and distinguishing fiat demarcations from boundaries grounded in reality.
- #88BVY3 Categories in Top-Level Ontologies: Revisiting the Aristotelian Background - Barry Smith, Ludger Jansen | 31 pp.
  Micro abstract: Reconstructs Aristotle’s categories as the philosophical basis of BFO, extending the ontological square with processes into a six-category framework for continuants, occurrents, dependence, and multiple scientific granularities.
- #9G4F42 CLASSIFYING PROCESSES: AN ESSAY IN APPLIED ONTOLOGY - 2012 | Barry Smith | Ratio | 21 pp. | doi:10.1111/j.1467-9329.2012.00557.x
  Micro abstract: Extends Basic Formal Ontology to scientific process data through process profiles—quality, rate, and cyclical aspects that ground measurements, time-series graphs, and representations of dynamic systems.
- #GSLMP8 Diagrams, Documents, and the Meshing of Plans - 2013 | Barry Smith | Visual Learning, vol. 3: How to Do Things with Pictures: Skill, Practice, Performance | 14 pp.
  Micro abstract: Shows how diagrams and evolving networks of documents mesh plans, obligations, and specialized labor to enable coordinated collective action beyond the limits of linear text.
- #M8BQ3S Do Mountains Exist? Towards an Ontology of Landforms - 2003 | Barry Smith, David M. Mark | Environment and Planning B: Planning and Design | 22 pp.
  Micro abstract: Argues that mountains are object-like in everyday thought but elevation fields in environmental science, motivating a geospatial ontology that supports both perspectives.
- #KSESR8 Drawing Boundaries - 2019 | Barry Smith | The Philosophy of GIS | 26 pp. | doi:10.1007/978-3-030-16829-2_7
  Micro abstract: Updates the distinction between human-demarcated fiat boundaries and physically grounded bona fide boundaries, tracing its uses in geography, property, ecology, and Basic Formal Ontology.
- #56MWAA Environmental Metaphysics - 2001 | Achille C. Varzi, Barry Smith | Metaphysics in the Post-Metaphysical Age: Proceedings of the 22nd International Wittgenstein Symposium | 12 pp.
  Micro abstract: Develops an ontology of token niches as tenant–medium–retainer structures, using physical and fiat boundaries to explain environmental fit, protection, movement, and niche construction.
- #K6JC2J Layers: A New Approach to Locating Objects in Space - 2003 | Barry Smith, Maureen Donnelly | Spatial Information Theory: Foundations of Geographic Information Science | 16 pp. | doi:10.1007/978-3-540-39923-0_4
  Micro abstract: Extends mereotopology with layers that distinguish material objects, holes, and processes from the spatial and spatiotemporal regions they occupy, supporting dynamic geospatial reasoning beyond static map overlays.
- #KG5TBB Making space: the natural, cultural, cognitive and social niches of human activity - 2021 | Barry Smith | Cognitive Processing | 11 pp. | doi:10.1007/s10339-021-01049-y
  Micro abstract: Shows how legal decisions, plans, historical reasoning, and language create fiat spatial and spatiotemporal entities, then draws limits and practical lessons for ontology-supported AI.
- #FJ5KCA More Things in Heaven and Earth - 1995 | Barry Smith | Grazer Philosophische Studien | 15 pp.
  Micro abstract: Develops an ontology of spatial regions and boundaries, arguing that political territories are historically created fiat objects through performative maps while also recognizing vague, overlapping, and incomplete geographic objects.
- #KY3Y9U Naïve Physics: An Essay in Ontology - 1994 | Barry Smith, Roberto Casati | Philosophical Psychology | 22 pp. | doi:10.1080/09515089408573121
  Micro abstract: Reconstructs naïve physics as a realist ontology of the common-sense world—objects, processes, stuffs, boundaries, media, and values—drawing on Gestalt psychology and phenomenology to broaden AI’s set-theoretic models.
- #TQPVBD New Foundations for Qualitative Physics - 1990 | Barry Smith, Jean Petitot | Evolving Knowledge in Natural Science and Artificial Intelligence | 13 pp.
  Micro abstract: Argues for a scientific ontology of the qualitative common-sense world, using morphological discontinuities to connect physical substrates, sensible qualities, Aristotelian categories, and ecologically constrained cognition.
- #B98HVX Objects and Their Environments: From Aristotle to Ecological Ontology - 2001 | Barry Smith | The Life and Motion of Socio-Economic Units | 26 pp. | doi:10.1201/9781482268096-14
  Micro abstract: Extends Aristotelian substance–accident ontology into a realist theory of behavioral settings and ecological niches as nested, bounded wholes in which organisms, objects, and activities mutually fit.
- #9GWUC8 On Classifying Material Entities in Basic Formal Ontology - 2012 | Barry Smith | Interdisciplinary Ontology: Proceedings of the Third Interdisciplinary Ontology Meeting | 13 pp.
  Micro abstract: Clarifies BFO’s material entities by distinguishing objects, aggregates, and fiat object parts, and analyzes objects through causal unity by covering, physical forces, or engineered assembly without claiming exhaustivity.
- #KYQGNH On Credentials - 2020 | Barry Smith, Giuseppe Lorini, Olimpia Giuliana Loddo | Journal of Social Ontology | 21 pp. | doi:10.1515/jso-2019-0034
  Micro abstract: Provides a social ontology of credentials as portable, inspectable institutional documents that certify identity or status and give bearers the practical deontic power to exercise rights, with a typology of their forms and functions.
- #BV47YZ On Drawing Lines on a Map - 1995 | Barry Smith | Spatial Information Theory: A Theoretical Basis for GIS | 10 pp. | doi:10.1007/3-540-60392-1_31
  Micro abstract: Builds a typology of spatial boundaries around the fiat–bona fide distinction, applying it to maps, political and property divisions, scattered objects, linguistic framing, and truthmakers.
- #D8LRQM Ontological Foundations for Geographic Information Science - 2004 | Barry Smith, David M. Mark, Max J. Egenhofer, Stephen C. Hirtle | A Research Agenda for Geographic Information Science | 8 pp. | doi:10.1201/9781420038330.ch12
  Micro abstract: Sets a research agenda for geospatial ontology, linking formal accounts of geographic objects, processes, scale, and vagueness to human concepts, interoperable data, and ontology-driven GIS.
- #GN66WW Ontologies of Common Sense, Physics and Mathematics - 2023 | Barry Smith, Jobst Landgrebe | arXiv | 32 pp. | doi:10.48550/arXiv.2305.01560
  Micro abstract: Proposes linked upper ontologies for common sense, physics, and mathematics, arguing that classical models connect real magnitudes to mathematics whereas modern physics relates measurements to entities lacking commonsense universals.
- #WYP3G6 Ontology and Geographic Kinds - 1998 | Barry Smith, David M. Mark | Proceedings of the 8th International Symposium on Spatial Data Handling (SDH ’98) | 7 pp.
  Micro abstract: Argues that geographic kinds are intrinsically spatial and boundary-centered, requiring mereology and topology to connect physical reality, cultural categorization, cognition, and GIS representation.
- #9YMD2E SNAP and SPAN: Towards Dynamic Spatial Ontology - 2004 | Barry Smith, Pierre Grenon | Spatial Cognition & Computation | 35 pp. | doi:10.1207/S15427633SCC0401_5
  Micro abstract: BFO's bicategorial framework: SNAP snapshot ontologies of continuants and a SPAN ontology of processes in spacetime, linked by trans-ontological relations to capture change — demonstrated on the ontology of geodynamics.
- #KWFTKJ Surrounding Space: The Ontology of Organism-Environment Relations - 2002 | Achille C. Varzi, Barry Smith | Theory in Biosciences | 29 pp. | doi:10.1078/1431-7613-00053
  Micro abstract: Develops a formal ontology of token niches as tenant–medium–retainer structures, extending static organism–environment fit into a dynamic account of vacant niches, movement, interaction, and niche construction.
- #DLY2WP The Cognitive Geometry of War - 1997 | Barry Smith | Current Issues in Political Philosophy: Justice in Society and World Order | 26 pp.
  Micro abstract: Argues that modern ideals of compact, contiguous national territory help drive conflict, and proposes perforated, non-contiguous, and geometrically flexible borders as alternatives to displacement and ethnic cleansing.
- #4QQD4A The Logic of Systems of Granular Partitions - 2005 | Barry Smith, Maureen Donnelly, Thomas Bittner | IFOMIS Reports | 23 pp.
  Micro abstract: Builds a sound S4-style modal logic for labeled, typed granular partitions, modeling refinement and cross-partition counterparts so differently selective views can reason about the same mereologically structured reality.
- #2F8T3H Toward a Realistic Science of Environments - 2009 | Barry Smith | Ecological Psychology | 11 pp.
  Micro abstract: Defends Gibsonian ecological realism: organisms directly perceive affordances in physically real niches, while granular partitions show how different species inhabit perspectives on one world, not separate constructed worlds.
- #DT9Y7X True Grid - 2002 | Barry Smith | Spatial Information Theory: Foundations of Geographic Information Science | 17 pp.
  Micro abstract: Generalizes Alberti’s perspectival grid into a realist theory of projection: pictures, maps, names, concepts, and databases are “true grids” when their cells preserve relevant structure and refer transparently to reality.
- #PHAFYA Truth and the Visual Field - 1997 | Barry Smith | Naturalizing Phenomenology: Issues in Contemporary Phenomenology and Cognitive Science | 8 pp.
  Micro abstract: Uses mereotopology and Gibsonian ecology to treat perception and language as carving transient fiat boundaries in reality, defining a judgment field as the truth-making portion of the world selected by a true sentence.
- #XZX6PE Vague Reference and Approximating Judgments - 2003 | Barry Smith, Thomas Bittner | Spatial Cognition and Computation | 20 pp.
  Micro abstract: Formalizes vague reference as multiple crisp candidate referents within granular partitions, then explains approximation as using familiar spatial or temporal reference grids to constrain vagueness without truth-value indeterminacy.

Procedural Generation & Co-Creation (14)
- #ABD2B8 Between Tech and Art: The Vegetation of Horizon Zero Dawn - 2018 | Gilbert Sanders, Guerrilla Games | Game Developers Conference (GDC) 2018 | 87 pp.
  Micro abstract: A production breakdown of Horizon Zero Dawn’s vegetation pipeline, covering global wind simulation, layered foliage motion, coverage-preserving alpha mipmaps, shading, asset LODs, placement, and cascaded shadows.
- #4TH488 Explainable AI for Designers: A Human-Centered Perspective on Mixed-Initiative Co-Creation - 2018 | Antonios Liapis, G. Michael Youngblood, Jichen Zhu, Rafael Bidarra, Sebastian Risi | 2018 IEEE Conference on Computational Intelligence and Games (CIG) | 8 pp. | doi:10.1109/CIG.2018.8490433
  Micro abstract: Defines explainable AI for game designers, mapping co-creative systems by their explainability, initiative, and domain overlap so explanations serve concrete design tasks.
- #9NQ94D Extracting Physics from Blended Platformer Game Levels - 2020 | Adam Summerville, Anurag Sarkar, Joseph C. Osborn, Sam Snodgrass | Joint Proceedings of the AIIDE 2020 Workshops (CEUR Workshop Proceedings, Vol. 2862) | 7 pp.
  Micro abstract: Infers playable jump physics from generated platformer levels, including hybrid physics models for levels that blend the geometry and style of multiple games.
- #66Q3W3 Ghost of Tsushima: Procedural Grass - 2021 | Eric Wohllaib, Sucker Punch Productions | Game Developers Conference (GDC) 2021 | 55 pp.
  Micro abstract: Explains Ghost of Tsushima’s compute-driven grass pipeline, from tiled placement and culling to indirect drawing, cubic Bézier blade geometry, variable LOD, wind animation, and material shading.
- #QHMFH2 Improved Alpha Testing Using Hashed Sampling - 2019 | Chris Wyman, Morgan McGuire | IEEE Transactions on Visualization and Computer Graphics | 12 pp. | doi:10.1109/TVCG.2017.2739149
  Micro abstract: Develops hashed alpha testing, a stable quasi-random thresholding method that preserves distant alpha-mapped foliage and hair while controlling flicker, anisotropy, and interactions with TAA and alpha-to-coverage.
- #7GR3AQ Procedural Content Generation through Quality Diversity - 2019 | Ahmed Khalifa, Antonios Liapis, Daniele Gravina, Georgios N. Yannakakis, Julian Togelius | 2019 IEEE Conference on Games (CoG) | 8 pp. | doi:10.1109/CIG.2019.8848053
  Micro abstract: Argues for quality-diversity algorithms in procedural generation, producing broad collections of varied, playable content while exposing the design space for exploration and co-creation.
- #CQBDX4 Procedural Content Generation via Machine Learning (PCGML) - 2018 | Aaron Isaksen, Adam Summerville, Amy K. Hoover, Andy Nealen, Christoffer Holmgård, Julian Togelius, Matthew Guzdial, Sam Snodgrass | IEEE Transactions on Games | 15 pp. | doi:10.1109/TG.2018.2846639
  Micro abstract: Defines and surveys PCGML: generating functional game content directly from models trained on existing examples, with uses spanning creation, completion, repair, critique, and compression.
- #EARFEK Procedural Generation of Villages on Arbitrary Terrains - 2012 | Adrien Bernhardt, Adrien Peytavie, Arnaud Emilien, Eric Galin, Marie-Paule Cani | The Visual Computer | 10 pp. | doi:10.1007/s00371-012-0699-7
  Micro abstract: Presents a three-stage procedural model that grows terrain-responsive village roads and settlements, partitions land into plausible parcels, and generates slope-adapted buildings with open shape grammars.
- #EDURTK Real-Time GPU Tree Generation - 2025 | Bastian Kuth, Carsten Faber, Dominik Baumeister, Max Oberberger, Pirmin Pfeifer, Quirin Meyer, Seyedmasih Tabaei | High-Performance Graphics – Symposium Papers | 10 pp. | doi:10.2312/hpg.20251168
  Micro abstract: Introduces a GPU work-graph pipeline that generates, animates, edits, and continuously LODs detailed seasonal trees every frame, replacing gigabytes of baked geometry with kilobytes of parameters.
- #GBXEP3 Realistic Modeling and Rendering of Plant Ecosystems - 1998 | Bernd Lintermann, Matt Pharr, Oliver Deussen, Pat Hanrahan, Przemyslaw Prusinkiewicz, Radomír Měch | Proceedings of SIGGRAPH ’98 | 12 pp. | doi:10.1145/280814.280898
  Micro abstract: Presents a foundational pipeline for authoring plant ecosystems through terrain design, ecological simulation, procedural plant models, approximate instancing, and efficient rendering of billion-primitive scenes.
- #BDBBL6 Real‐time Realistic Rendering and Lighting of Forests - 2012 | Eric Bruneton, Fabrice Neyret | Computer Graphics Forum | 11 pp. | doi:10.1111/j.1467-8659.2012.03016.x
  Micro abstract: Combines detailed z-field trees with terrain shader-maps to render immense forests in real time, preserving sun, sky, canopy, and ground-lighting effects through seamless, scale-consistent transitions.
- #PQ68ZH Responsive Real-Time Grass Rendering for General 3D Scenes - 2017 | Klemens Jahrmann, Michael Wimmer | Proceedings of the 2017 Symposium on Interactive 3D Graphics and Games (I3D ’17) | 10 pp. | doi:10.1145/3023368.3023380
  Micro abstract: Renders every grass blade as responsive tessellated geometry on arbitrary 3D surfaces, with per-blade wind, gravity, and collision physics plus aggressive culling that retains dense fields in real time.
- #WZ8DHP Runtime Evaluation of Procedural Content Generation in an Endless Runner Game Using Autonomous Agents - 2026 | Rishabh Kar | arXiv | 25 pp. | doi:10.48550/arXiv.2605.01783
  Micro abstract: Integrates procedural generation and validation in an endless runner, using aerial and ground agents to detect blocked or unnavigable content before the player reaches it.
- #NRBMD5 Towards Friendly Mixed Initiative Procedural Content Generation: Three Pillars of Industry - 2020 | Frederic Fol Leymarie, Gorm Lai, William Latham | Proceedings of the International Conference on the Foundations of Digital Games (FDG '20) | 4 pp. | doi:10.1145/3402942.3402946
  Micro abstract: Distills three requirements for industry-friendly co-creative PCG tools: preserve designer control, keep feedback loops short, and fit into existing production pipelines.

Roads, Trails & Movement (8)
- #G3TBNG A Sequential Two-Step Algorithm for Fast Generation of Vehicle Racing Trajectories - 2016 | J. Christian Gerdes, John Subosits, Nitin R. Kapania | Journal of Dynamic Systems, Measurement, and Control | 12 pp. | doi:10.1115/1.4033311
  Micro abstract: Generates near-optimal racing trajectories quickly by alternating between a minimum-time speed profile and a convex path update that reduces curvature.
- #B6P8L4 Active walker model for the formation of human and animal trail systems - 1997 | Dirk Helbing, Frank Schweitzer, Joachim Keltsch, Péter Molnár | Physical Review E | 34 pp. | doi:10.1103/physreve.56.2527
  Micro abstract: Models trail systems as self-organization: walkers reinforce attractive routes while unused traces fade, producing dendritic ant trails and low-detour pedestrian networks.
- #V4TQYB Interactive procedural street modeling - 2008 | Eugene Zhang, Gregory Esch, Guoning Chen, Pascal Müller, Peter Wonka | ACM Transactions on Graphics | 10 pp. | doi:10.1145/1360612.1360702
  Micro abstract: Lets designers generate and edit large street networks through tensor fields, combining procedural speed with brush-like global and local control over street patterns.
- #UYLTYJ Modelling the Evolution of Human Trail Systems - 1997 | Dirk Helbing, Joachim Keltsch, Péter Molnár | Nature | 11 pp. | doi:10.1038/40353
  Micro abstract: Shows how pedestrian trails emerge through feedback between destination-seeking walkers, existing paths, and vegetation recovery, yielding a compromise between directness and shared infrastructure.
- #GY93FG Mountain Trail Formation and the Active Walker Model - 2009 | J. P. Hague, S. J. Gilks | International Journal of Modern Physics C | 22 pp. | doi:10.1142/S0129183109014059
  Micro abstract: Extends the active-walker model to steep terrain, explaining zigzag mountain trails through slope avoidance, directional persistence, and mutual reinforcement by ascending and descending walkers.
- #LXV9AT Principles of Trail Layout and Design - 2019 | California State Parks | California State Parks Trails Handbook | 64 pp.
  Micro abstract: A field-oriented guide to durable trail design, emphasizing curvilinear alignment, natural drainage, sustainable grades, control points, and close reading of landform and soils.
- #XDEFZS Procedural Generation of Roads - 2010 | A. Peytavie, E. Galin, E. Guérin, N. Maréchal | Computer Graphics Forum | 10 pp. | doi:10.1111/j.1467-8659.2009.01612.x
  Micro abstract: Automatically routes and constructs roads with an anisotropic shortest-path method that weighs slope and obstacles while treating surface segments, bridges, and tunnels consistently.
- #ARP5U7 The Topography of Minoan Peak Sanctuaries - 1983 | A. A. D. Peatfield | The Annual of the British School at Athens | 8 pp. | doi:10.1017/s0068245400019729
  Micro abstract: Argues that Minoan peak sanctuaries were chosen for visibility and proximity to local settlements, forming a beacon-like sacred network whose contraction tracked settlement abandonment rather than cultic collapse.

Scenario-Based & Behavioral Programming (8)
- #P2W4J5 Adaptive Behavioral Programming - 2011 | David Harel, Nir Eitan | 8 pp. | doi:10.1109/ictai.2011.109
  Micro abstract: Adds reinforcements to live sequence charts and BPJ so scenario-based programs can learn from their environment, specifying goals to pursue and scenarios to avoid, with modular learning decompositions.
- #XQ5NKX Challenges in Modeling and Unmodeling Emergence, Rule Composition, and Networked Interactions in Complex Reactive Systems - 2023 | Assaf Marron, David Harel, Guy Frankel, Irun Cohen, Smadar Szekely | 8 pp. | doi:10.5220/0011728900003402
  Micro abstract: Position paper on modeling emergence, rule composition, and networked interactions in complex reactive systems, introducing "unmodeling"—explicitly excluding entities and behaviors from model execution.
- #D4VB7S Distributing Scenario-Based Models: A Replicate-and-Project Approach - 2017 | Assaf Marron, Daniel Gritzner, David Harel, Guy Katz, Joel Greenyer, Shlomi Steinberg | MODELSWARD 2017 | 16 pp. | doi:10.5220/0006271301820195
  Micro abstract: Distributes scenario-based models by replicating the full specification on every component and projecting it per component, mimicking centralized behavior while sharply reducing synchronization.
- #CSJARA Enhancing Scenario-Based Modeling Using Large Language Models - 2026 | Assaf Marron, David Harel, Guy Katz, Smadar Szekely | Communications in Computer and Information Science | Springer Nature Switzerland | pp. 43-68 | 26 pp. | doi:10.1007/978-3-031-96841-9_3
  Micro abstract: Extended methodology for combining LLM chatbots with scenario-based modeling: iterative generation of stand-alone scenarios checked by analysis and human review, framed as a step toward Wise Computing.
- #3JCRAD On Augmenting Scenario-Based Modeling with Generative AI - 2024 | Assaf Marron, David Harel, Guy Katz, Smadar Szekely | MODELSWARD 2024 | 12 pp. | doi:10.5220/0012427100003645
  Micro abstract: Outlines a structured method for using generative-AI chatbots in modeling: iteratively generate scenario-based model fragments, then analyze and inspect them to converge on an accurate system model.
- #QV3BWZ On tracing reactive systems - 2011 | David Harel, Shahar Maoz | Software &amp; Systems Modeling | 22 pp. | doi:10.1007/s10270-010-0151-2
  Micro abstract: Introduces model-based trace visualization and exploration for reactive systems, using scenario-based (LSC) abstractions and the Tracer prototype, demonstrated on a PacMan game.
- #TDS4H2 Relaxing Synchronization Constraints in Behavioral Programs - 2013 | Amir Kantor, David Harel, Guy Katz | LPAR 2013 (Logic for Programming, Artificial Intelligence, and Reasoning) | 17 pp. | doi:10.1007/978-3-642-45221-5_25
  Micro abstract: Proposes eager execution for behavioral programs: fast b-threads run ahead when synchronization outcomes are predictable, improving performance, modularity, and distributability, shown in a C++ BP framework.
- #M5788P Towards Behavioral Programming in Distributed Architectures - 2015 | Amir Kantor, Assaf Marron, David Harel, Gera Weiss, Guy Katz, Guy Wiener | Science of Computer Programming | 58 pp. | doi:10.1016/j.scico.2014.03.003
  Micro abstract: Extends behavioral programming to distributed architectures: b-threads as Erlang processes, eager execution to relax synchronization, and modular distributed execution, demonstrated on simulations and a quadrotor.

Technology, Scale & Conviviality (2)
- #WYH36B The City as Convivial Centre - 1974 | Leopold Kohr | Tract, no. 12 (Gryphon Press) | 18 pp.
  Micro abstract: Kohr's essay arguing that cities exist for convivial life rather than economic function, and that human-scale size is what lets a city serve as a centre of leisure, culture, and encounter.
- #67REFX The Question Concerning Technology - 1977 | Martin Heidegger | The Question Concerning Technology and Other Essays (Harper & Row) | 23 pp.
  Micro abstract: Heidegger's essay on the essence of technology as Enframing (Gestell), a mode of revealing that reduces the world to standing-reserve, and on art as a possible saving power.

Terrain Modeling (1)
- #Y55KCV Arches: A Framework for Modeling Complex Terrains - 2009 | A. Peytavie, E. Galin, J. Grosjean, S. Mérillou | Computer Graphics Forum | 11 pp. | doi:10.1111/j.1467-8659.2009.01385.x
  Micro abstract: Introduces a hybrid volumetric–implicit framework for interactively modeling terrains with overhangs, arches, caves, sand, and rocks, plus efficient tools for erosion and automated rock-pile generation.

Terrain, Hydrology & Erosion (8)
- #NV2YRW FastFlow: GPU Acceleration of Flow and Depression Routing for Landscape Simulation - 2024 | Aryamaan Jain, Bernhard Kerbl, Brandon Finley, Guillaume Cordonnier, James Gain | Computer Graphics Forum | 13 pp. | doi:10.1111/cgf.15243
  Micro abstract: A GPU framework for routing surface flow through terrain and its depressions fast enough to make erosion, river, lake, and ecosystem simulations interactive.
- #2284QZ From features to fingerprints: A general diagnostic framework for anthropogenic geomorphology - 2019 | Damian Evans, Erle C Ellis, Giulia Sofia, Paolo Tarolli, Wenfang Cao | Progress in Physical Geography: Earth and Environment | 34 pp. | doi:10.1177/0309133318825284
  Micro abstract: Integrates geomorphology, archaeology, and high-resolution remote sensing into a framework for reading anthropogenic landforms as landscape-scale sociocultural fingerprints.
- #96ZMGK Large Scale Terrain Generation from Tectonic Uplift and Fluvial Erosion - 2016 | Adrien Peytavie, Bedrich Benes, Guillaume Cordonnier, Jean Braun, Marie-Paule Cani, Éric Galin, Éric Guérin | Computer Graphics Forum | 11 pp. | doi:10.1111/cgf.12820
  Micro abstract: Generates large, controllable mountain terrains by coupling user-painted tectonic uplift with fluvial erosion, then turning the resulting stream graph into detailed landforms.
- #K82AS7 Legacy sediment: Definitions and processes of episodically produced anthropogenic sediment - 2013 | L. Allan James | Anthropocene | 11 pp. | doi:10.1016/j.ancene.2013.04.001
  Micro abstract: Broadens legacy sediment to episodically produced anthropogenic alluvium and colluvium, and explains its deposition, storage, and remobilization through sediment delivery–transport capacity dynamics.
- #DWXKYQ Physically-based analytical erosion for fast terrain generation - 2024 | Boris Gailleton, Guillaume Cordonnier, Petros Tzathas, Philippe Steer | Computer Graphics Forum | 14 pp. | doi:10.1111/cgf.15033
  Micro abstract: Turns the stream power law into an interactive terrain tool, replacing thousands of erosion time steps with analytical solutions and a direct control for landscape age.
- #MTDKDE Priority-Flood: An Optimal Depression-Filling and Watershed-Labeling Algorithm for Digital Elevation Models - 2014 | Clarence Lehman, David Mulla, Richard Barnes | Computers & Geosciences | 17 pp. | doi:10.1016/j.cageo.2013.04.024
  Micro abstract: Introduces Priority-Flood, a simple, optimal algorithm that removes drainage-blocking depressions from elevation models and can also derive watersheds and flow directions.
- #AK7NGE Procedural Riverscapes - 2019 | A. Peytavie, B. Benes, E. Galin, E. Guérin, J. Gain, T. Dupont, Y. Cortial | Computer Graphics Forum | 12 pp. | doi:10.1111/cgf.13814
  Micro abstract: Builds editable, animated riverscapes from bare terrain by carving hydrologically plausible channels and blending real-time procedural water primitives instead of simulating fluids.
- #DMTA8Y Terrain Generation Using Procedural Models Based on Hydrology - 2013 | Adrien Peytavie, Bedřich Beneš, Jean-David Génevaux, Éric Galin, Éric Guérin | ACM Transactions on Graphics | 10 pp. | doi:10.1145/2461912.2461996
  Micro abstract: Generates controllable, multiscale terrain from a sketched drainage network, representing rivers and landforms as an editable hierarchy of continuous procedural primitives.

Water Simulation & Rendering (12)
- #RBS5K6 A Layered Particle-Based Fluid Model for Real-Time Rendering of Water - 2010 | Daniel Scherzer, Florian Bagar, Michael Wimmer | Computer Graphics Forum | 7 pp. | doi:10.1111/j.1467-8659.2010.01734.x
  Micro abstract: Renders particle-based water and volumetric foam in real time using perspective-aware surface smoothing, physically guided foam formation, and layered depth compositing.
- #C4AY2M A Survey of Ocean Simulation and Rendering Techniques in Computer Graphics - 2011 | B. Crespin, D. Ghazanfarpour, E. Darles, J.-C. Gonzato | Computer Graphics Forum | 17 pp. | doi:10.1111/j.1467-8659.2010.01828.x
  Micro abstract: Surveys ocean graphics from spectral deep-water models to near-shore fluid simulation, then covers the foam, spray, and light transport needed for convincing rendering.
- #WZMZGY Advected river textures - 2009 | Dirk Arnold, Stephen Brooks, Tim Burrell | Computer Animation and Virtual Worlds | 11 pp. | doi:10.1002/cav.288
  Micro abstract: Combines a 2D Navier–Stokes solver, hydrostatic pressure columns, and advected procedural textures to render detailed, terrain-responsive rivers at real-time frame rates.
- #92XRH7 Lagrangian Texture Advection: Preserving both Spectrum and Velocity Field - 2011 |  Qizhi Yu, E. Bruneton, F. Neyret, N. Holzschuch | IEEE Transactions on Visualization and Computer Graphics | 13 pp. | doi:10.1109/tvcg.2010.263
  Micro abstract: Advects fluid textures with deformable particle grids, preserving both the input texture’s visual spectrum and exact motion along the velocity field without cumulative stretching.
- #8SERGP Real-time Breaking Waves for Shallow Water Simulations - 2007 | Markus Gross, Matthias Müller-Fischer, Nils Thürey, Simon Schirm | 15th Pacific Conference on Computer Graphics and Applications (Pacific Graphics 2007) | 8 pp. | doi:10.1109/PG.2007.33
  Micro abstract: Adds real-time overturning waves to shallow-water heightfields by detecting steep fronts and spawning connected particle sheets that collapse into splashes and foam.
- #CWC7H9 Real-time Rendering of Enhanced Shallow Water Fluid Simulations - 2013 | Antonio Susín, Jesús Ojeda | Computers & Graphics | 9 pp.
  Micro abstract: Builds a real-time rendering pipeline for shallow-water simulations, adding fine surface detail, advected foam, photon-based caustics, and screen-space reflection and refraction.
- #MVUJ8Z Real-time Rendering of River Networks - 2010 | Quintijn Hendrickx, Rafael Bidarra, Ruben M. Smelik | Proceedings of the ACM SIGGRAPH Symposium on Interactive 3D Graphics and Games | 1 pp.
  Micro abstract: Renders branching river networks efficiently with quadratic Bézier curves, GPU distance fields, and streaming normal maps instead of dense geometry or particle simulation.
- #5MGCZ5 Real-time River Representation by Dynamic Control of Data on Waves - 2008 | Makoto Kosugi, Nobuhiko Mukai, Yasuhiro Kato | 4 pp. | doi:10.3169/itej.62.2063
  Micro abstract: Dynamically switches river-wave models by viewing distance, preserving nearby reflection and wave detail while retaining wind-driven motion across the full landscape.
- #XDESU9 Scalable real‐time animation of rivers - 2009 | Eric Bruneton, Fabrice Neyret, Nicolas Holzschuch, Qizhi Yu | Computer Graphics Forum | 11 pp. | doi:10.1111/j.1467-8659.2009.01363.x
  Micro abstract: Animates rivers across vast terrains by computing steady flow locally and advecting fine surface detail only where visible, at a screen-space sampling density.
- #869NHK Very Fast Real-Time Ocean Wave Foam Rendering Using Halftoning - 2011 | Ian Parberry, Jennifer R. Alford, Mary Yingst | Proceedings of the 6th International North American Conference on Intelligent Games and Simulation (GAMEON-NA) | 7 pp.
  Micro abstract: Uses a spectrally controlled halftone mask to make texture-based ocean foam dissipate as clumped bubbles that pop, rather than merely fading transparently.
- #A2QB8L Water Flow in Portal 2 - 2010 | Alex Vlachos | ACM SIGGRAPH 2010 Course: Advances in Real-Time Rendering in 3D Graphics and Games | 54 pp.
  Micro abstract: Explains Portal 2’s production-friendly water shader: artist-painted flow maps advect layered normal maps, while offsets and noise suppress repetition and pulsing on limited hardware.
- #PBM2TC Water surface wavelets - 2018 | Chris Wojtan, Matthias Müller-Fischer, Miles Macklin, Nuttapong Chentanez, Stefan Jeschke, Tomáš Skřivan | ACM Transactions on Graphics | 13 pp. | doi:10.1145/3197517.3201336
  Micro abstract: Represents water waves as spatially varying directional amplitudes, enabling vast interactive simulations with fine detail, moving obstacles, solid coupling, and direct artistic control.

Unfiled (1)
- #3CV92Z moppe

### 4. Assistant

Great topic — the corpus has a solid cluster on this. Let me pull the outlines of the most relevant documents.

#### Tool call: `get_document`

```json
{
  "id": "66Q3W3"
}
```

#### Tool call: `get_document`

```json
{
  "id": "ABD2B8"
}
```

#### Tool call: `get_document`

```json
{
  "id": "EDURTK"
}
```

#### Tool call: `get_document`

```json
{
  "id": "QHMFH2"
}
```

#### Tool call: `get_document`

```json
{
  "id": "BDBBL6"
}
```

#### Tool call: `get_document`

```json
{
  "id": "3CV92Z"
}
```

### 5. Tool result: get_document

DOCUMENT #66Q3W3
Title: Ghost of Tsushima: Procedural Grass

Outline:
  - 1 #W32QZT GHOSTOF TSUSHIMA
    - 1.1 #5PUTKY Placing a Grass Blade
    - 1.2 #NF4YZX Generating a Grass Blade
      - 1.2.1 #YVU33M DATA PIPELINE
      - 1.2.2 #M2AA9T Cubic Bezier curve
    - 1.3 #8N9Y6H ACKNOWLEDGEMENTS

### 6. Tool result: get_document

DOCUMENT #ABD2B8
Title: Between Tech and Art: The Vegetation of Horizon Zero Dawn

Outline:
  - 1 #MFUGJF The Vegetation of Horizon Zero Dawn
    - 1.1 #52X55Y Welcome
    - 1.2 #TEDEY5 Topics
    - 1.3 #Z98PAS Our Renderer
    - 1.4 #82JYHM Horizon Zero Dawn
    - 1.5 #ECWA9L Our first steps...
      - 1.5.1 #HJR95K Killzone Shadow Fall:
      - 1.5.2 #9FDTZH Placement System
    - 1.6 #9TYLT2 Simulation
    - 1.7 #NZT45Z Making Foliage Move
      - 1.7.1 #7EWEMB Making Foliage Move
    - 1.8 #CZ5EA6 Categories of Spring Settings:
      - 1.8.1 #258E34 Trees and Plants
        - 1.8.1.1 #T6PRQR Vertex Program: Trees
      - 1.8.2 #X2P7C7 Vertex Program: Trees
        - 1.8.2.1 #MCYTHX Vertex Program: Trees
        - 1.8.2.2 #YLAJVL Vertex Program: Trees
        - 1.8.2.3 #5RLYFV Vertex Program: Trees
        - 1.8.2.4 #7VEUX8 Vertex Program: Trees
        - 1.8.2.5 #XVFHTY Vertex Program: Trees
      - 1.8.3 #S92KH2 Vertex Program: Plants
      - 1.8.4 #FDNGMJ Grasses
        - 1.8.4.1 #CXM7PA Vertex Program: Grass
        - 1.8.4.2 #NDVV5L Vertex Programs: Making Grass Move
        - 1.8.4.3 #3ZT5Q6 Vertex Programs: Making Grass Move
        - 1.8.4.4 #3RQF7M Vertex Programs: Making Grass Move
        - 1.8.4.5 #NVESGB Vertex Programs: Making Grass Do More
        - 1.8.4.6 #JVVVCU Vertex Programs: Making Grass Do More
        - 1.8.4.7 #43G49Z Vertex Programs: Making Grass Do More
        - 1.8.4.8 #FSRQ8J Vertex Programs: Making Grass Do More
      - 1.8.5 #ZB9PQG Shading – Alpha
        - 1.8.5.1 #4UJQLS Pixel Program: Alpha
          - 1.8.5.1.1 #XZDE58 Scalpel GPU Profiler
          - 1.8.5.1.2 #3JPF3T Draw Hierarchy / Samplers / Render targets / Render state / Shader state
          - 1.8.5.1.3 #S4F3RD Legend
            - 1.8.5.1.3.1 #GZGJ4G Selection
            - 1.8.5.1.3.2 #6TDX24 Table controls
          - 1.8.5.1.4 #RM7MAN Scalpel GPU Profiler
          - 1.8.5.1.5 #BQTL6Y Draw Hierarchy / Samplers / Render targets / Render state / Shader state
            - 1.8.5.1.5.1 #JB7378 Legend
            - 1.8.5.1.5.2 #K69M9B Selection
            - 1.8.5.1.5.3 #JXYTFK Table controls
        - 1.8.5.2 #JG85A7 Pixel Program: Alpha
          - 1.8.5.2.1 #CD68QM Scalpel GPU Profiler
        - 1.8.5.3 #DT7AR5 Pixel Program: Alpha
        - 1.8.5.4 #VD4449 Pixel Program: Alpha
          - 1.8.5.4.1 #9ZGYVL Alpha Testing Optimization:
          - 1.8.5.4.2 #NVPV6U Anti-Aliasing
        - 1.8.5.5 #RLC25Z Shading
        - 1.8.5.6 #28BQGN Pixel Program: G-Buffers
        - 1.8.5.7 #E4HXXB Shading
        - 1.8.5.8 #KEY88P Pixel Program: Vegetation Textures
        - 1.8.5.9 #ZJZ5VC Vegetation Textures:
        - 1.8.5.10 #AFKMK8 Shading
        - 1.8.5.11 #BS6F8P Pixel Program: Vegetation Textures
          - 1.8.5.11.1 #KZSEET Vegetation Textures:
          - 1.8.5.11.2 #2QAL8Y Get packed into:
        - 1.8.5.12 #V8LJSD Pixel Program: Normals
        - 1.8.5.13 #P9SP93 Pixel Program: Normals
        - 1.8.5.14 #49U8HT Pixel Program: Normals
        - 1.8.5.15 #EXJ6GV Pixel Program: Normals
        - 1.8.5.16 #QN8SA5 Pixel Program: Normals
        - 1.8.5.17 #RK5T3U Pixel Program: Normals
        - 1.8.5.18 #H4PWUB Pixel Program: Normals
        - 1.8.5.19 #EDGLAY Pixel Program: Albedo
        - 1.8.5.20 #XLT7XC Colorization Texture Array:
        - 1.8.5.21 #TS24R5 Pixel Program: Albedo
        - 1.8.5.22 #3JHMBP Pixel Program: Roughness / Reflectance
        - 1.8.5.23 #MXZG4T Pixel Program: Translucency
    - 1.9 #3EYJES Asset Creation
      - 1.9.1 #6HBR9J Asset Creation
      - 1.9.2 #K7UVRC Creation Process
        - 1.9.2.1 #B933Q9 Process
      - 1.9.3 #EPE7ES Creation Process
      - 1.9.4 #L28KHX Creation Process
      - 1.9.5 #TH3B3D Creation Process
        - 1.9.5.1 #U5GFWD Process
        - 1.9.5.2 #Y5VX3S Process
        - 1.9.5.3 #V6AJ9K Process
      - 1.9.6 #HCAQY2 Assets: Trees
      - 1.9.7 #LD3AX4 Assets: Plants
    - 1.10 #F4W7Y2 Shadow Casting
      - 1.10.1 #5LFPRD Shadow Casting
    - 1.11 #H5T7M2 Shadow Casting
      - 1.11.1 #R22YCC Shadow Casting Vegetation
    - 1.12 #KBMKEN Summary
      - 1.12.1 #LZ8AXU Summary
      - 1.12.2 #V4MS42 What worked for us?
    - 1.13 #VWFGJZ References

### 7. Tool result: get_document

DOCUMENT #EDURTK
Title: Real-Time GPU Tree Generation

Outline:
  - 1 #ST65WQ Real-Time GPU Tree Generation
    - 1.1 #3AF4HM Abstract
    - 1.2 #BGB37V CCS Concepts
    - 1.3 #PYAKW4 1. Introduction
    - 1.4 #ZYPVGN 2. Background
      - 1.4.1 #8AWK3J 2.1. The Weber-Penn Model
      - 1.4.2 #AR5QHU 2.2. Mesh Nodes
    - 1.5 #LMRE4T 3. The Tree Generation Work Graph
      - 1.5.1 #4NQTQP 3.1. Stem Level
      - 1.5.2 #5PR9YG 3.2. Stem Drawing
      - 1.5.3 #3JVLMA 3.3. Leaves
      - 1.5.4 #WP55PQ 3.4. Seasons
      - 1.5.5 #22ZJCT 3.5. Animation
      - 1.5.6 #MRQUQ2 3.6. Advanced Editing
      - 1.5.7 #EX3H73 3.7. Advanced Optimizations
      - 1.5.8 #TGWYY9 3.8. Pass Fusion for Deferred Shadow Mapping
      - 1.5.9 #9Q2R4Z 3.9. Automatic Level of Detail
    - 1.6 #GNUJ7A 4. Results and Discussion
    - 1.7 #CXP5S9 5. Conclusion and Future Work
    - 1.8 #RV794Z Acknowledgments
    - 1.9 #96FQ94 References

### 8. Tool result: get_document

DOCUMENT #QHMFH2
Title: Improved Alpha Testing Using Hashed Sampling

Outline:
  - 1 #44GBYC Improved Alpha Testing Using Hashed Sampling
    - 1.1 #42D4DE Abstract—
    - 1.2 #2WK4G6 1 INTRODUCTION
    - 1.3 #5QEEQJ 2 WHY DOES GEOMETRY DISAPPEAR?
    - 1.4 #53S2QB 3 STATE OF THE ART IN ALPHA TESTING
    - 1.5 #BTYG8Z 4 STOCHASTIC ALPHA TESTING
    - 1.6 #JRGDYC 5 HASHED ALPHA TESTING
      - 1.6.1 #984KZ6 5.1 Hash Function
      - 1.6.2 #VY22UQ 5.2 Anchoring Hashed Noise to Geometry
      - 1.6.3 #AVPY9H 5.3 Avoiding Correlations Between Layers
      - 1.6.4 #899GPH 5.4 Achieving Stable Pixel-Scale Noise
        - 1.6.4.1 #QXZKEC 5.4.1 Stability For Screen-Space Translations in X and Y
        - 1.6.4.2 #KFA6B4 5.4.2 Stability For Screen-Space Translations in Z
    - 1.7 #PBEZQQ 6 ANISOTROPIC HASHED ALPHA TESTING
      - 1.7.1 #BLTXA3 6.1 Difficulties Removing Anisotropy
      - 1.7.2 #726Q7U 6.2 Mitigating Anisotropy
    - 1.8 #3SCLT7 7 IMPLEMENTATION CONSIDERATIONS
      - 1.8.1 #YLJH9N 7.1 Fading in Noise with Distance
        - 1.8.1.1 #E5XEXG 7.1.1 Fading in Noise With Anisotropic Texture Sampling
      - 1.8.2 #7Z55V6 7.2 Hashed Alpha Testing With Temporal Antialiasing
        - 1.8.2.1 #5XTVGC 7.2.1 Temporal Antialiasing by Reducing Noise Scale
        - 1.8.2.2 #MSBL54 7.2.2 Temporal Antialiasing by Independent Hashing
        - 1.8.2.3 #2Q4G64 7.2.3 Temporal Antialiasing by Temporal Stratification
      - 1.8.3 #KFFJHQ 7.3 Using Premultiplied Alpha
    - 1.9 #ZJXSLW 8 APPLICATIONS TO ALPHA-TO-COVERAGE
      - 1.9.1 #KVJWMS 8.1 Applications to Screen Door Transparency
    - 1.10 #RU94D8 9 OTHER APPLICATIONS OF HASHED SAMPLING
    - 1.11 #DBB4PS 10 STOCHASTIC TRANSPARENCY COMPARISON
    - 1.12 #R5QF6K 11 RESULTS
    - 1.13 #ZW8XY9 12 CONCLUSIONS
    - 1.14 #PAWDPN ACKNOWLEDGMENTS
    - 1.15 #J7E8F8 REFERENCES

### 9. Tool result: get_document

DOCUMENT #BDBBL6
Title: Real‐time Realistic Rendering and Lighting of Forests

Outline:
  - 1 #XR7DQ8 Real-time Realistic Rendering and Lighting of Forests
    - 1.1 #KFK357 ► To cite this version:
  - 2 #ZX2JYE Real-time Realistic Rendering and Lighting of Forests
    - 2.1 #9S3JS2 Abstract
    - 2.2 #QREKH2 1. Introduction
    - 2.3 #4THWJB 2. Related work
    - 2.4 #RNMTHM 3. Strahler et al. model
    - 2.5 #Y8VXY9 4. Our Model
    - 2.6 #6MAATG 5. Nearest trees: representation and rendering
    - 2.7 #6DMZ4P 6. Farthest trees: representation and rendering
    - 2.8 #5KNTTV 7. Seamless transition between representations
    - 2.9 #387UFC 8. Implementation
    - 2.10 #2L89HX 9. Results and validation
    - 2.11 #ZX7AQX 10. Discussion
    - 2.12 #XZX56G 11. Conclusion
    - 2.13 #PYN6GH References

### 10. Tool result: get_document

GIT REPOSITORY #3CV92Z
Title: moppe

Outline:
  - /3CV92Z/source-directories/YXRlbGllcg atelier
  - /3CV92Z/source-directories/Y21ha2U cmake
  - /3CV92Z/source-directories/ZG9jcw docs
  - /3CV92Z/source-directories/ZXRhbG9u etalon
  - /3CV92Z/source-directories/aWRlYXM ideas
  - /3CV92Z/source-directories/bGF2b2ly lavoir
  - /3CV92Z/source-directories/bW9wcGU moppe
  - /3CV92Z/source-directories/cGxhbg plan
  - /3CV92Z/source-directories/cGxhbm5pbmc planning
  - /3CV92Z/source-directories/cmVzZWFyY2g research
  - /3CV92Z/source-directories/dGVzdHM tests
  - /3CV92Z/source-directories/dGhpcmRfcGFydHk third_party
  - /3CV92Z/source-directories/dG9vbHM tools
  - /3CV92Z/source-files/LmNsYW5nLWZvcm1hdA .clang-format
  - /3CV92Z/source-files/LmNsYW5nZA .clangd
  - /3CV92Z/source-files/LmdpdGF0dHJpYnV0ZXM .gitattributes
  - /3CV92Z/source-files/LmdpdGlnbm9yZQ .gitignore
  - /3CV92Z/source-files/QUdFTlRTLm1k AGENTS.md
  - /3CV92Z/source-files/Q0xBVURFLm1k CLAUDE.md
  - /3CV92Z/source-files/Q01ha2VMaXN0cy50eHQ CMakeLists.txt
  - /3CV92Z/source-files/Q01ha2VQcmVzZXRzLmpzb24 CMakePresets.json
  - /3CV92Z/source-files/TWFrZWZpbGU Makefile
  - /3CV92Z/source-files/UkVBRE1FLm1k README.md

### 11. Assistant tool calls

#### Tool call: `read`

```json
{
  "blocks": [
    "9Q2R4Z",
    "5KNTTV",
    "9S3JS2",
    "42D4DE"
  ],
  "expand": true
}
```

#### Tool call: `search_text`

```json
{
  "document_id": "66Q3W3",
  "limit": 8,
  "query": "grass blade level of detail transition distance"
}
```

#### Tool call: `search_text`

```json
{
  "document_id": "ABD2B8",
  "limit": 8,
  "query": "coverage preserving alpha mipmaps distance fade LOD"
}
```

### 12. Tool result: read

DOCUMENT #EDURTK
Real-Time GPU Tree Generation

SECTION #9Q2R4Z 3.9. Automatic Level of Detail

EXCERPT #2WBRVG p. 6
  Dynamic resolution scaling (DRS) [Bin11] is a common optimization for video games. Here, the rendering resolution is dynamically reduced for demanding scenes to meet a target frame time. However, sudden resolution changes are conspicuous and can break effects based on temporal accumulation. As our generation runs every frame and changing quality is continuous, we can dynamically adjust the geometric detail of our trees. For each quality parameter, like the pixels per triangle from Sec. 3.2 or the leaf density from Sec. 3.3, the user configures an acceptable range. The parameter ranges are then ordered by priority: the least noticeable parameter gets degraded first if the performance goal was not met in the last frame. If the performance is better than the goal, the parameters are reverted again. Constraining the maximum rate of change of the parameters assures that no sudden quality change occurs.

DOCUMENT #BDBBL6
Real‐time Realistic Rendering and Lighting of Forests

SECTION #5KNTTV 7. Seamless transition between representations

EXCERPT #DPJ27C p. 7
  A sudden switch from our z-field to our shader-map representation would be very noticeable, for several reasons. First, at large distances, the visual fidelity of our z-field representation decreases: the length \|\mathbf{p}_t - \mathbf{p}_v\| computed with our iterative algorithm becomes very imprecise, because then it is computed on very coarse MIP-mapped depth maps. Also, the exponential of this “average” length is not what we want, i.e. , the average of the exponential over the visible tree pixels. The cascaded shadow maps become also imprecise at this distance, as well as the occlusion effects. Second, although tree locations fit precisely between the 2 models, our

EXCERPT #HWH7E9 p. 7
  4 color components (tree vs ground, lit vs unlit) are computed on a statistical basis and thus cannot match a given tree instance. This would give a visible color discontinuity at the transition. Third, our shader-map model neglects forest thickness. Even if we use it when trees are at most 3 pixels tall, this would yield popping on the terrain silhouettes. To solve these problems, we propose a seamless transition scheme divided in three parts: one transition inside each representation, and a transition between them.

EXCERPT #D4D5AP p. 7
  Transition in z-field representation. Although the radiances I_t(\mathbf{p}_v) , J_t(\mathbf{p}_v) , I_g(\mathbf{x}) and J_g(\mathbf{x}) become imprecise in the distance, we know the values toward which they should converge. Indeed, we computed them in Section 6. Thus, to solve the problem, we simply force a transition of these values toward their expected average. Concretely, if s is the distance to the viewer, and s_{max} the maximal distance at which the z-field representation is used, then we render the trees and the ground with:

EXCERPT #W8VWLB p. 7
  \bar{I}_t(\mathbf{p}_v) \stackrel{\text{def}}{=} (1 - v) I_t(\mathbf{p}_v) + v \bar{I}_t \frac{k_t}{k_t + k_g} \quad (22)

EXCERPT #MGT5K7 p. 7
  \bar{J}_t(\mathbf{p}_v) \stackrel{\text{def}}{=} (1 - v) J_t(\mathbf{p}_v) + v \bar{J}_t \quad (23)

EXCERPT #DB2Q6T p. 7
  \bar{I}_g(\mathbf{x}) \stackrel{\text{def}}{=} (1 - v) I_g(\mathbf{x}) + v \bar{I}_g \frac{k_g}{k_g + k_t} \quad (24)

EXCERPT #6Z9HU4 p. 7
  \bar{J}_g(\mathbf{x}) \stackrel{\text{def}}{=} (1 - v) J_g(\mathbf{x}) + v \bar{J}_g \quad (25)

EXCERPT #2NR2WW p. 7
  where v = \text{smoothstep}(0, 0.8, s/s_{max}) .

EXCERPT #M4JWLE p. 7
  Transition in shader-map representation. Up to now, we have only used the coarse densities \Lambda_h(\mathbf{x}) in our forest model components k_g, k_{\bar{g}}, k_t, k_{\bar{t}} . This gives only an average forest radiance which cannot match the spatial variations obtained with our z-field model at the transition. Instead, we want the tree disks in the coverage channel \Gamma(\mathbf{x}) shaded only with the tree radiance components, and the ground between them only with the ground components. That is, we want to have k_g = k_{\bar{g}} = 0 when \Gamma(\mathbf{x}) = 1 . For this, we simply replace \Lambda_h(\mathbf{x}) with \Gamma(\mathbf{x})/A in Eqs. 14 and 15. Indeed, this changes nothing for really far trees (where \Gamma(\mathbf{x}) = \Lambda_h(\mathbf{x})A ), but gives k_g = k_{\bar{g}} = 0 inside distinguishable disks, as desired (provided we ensure that \mathbb{G}(\cdot, \cdot, \cdot, 1/A) = 0 ).

EXCERPT #X9PHPN p. 7
  Transition between representations. To avoid popping on the terrain silhouettes, we progressively fade out the trees rendered with our z-field tree representation. For this, we multiply the opacity \alpha_v with 1 - \text{smoothstep}(0.8, 1, s/s_{max}) , while replacing the ground radiance in this transition region with our forest radiance model, modified as follows:

EXCERPT #8SXDS4 p. 7
  L = (1 - \mu) \left[ \frac{k_g}{k_g + k_{\bar{g}}} \bar{I}_g + \bar{J}_g \right] + \mu \left[ \frac{k_t}{k_t + k_{\bar{t}}} \bar{I}_t + \bar{J}_t \right] \quad (26)

EXCERPT #WBFDNZ p. 7
  \mu = (1 - k_g - k_{\bar{g}}) \text{smoothstep}(0.8, 1, s/s_{max}) \quad (27)

EXCERPT #RVXBAH p. 7

EXCERPT #LYX7VD p. 7

EXCERPT #ZCHWTF p. 8

DOCUMENT #BDBBL6
Real‐time Realistic Rendering and Lighting of Forests

SECTION #9S3JS2 Abstract

EXCERPT #7R6ZNJ p. 1
  Realistic real-time rendering and lighting of forests is an important aspect for simulators and video games. This is a difficult problem, due to the massive amount of geometry: aerial forest views display millions of trees on a wide range of distances, from the camera to the horizon. Light interactions, whose effects are visible at all scales, are also a problem: sun and sky dome contributions, shadows between trees, inside trees, on the ground, and view-light masking correlations. In this paper we present a method to render very large forest scenes in real-time, with realistic lighting at all scales, and without popping nor aliasing. Our method is based on two new forest representations, z-fields and shader-maps, with a seamless transition between them. Our first model builds on light fields and height fields to represent and render the nearest trees individually, accounting for all lighting effects. Our second model is a location, view and light dependent shader mapped on the terrain, accounting for the cumulated subpixel effects. Qualitative comparisons with photos show that our method produces realistic results.

EXCERPT #7QAJTP p. 1
  Categories and Subject Descriptors (according to ACM CCS): Computer Graphics [I.3.7]: Three-Dimensional Graphics and Realism—

DOCUMENT #QHMFH2
Improved Alpha Testing Using Hashed Sampling

SECTION #42D4DE Abstract—

EXCERPT #DN4PKL p. 0
  We further describe and analyze the idea of hashed alpha testing from Wyman and McGuire [1], which builds on stochastic alpha testing and simplifies stochastic transparency. Typically, alpha testing provides a simple mechanism to mask out complex silhouettes using simple proxy geometry with applied alpha textures. While widely used, alpha testing has a long-standing problem: geometry can disappear entirely as alpha mapped polygons recede with distance. As foveated rendering for virtual reality spreads, this problem worsens as peripheral minification and prefiltering introduce this problem on nearby objects.

EXCERPT #N6QLYJ p. 0
  We first introduce the notion of stochastic alpha testing , which replaces a fixed alpha threshold of \alpha_\tau = 0.5 with a randomly chosen \alpha_\tau \in [0..1) . This entirely avoids the problem of disappearing alpha-tested geometry, but introduces temporal noise.

EXCERPT #S4F32B p. 0
  Hashed alpha testing uses a hash function to choose \alpha_\tau procedurally. With a good hash function and inputs, hashed alpha testing maintains distant geometry without introducing more temporal flicker than traditional alpha testing. We also describe how hashed alpha interacts with temporal antialiasing and applies to alpha-to-coverage and screen-door transparency. Because hashed alpha testing addresses alpha test aliasing by introducing stable sampling, it has implications in other domains where increased sample stability is desirable. We show how our hashed sampling might apply to other stochastic effects.

EXCERPT #7Y42YQ p. 0
  Index Terms —anisotropy, alpha map, alpha test, hash, hashed alpha test, stable shading, stochastic sampling.

### 13. Tool result: search_text

Exact matches

1. Source: Ghost of Tsushima: Procedural Grass (#66Q3W3), Eric Wohllaib, Sucker Punch Productions, p. 54
  Context:
    #W32QZT GHOSTOF TSUSHIMA
      #8N9Y6H ACKNOWLEDGEMENTS
  Matching excerpt #HV8F3A:
      https://outerra.blogspot.com/2012/05/procedural-grass-rendering.html

2. Source: Ghost of Tsushima: Procedural Grass (#66Q3W3), Eric Wohllaib, Sucker Punch Productions, p. 0
  Context:
    #W32QZT GHOSTOF TSUSHIMA
  Matching excerpt #X6UFR4:
      Procedural Grass

Approximate matches

1. Source: Ghost of Tsushima: Procedural Grass (#66Q3W3), Eric Wohllaib, Sucker Punch Productions, p. 54
  Context:
    #W32QZT GHOSTOF TSUSHIMA
      #8N9Y6H ACKNOWLEDGEMENTS
  Score: 0.03
  Related excerpt #HV8F3A:
      https://outerra.blogspot.com/2012/05/procedural-grass-rendering.html

### 14. Tool result: search_text

Exact matches

1. Source: Between Tech and Art: The Vegetation of Horizon Zero Dawn (#ABD2B8), Gilbert Sanders, Guerrilla Games, p. 29
  Context:
    #MFUGJF The Vegetation of Horizon Zero Dawn
      #CZ5EA6 Categories of Spring Settings:
        #FDNGMJ Grasses
          #FSRQ8J Vertex Programs: Making Grass Do More
  Matching excerpt #FH7HUR:
      Displacement = [Percentage of Object Height] based on Distance to Camera

2. Source: Between Tech and Art: The Vegetation of Horizon Zero Dawn (#ABD2B8), Gilbert Sanders, Guerrilla Games, p. 29
  Context:
    #MFUGJF The Vegetation of Horizon Zero Dawn
      #CZ5EA6 Categories of Spring Settings:
        #FDNGMJ Grasses
          #FSRQ8J Vertex Programs: Making Grass Do More
  Matching excerpt #UUL95W:
      Displacement = [Percentage of All Animation] based on Distance to Camera

3. Source: Between Tech and Art: The Vegetation of Horizon Zero Dawn (#ABD2B8), Gilbert Sanders, Guerrilla Games, p. 39
  Context:
    #MFUGJF The Vegetation of Horizon Zero Dawn
      #CZ5EA6 Categories of Spring Settings:
        #ZB9PQG Shading – Alpha
          #DT7AR5 Pixel Program: Alpha
  Matching excerpt #9QVCY3:
      Alpha Testing, Solution:

4. Source: Between Tech and Art: The Vegetation of Horizon Zero Dawn (#ABD2B8), Gilbert Sanders, Guerrilla Games, p. 35
  Context:
    #MFUGJF The Vegetation of Horizon Zero Dawn
      #CZ5EA6 Categories of Spring Settings:
        #ZB9PQG Shading – Alpha
          #JG85A7 Pixel Program: Alpha
  Matching excerpt #A7AJHE:
      Alpha Testing, Initial HZD setup:

5. Source: Between Tech and Art: The Vegetation of Horizon Zero Dawn (#ABD2B8), Gilbert Sanders, Guerrilla Games, p. 80
  Context:
    #MFUGJF The Vegetation of Horizon Zero Dawn
      #H5T7M2 Shadow Casting
  Matching excerpt #WQPVC4:
      Smooth Alpha Blended Crossfade between Cascades!

### 15. Assistant tool calls

#### Tool call: `read`

```json
{
  "blocks": [
    "5PUTKY",
    "NF4YZX",
    "FSRQ8J",
    "DT7AR5",
    "VD4449",
    "YLJH9N"
  ],
  "expand": true
}
```

#### Tool call: `search_text`

```json
{
  "document_id": "PQ68ZH",
  "limit": 6,
  "query": "level of detail distance blade density transition"
}
```

### 16. Tool result: read

DOCUMENT #66Q3W3
Ghost of Tsushima: Procedural Grass

SECTION #5PUTKY Placing a Grass Blade

EXCERPT #NH5PX4 p. 10
  • Turn lane ID into position on tile grid, jitter • Distance culling and frustum culling • Determine grass type and height from texture at position • Drop lanes that don't have grass • Occlusion culling

EXCERPT #LJ5QTJ p. 11
  A dense field of tall grass, viewed from a high angle. The foreground is dominated by a large, irregular patch of bright blue grass, which appears to be a different variety or perhaps dyed. This blue patch is surrounded by a thick carpet of green grass. The grass blades are long and thin, creating a textured, almost chaotic pattern. The lighting is even, highlighting the vibrant colors of the grass. A dense field of tall grass, with a large, irregular patch of bright blue grass in the foreground and surrounding areas of green grass.

EXCERPT #D99QNG p. 12
  A top-down view of a dense field of tall, thin grass. The majority of the grass is a vibrant yellow-green color. In the lower half of the image, there is a large, irregular patch of bright blue grass, which appears to be a different variety or perhaps a digital overlay. The grass blades are long and slender, creating a textured, almost chaotic pattern. The background grass is slightly more out of focus than the foreground patch. A dense field of tall grass with a large patch of blue grass in the foreground.

EXCERPT #3WMTH3 p. 13

EXCERPT #8M3FAF p. 13

DOCUMENT #66Q3W3
Ghost of Tsushima: Procedural Grass

SECTION #NF4YZX Generating a Grass Blade

EXCERPT #66DTCB p. 13
  • Position (3 floats) • Facing (2 floats) • Wind strength at position • Per-blade Hash • Grass Type • Clump facing (2 floats) • Clump color • Height • Width • Tilt • Bend • Side Curve

EXCERPT #QAQ7HN p. 14

EXCERPT #N76PZQ p. 14
  A vibrant, high-resolution digital landscape featuring a dense field of tall, green grass in the foreground. The grass blades are long and thin, with a slight wave to them, suggesting a gentle breeze. In the middle ground, a cluster of large, grey, craggy rocks sits on a slight rise. To the left of the rocks, a small, dark, rectangular structure, possibly a gazebo or a small building, is partially visible. The background is filled with a dense forest of tall, thin trees with green foliage. The sky is a clear, bright blue. The overall scene is a peaceful, naturalistic environment, likely a screenshot from a video game or a digital art rendering. A lush green field of tall grass with large grey rocks and a forest in the background.

EXCERPT #LVWCEP p. 15

EXCERPT #58UR78 p. 15

EXCERPT #4DUMUU p. 15
  The diagram shows a 2D grid with x and y axes ranging from 0 to 3. A central point at (1, 1) is labeled 'sample point'. Several arrows originate from this point and point to various colored dots scattered across the grid, representing different data samples or textures being accessed. The dots are colored blue, green, red, and purple. The arrows indicate the direction of sampling or data flow. Sample Color Approximate Coordinates (x, y) Blue (0.5, 2.5) Green (1.2, 2.5) Red (0.8, 0.8) Purple (1.8, 1.8) Red (2.3, 1.8) Purple (2.8, 2.2) Blue (1.8, 0.5) Green (2.8, 0.5) A 2D grid diagram illustrating a compute shader's sampling process. The grid has x and y axes from 0 to 3. A 'sample point' is marked at (1, 1). Arrows point from the sample point to various colored dots (blue, green, red, purple) scattered across the grid, representing different data samples or textures being accessed.

EXCERPT #L92SFN p. 16

EXCERPT #JS5SQP p. 16
  A vibrant, high-resolution digital landscape. The foreground is dominated by a dense field of tall, green grass that appears to be blowing in a breeze, creating a sense of movement. To the left, a cluster of large, grey, craggy rocks sits on a slight incline. A small, light-colored path or stream bed winds through the rocks and grass. In the background, a dense forest of tall, thin trees with green foliage stretches across the horizon. The sky is a clear, bright blue. The overall scene is a lush, naturalistic environment, likely a render from a video game or a digital art piece. A lush, green landscape with tall grass, large rocks, and a forest in the background.

EXCERPT #EW3XXE p. 17

EXCERPT #CPBZBW p. 17
  A vibrant, high-resolution digital landscape. The foreground is dominated by a dense field of tall, green grass that appears to be blowing in a breeze, creating a sense of movement. In the middle ground, several large, grey, craggy rocks are scattered across the terrain. To the left, a small, dark wooden structure, possibly a gazebo or a small shrine, is partially visible among the trees. The background is filled with a dense forest of tall, thin trees with green foliage. In the far distance, a mountain peak is visible under a clear blue sky. The overall scene is bright and sunny, with strong shadows cast by the rocks and trees. A lush, green landscape with tall grass, large rocks, and a forest in the background.

EXCERPT #6V8FY2 p. 18

EXCERPT #NCJDGQ p. 18
  A vibrant, high-resolution digital landscape. The foreground is dominated by a dense field of tall, green grass that appears to be blowing in a breeze, creating a sense of movement. To the left, a cluster of large, grey, craggy rocks sits on a slight incline. In the background, a dense forest of tall, thin trees with green foliage stretches across the horizon under a clear blue sky. A small, dark wooden structure, possibly a gazebo or a small building, is visible among the trees on the left. The overall scene is bright and sunny, with strong shadows cast by the rocks and trees. A lush, green landscape with tall grass, large rocks, and a forest in the background.

EXCERPT #8JAG9C p. 19

EXCERPT #AFGG6H p. 19
  A vibrant, high-resolution digital landscape. The foreground is dominated by a dense field of tall, green grass that appears to be blowing in a gentle breeze. To the left, a cluster of large, grey, textured rocks sits on a slight incline. A small, dark wooden structure, possibly a gazebo or a small shrine, is partially visible behind the rocks. In the background, a dense forest of tall, thin trees with green foliage stretches across the horizon. The sky is a clear, bright blue. The overall scene is a peaceful, naturalistic environment, likely a screenshot from a video game or a digital art render. A lush, green landscape with tall grass, large rocks, and a forest in the background.

EXCERPT #H5WQSG p. 20

EXCERPT #FH36C7 p. 20
  A vibrant, high-resolution digital landscape. The foreground is dominated by a dense field of tall, green grass that appears to be blowing in a breeze, creating a sense of movement. To the left, a cluster of large, grey, craggy rocks sits on a slightly elevated patch of land. A small, light-colored path or stream bed winds through the rocks and grass. In the background, a dense forest of tall, thin trees with green foliage stretches across the horizon. The sky is a clear, bright blue. The overall scene is a lush, naturalistic environment, likely a screenshot from a video game or a high-quality digital rendering. A lush, green landscape with tall grass, large rocks, and a forest in the background.

EXCERPT #NBQQDS p. 21

EXCERPT #2BM5VF p. 21

EXCERPT #VF26AF p. 21
  graph LR; C1[compute 1] --> BC[blade count]; BC --> C2[compute 2]; C2 --> IDA[indirect draw args]; C1 --> ID[instance data]; IDA --> V[vertex]; ID --> V; V --> P[pixel]; The diagram illustrates a data pipeline with the following components and flow: compute 1 (blue box) is the starting point. compute 1 outputs to blade count (gray box). blade count outputs to compute 2 (blue box). compute 2 outputs to indirect draw args (gray box). compute 1 also outputs directly to instance data (gray box). indirect draw args outputs to vertex (green box). instance data outputs to vertex (green box). vertex outputs to pixel (orange box). A data pipeline diagram showing the flow from compute 1 to pixel.

SECTION #YVU33M DATA PIPELINE

EXCERPT #E5W76B p. 22

EXCERPT #WFYYXB p. 22
  The diagram illustrates a data pipeline architecture. On the left, there are two vertical gray boxes, each labeled "instance data buffer". Arrows from these buffers feed into a series of four horizontal processing blocks. Each block is composed of four colored segments: a blue segment labeled "compute 1", a blue segment labeled "compute 2", a green segment labeled "vertex", and an orange segment labeled "pixel". The first block is connected to the top "instance data buffer". The second block is connected to the bottom "instance data buffer". The third block is connected to the top "instance data buffer". The fourth block is connected to the bottom "instance data buffer". The pipeline shows a staggered flow where data from both buffers is processed in parallel through the same sequence of stages. Diagram of a data pipeline showing two instance data buffers feeding into a sequence of compute and vertex/pixel stages.

EXCERPT #B7ZAVL p. 23

EXCERPT #M64NLC p. 23

EXCERPT #4TS8SV p. 23
  High LOD 15 vertices

EXCERPT #P4SXS7 p. 23
  A tall, thin, black wireframe tower structure standing on a green grassy field. The tower is composed of many small, interconnected triangles, creating a smooth, tapering silhouette. The background is a blurred, light-colored landscape. High LOD tower model

EXCERPT #AYX9GT p. 23
  Low LOD 7 vertices

EXCERPT #36KSRJ p. 23
  A tall, thin, black wireframe tower structure standing on a green grassy field. The tower is composed of a few large, interconnected triangles, creating a simplified, blocky silhouette. The background is a blurred, light-colored landscape. Low LOD tower model

EXCERPT #UXZ3HJ p. 24
  A tall, thin, black metal structure, possibly a marker or a small tower, stands vertically in the center of a field. The structure is composed of a central vertical pole with several horizontal rungs and diagonal bracing, forming a series of triangles. The ground is covered in green grass, interspersed with patches of dry, brownish-yellow grass and scattered dry leaves. The lighting is bright, casting a soft shadow of the structure onto the ground to its right. A tall, thin, black metal structure standing in a field of green grass and dry leaves.

EXCERPT #XJ4D3S p. 25

EXCERPT #LCYKS4 p. 25

EXCERPT #SZDQPA p. 25
  A diagram illustrating a vertex shader input. It consists of a 2x2 grid of blue squares. Each square contains a black dot in the bottom-left corner and a red 'X' in the top-right corner. This represents a 2x2 grid of vertices, where each vertex is defined by its position (black dot) and a texture coordinate (red 'X'). A 2x2 grid of blue squares, each containing a black dot and a red 'X'.

EXCERPT #XGLVH9 p. 25
  A diagram illustrating a vertex shader output. It consists of a single teal square. Inside the square, there are four black dots arranged in a 2x2 grid, representing the output vertices. This represents a single vertex, where the position is defined by the black dot. A single teal square containing four black dots.

EXCERPT #K5C9U9 p. 26

EXCERPT #87ZHJW p. 26

EXCERPT #6HZFWE p. 26
  High LOD 15 vertices

EXCERPT #BFBP58 p. 26
  A wireframe rendering of a sword hilt, showing a complex structure with 15 vertices. The hilt is positioned on a grassy ground. The wireframe is composed of white and black lines, highlighting the detailed geometry of the pommel, guard, and crossguard. High LOD wireframe model of a sword hilt.

EXCERPT #XDWR9Q p. 26
  Low LOD 7 vertices

EXCERPT #XS87D8 p. 26
  A wireframe rendering of the same sword hilt, but with a simplified geometry using only 7 vertices. The structure is much less detailed, appearing as a basic outline of the hilt's main components. The background is the same grassy ground. Low LOD wireframe model of a sword hilt.

EXCERPT #T7D67G p. 27

EXCERPT #8SW2UB p. 27

SECTION #M2AA9T Cubic Bezier curve

EXCERPT #DJSHKW p. 27
  • Position easy to calculate • Derivative easy to calculate • Use derivative to find normal • Moving control points changes blade shape • Useful for animation • Useful for varying appearance of grass

EXCERPT #8XDCU9 p. 28

EXCERPT #CKZ4TE p. 28

EXCERPT #WVHCE7 p. 28
  A graph illustrating a curve controlled by bend. The x-axis ranges from 0 to 10, and the y-axis ranges from 0 to 8. A blue curve starts at the origin (0,0) and ends at approximately (9, 9). A black dot marks the midpoint of the curve at approximately (3, 8.5), labeled "midpoint". An orange arrow points from the text "controlled by bend" to the curve, indicating the parameter that controls the curve's shape. x y 0 0 2 4 4 6.5 6 8 8 9 9 9 A graph illustrating a curve controlled by bend, with a midpoint marked.

EXCERPT #DCBZ7Z p. 29

EXCERPT #4F7CHH p. 29

EXCERPT #M4XPGF p. 29
  • Evaluate Bezier curve • Get normal orthogonal to facing • Step vertex in width direction • Evaluate Bezier derivative curve

EXCERPT #6BQUET p. 29
  The image shows a 3D perspective view of a textured surface, possibly a ground plane with a green and brown pattern. A black Bezier curve is drawn on the surface. A yellow arrow labeled "surface normal" points upwards from a vertex on the curve. A grey arrow labeled "Bezier derivative" points along the curve, indicating the direction of the derivative at that point. The curve is composed of several segments, and the derivative arrow is shown at one of the vertices. A 3D visualization of a Bezier curve on a textured surface, illustrating the calculation of a surface normal and a Bezier derivative.

EXCERPT #FZQAKK p. 30
  This image displays a 3D visualization of a point cloud or a mesh, likely generated from a depth sensor like a LiDAR scanner. The surface is represented by a grid of points or small squares, each colored according to its vertical position (height or depth). The color scheme follows a gradient: blue and purple for the lowest points, transitioning through green and yellow to red and orange for the highest points. The terrain features a prominent central depression or valley, with a small, dark, vertical object standing within it. The background is dark, suggesting a night-time scan or a lack of ambient light, with some faint outlines of trees or structures visible in the distance. A 3D visualization of a point cloud or mesh, colored by height or depth, showing a grid-like structure with a central depression.

EXCERPT #GZNPFP p. 32

EXCERPT #Q84AVP p. 32

EXCERPT #VLBK59 p. 32
  Flat normals

EXCERPT #B4X95N p. 32
  A blue sword is shown against a background of a colorful, noisy texture. The sword's surface is smooth and uniform in color, indicating that the normals are flat and do not vary across the surface. A blue sword with flat normals.

EXCERPT #WRDVWY p. 32
  Rounded normals

EXCERPT #HCM4VS p. 32
  A blue sword is shown against a background of a colorful, noisy texture. The sword's surface is smooth and uniform in color, indicating that the normals are rounded and do not vary across the surface. A blue sword with rounded normals.

EXCERPT #D22JJJ p. 33
  A low-angle, close-up shot of a vast field of tall, vibrant green grass. The blades are long and slender, some standing upright while others lean over, creating a dense, textured appearance. The grass fills the foreground and middle ground, extending to a flat horizon line. Above the horizon, the sky is a clear, uniform light blue, occupying the upper half of the frame. A single, long blade of grass is prominently featured in the upper left foreground, extending diagonally across the frame. The lighting is bright and even, suggesting a sunny day, with no visible shadows or clouds. A field of tall green grass under a clear blue sky.

EXCERPT #6SHMVA p. 34
  A low-angle, close-up view of a cornfield. The foreground is dominated by large, vibrant green corn leaves, some of which are slightly out of focus. The plants are densely packed, and their long, pointed leaves create a sense of depth and texture. In the background, the field stretches out to a flat horizon under a clear, bright blue sky. The lighting is bright and even, suggesting a sunny day. The overall composition emphasizes the lushness and growth of the crops. A dense field of green corn plants under a clear blue sky.

EXCERPT #27ESYZ p. 35
  A wide-angle photograph of a vast, flat agricultural field. The foreground and middle ground are filled with dense, green crops, likely corn or a similar grain, growing in neat rows. The plants are vibrant green and appear to be in the early stages of growth. The field extends to a straight, level horizon line. Above the horizon, the sky is a clear, pale blue, with a slight gradient from a lighter hue near the horizon to a slightly deeper blue at the top. The overall scene is bright and open, suggesting a sunny day in a rural or agricultural setting. A vast, flat field of green crops under a clear blue sky.

EXCERPT #MQX43N p. 36
  A wide-angle photograph of a vast, flat landscape covered in dense green vegetation, likely a field of tall grass or reeds. The vegetation is lush and green, with individual blades visible in the foreground. The field extends to a flat horizon line under a clear, pale blue sky. The lighting is bright and even, suggesting a sunny day. The overall composition is simple and emphasizes the expanse and uniformity of the natural environment. A vast, flat landscape covered in dense green vegetation under a clear blue sky.

EXCERPT #2H4SQN p. 37

EXCERPT #ZGFCXA p. 37

EXCERPT #Q3PEUX p. 37
  • Output material data to G buffers • Gloss- 1D texture • Diffuse- Two textures • 1D texture for vein • 2D texture for color and alternate colors • Translucency- Constant value • AO- Constant value

EXCERPT #ZLKMHB p. 37
  A vertical rectangular inset image showing a close-up of a textured surface, likely a tree trunk or bark. The texture is detailed with various shades of brown and tan, showing natural patterns and imperfections. A bright white vertical highlight or scratch runs down the center of the image, creating a strong contrast with the darker, textured background. A vertical rectangular inset showing a close-up of a textured surface, likely a tree trunk or bark, with a bright white vertical highlight or scratch running down the center.

EXCERPT #48UVGV p. 38
  A dense field of tall green grass with white feathery seed heads, likely a type of ornamental grass, growing in a field. The grass is illuminated by bright sunlight, creating strong highlights and shadows. The foreground shows the lower green blades of the grass, while the background is filled with the white seed heads. The lighting is bright, suggesting a sunny day, and the overall scene is a lush, natural landscape. A dense field of tall green grass with white feathery seed heads, likely a type of ornamental grass, growing in a field. The grass is illuminated by bright sunlight, creating strong highlights and shadows.

EXCERPT #XGFYHB p. 39
  A vibrant, high-quality digital landscape scene. The foreground is dominated by a vast field of white, daisy-like flowers with green foliage, stretching across the lower two-thirds of the frame. A narrow, light-brown dirt path winds through the field, starting from the left and curving towards the center. To the left of the path, a calm blue lake reflects the sky, bordered by a dense line of trees with bright yellow foliage. To the right, a lush green forest of tall, leafy trees stands on a gentle slope. In the background, a rocky, mountainous peak rises against a clear blue sky with a few wispy clouds. The overall scene is bright and colorful, suggesting a sunny day in a fantastical or idealized natural setting. A vibrant landscape scene featuring a field of white flowers in the foreground, a winding path, a lake, and a forest of yellow and green trees under a blue sky.

EXCERPT #FL25VE p. 40
  A screenshot of a rural landscape, likely from a video game. The foreground and middle ground are dominated by a large field of harvested rice stalks, arranged in neat rows. Several large, conical haystacks are scattered throughout the field. In the lower-left foreground, two white geese are visible, one standing and one partially submerged in a small pool of water. To the right, there is a dense, green vine-covered wall or fence. In the background, a dense forest of tall, thin trees (possibly bamboo or birch) stretches across the horizon. A small, white shrine-like structure with a thatched roof is visible on the left side of the background. Further back, a few traditional houses with thatched roofs are nestled on a hillside. The sky is clear and blue, with a few wispy clouds. The overall scene depicts a peaceful, rural setting during the harvest season. A screenshot of a rural landscape featuring a large field of harvested rice stalks arranged in rows. Several large, conical haystacks are scattered throughout the field. In the foreground, two white geese are visible. The background shows a dense forest of tall, thin trees, a small shrine-like structure, and a few traditional houses on a hillside under a clear blue sky.

EXCERPT #X6GRWL p. 41
  An aerial view of a lush, rocky landscape. A river flows through the center, surrounded by dense green forests and rocky terrain. In the foreground, a sandy beach meets the water, with three small, simple huts or structures built near the shore. The background features more rugged, rocky hills and a small, distant settlement or village. The scene is bathed in bright sunlight, creating strong shadows and highlighting the textures of the rocks, sand, and foliage. Aerial view of a lush, rocky landscape with a river, forest, and a small settlement on a sandy beach.

EXCERPT #KHL9MZ p. 42
  A samurai, seen from behind, stands in a field of tall green grass and white flowers. He is wearing dark armor with red lacing and a shoulder guard. He holds a sword in his right hand. The landscape is a vast field of white flowers, possibly a rice paddy, with a river flowing through it. In the background, there is a dense forest of green trees and a large, rocky mountain under a blue sky with some clouds. A small bridge is visible on the right side of the river. A samurai standing in a field of white flowers, looking out over a river and a forested landscape.

EXCERPT #HLWD6P p. 43
  This is a digital landscape rendering, likely from a video game. The scene is set in a coastal or marshy area. In the foreground, a dirt path leads from the bottom center towards the middle ground. The path is flanked by dense, tall grass with long, thin blades. The grass on the left has white, feathery seed heads, while the grass on the right is mostly green. The path itself is brown and shows some signs of wear and small puddles. In the middle ground, the path continues towards a body of water. On the far side of the water, there is a harbor or port area. Several large, traditional-looking sailing ships with multiple masts and sails are docked or anchored. The ships have a reddish-brown hull and white sails. In the background, there are more ships and some distant landmasses or mountains under a clear blue sky with a few wispy clouds. The overall lighting is bright, suggesting a sunny day. The image has a slightly stylized, high-quality digital art feel. A digital landscape rendering featuring a dirt path, tall grass, and a distant harbor with ships.

EXCERPT #PBBQBP p. 44
  A black and white photograph capturing a dense thicket of tall, slender grasses or reeds. The plants are clustered together, with their long, narrow leaves creating a complex, layered texture. The foreground is dominated by a large, bright white area, likely due to overexposure, which obscures the lower portions of the vegetation. The background also features a bright, washed-out section, suggesting a continuation of the field or a distant horizon. The overall composition emphasizes the verticality and density of the natural growth. A black and white photograph of a dense patch of tall grass or reeds, with a bright, overexposed area in the foreground and background.

EXCERPT #KDGDWP p. 45
  A black and white photograph capturing a dense cluster of tall, slender grasses or reeds. The plants are positioned on the left side of the frame, leaning slightly towards the right. Their long, narrow leaves create a complex, layered texture. The base of the plants meets a light-colored, sandy surface, which occupies the lower right portion of the image. Sharp, dark shadows are cast from the base of the grasses onto the sand, indicating a strong light source from the upper left. The background is a bright, featureless white, likely representing a body of water or a very bright sky, which contrasts sharply with the dark foliage and shadows. Black and white photograph of a dense clump of tall, thin grasses or reeds growing along a sandy bank, with shadows cast onto the sand.

EXCERPT #NCLCK5 p. 46
  A close-up, low-angle shot of a dense patch of green grass growing on a sandy, textured ground. The grass blades are long, narrow, and vibrant green, with some showing signs of being cut or broken. They are growing in a somewhat uniform, dense clump. The ground is a mix of light brown and tan sand, with visible ripples and small indentations, suggesting it might be a beach or a dune area. The lighting is bright, casting soft shadows from the grass onto the sand. The overall composition is simple, focusing on the texture and color of the natural elements. A close-up view of a dense patch of green grass growing on a sandy, textured ground.

EXCERPT #UZUZUU p. 47
  A scenic landscape featuring a river, a bridge, and a large, mossy rock formation in the foreground. The foreground is dominated by a large, light-colored rock formation with a flat top covered in green moss and patches of dry grass. The rock face is steep and shows signs of weathering. To the left, a river flows, its water reflecting the sky. In the background, a stone bridge with multiple arches spans the river. The surrounding area is lush with green grass and some trees, with shadows cast across the mossy rock surface. A scenic landscape featuring a river, a bridge, and a large, mossy rock formation in the foreground.

EXCERPT #L758GM p. 48
  This image captures a vibrant forest floor scene. The foreground and middle ground are dominated by a thick, undulating carpet of green ferns, their fronds creating a complex, textured pattern. Interspersed among the ferns are patches of reddish-brown soil and smaller, low-lying plants. In the lower-left corner, a clump of tall, thin grasses adds to the variety of vegetation. To the right, a large, weathered log lies horizontally across the fern-covered slope. The background is filled with tall, slender trees with light-colored bark, their trunks rising vertically towards a canopy of green leaves. Sunlight filters through the trees, casting soft, dappled shadows across the ferns and the forest floor, creating a sense of depth and tranquility. The overall color palette is rich with various shades of green, accented by the earthy reds of the soil and the warm tones of the tree trunks. A lush forest scene featuring a dense carpet of green ferns and other vegetation covering a sloping ground. Tall, slender trees stand in the background, and a fallen log lies on the right side. The scene is bathed in soft, dappled sunlight.

EXCERPT #ZWFGE5 p. 49

EXCERPT #HC8DCU p. 49

EXCERPT #QV9BGD p. 49
  A diagram consisting of a 2x4 grid of blue squares. Each square contains a single black dot in its center. The grid is divided into two columns and two rows by thin black lines. A 2x4 grid of blue squares, each containing a black dot.

EXCERPT #NRJDU9 p. 49
  A diagram consisting of a single large teal square. Inside the square, there are four black dots arranged in a 2x2 grid pattern, corresponding to the positions of the dots in the blue grid to its left. A large teal square containing four black dots.

EXCERPT #FYZTEA p. 50
  A digital rendering of a misty forest landscape. In the foreground, a lush green hillside covered in tall grass slopes upwards from the bottom left towards the center. Several tall, slender pine trees are scattered across the hillside, some standing alone and others in small groups. A thick layer of white mist or fog flows through the valley between the hills, partially obscuring the trees in the distance. In the background, more forested hills are visible under a pale, overcast sky. The overall atmosphere is serene and ethereal, with soft lighting and a cool color palette dominated by greens and blues. A misty forest landscape with tall pine trees and a grassy hillside.

EXCERPT #N9ZPJC p. 51
  THANKS

EXCERPT #6WTTVA p. 51
  Questions? @ericwoh

EXCERPT #GVY3YT p. 51
  A small red logo located in the bottom right corner of the slide. It consists of the letters 'SP' in a white, bold, sans-serif font, enclosed within a red rounded square border. Small red logo with the letters 'SP' inside a rounded square.

DOCUMENT #ABD2B8
Between Tech and Art: The Vegetation of Horizon Zero Dawn

SECTION #FSRQ8J Vertex Programs: Making Grass Do More

EXCERPT #JUVVDE p. 29
  • To facilitate better Lodding, over distance :

EXCERPT #8VWZJV p. 29
  • we scale the whole animation part down

EXCERPT #UUL95W p. 29
  Displacement = [Percentage of All Animation] based on Distance to Camera

EXCERPT #3YLA7U p. 29
  • we vertically push the vertices of the mesh down

EXCERPT #FH7HUR p. 29
  Displacement = [Percentage of Object Height] based on Distance to Camera

EXCERPT #G2PWTE p. 29
  A close-up, high-resolution view of a grassy field, showing individual blades of grass in detail. The grass is green and appears to be blowing in the wind, creating a sense of movement and texture. The image is positioned on the right side of the slide, partially overlapping the white background.

EXCERPT #HBCRDY p. 29

EXCERPT #M7EVDE p. 30
  The Guerrilla Games logo, a stylized 'G' composed of three chevron-like shapes, is located in the top-left corner of the slide. Guerrilla Games logo

EXCERPT #YEU6BU p. 30
  A screenshot from the video game Horizon Zero Dawn showing a dense forest. Tall, slender trees with thick, textured bark and lush green foliage dominate the scene. Sunlight filters through the canopy, creating dappled light and long shadows on the forest floor. The image is overlaid with a faint, light-gray triangular grid pattern. Forest scene from Horizon Zero Dawn

DOCUMENT #ABD2B8
Between Tech and Art: The Vegetation of Horizon Zero Dawn

SECTION #DT7AR5 Pixel Program: Alpha

EXCERPT #9QVCY3 p. 39
  Alpha Testing, Solution:

EXCERPT #WZDZZW p. 39
  • No more adjusting the Alpha in the Shader! • 'Custom Coverage' Algorithm to build a better Mip Chain: • Calculate coverage of the input image (after alpha testing). • Generate a regular Mip-chain. • For each Mip: • Generate a histogram of this Mip after bilinear up sampling. In our case to 4096x4096. • Find the point P in the histogram that corresponds to the original coverage. • Scale the pixels by 0.5 / P so that P ends up at our alpha testing value of 0.5.

EXCERPT #ER5AAV p. 39
  A 3D rendered bush with green leaves and a shadow, illustrating the result of the alpha testing solution. A 3D rendered bush with green leaves and a shadow, illustrating the result of the alpha testing solution.

EXCERPT #PCH5BL p. 39
  A grayscale image showing a complex, branching pattern, likely representing a signed distance alpha texture. A grayscale image showing a complex, branching pattern, likely representing a signed distance alpha texture.

EXCERPT #AJTT4N p. 39
  SIGNED DISTANCE ALPHA TEXTURE

EXCERPT #PH7Y8F p. 39

EXCERPT #KQE9TX p. 40
  A horizontal sequence of six images showing a mipmap chain. The first image on the left is a high-resolution, detailed grayscale render of a dense, branching plant structure. As the images progress from left to right, the resolution decreases, showing increasing levels of blurring and loss of fine detail, which is characteristic of a standard mipmap chain. Regular MIP-CHAIN of our SDF ALPHA

EXCERPT #CXEQXS p. 40
  Regular MIP-CHAIN of our SDF ALPHA

EXCERPT #YBUYCJ p. 40
  A horizontal sequence of six images showing a mipmap chain after a thresholding operation. The first image on the left is a high-resolution, detailed white-on-black render of the same plant structure. As the images progress from left to right, the resolution decreases. However, the images remain significantly sharper than the standard mipmap chain, with the white structures of the plant clearly visible against the black background even at lower resolutions, indicating that the thresholding operation has preserved the binary information. Regular MIP-CHAIN of our SDF ALPHA after THRESHOLD = 0.5

EXCERPT #7V45VX p. 40
  Regular MIP-CHAIN of our SDF ALPHA after THRESHOLD = 0.5

EXCERPT #JLANCQ p. 40

EXCERPT #9E22KC p. 41
  A horizontal sequence of six grayscale images showing a mipmap chain. The first image is a high-resolution, detailed view of a dense, branching plant structure. As the images progress from left to right, the resolution decreases, showing increasing levels of blurring and loss of fine detail, characteristic of a standard mipmap chain. Regular MIP-CHAIN of our SDF ALPHA

EXCERPT #RFZ6UW p. 41
  Regular MIP-CHAIN of our SDF ALPHA

EXCERPT #H9X85E p. 41
  A horizontal sequence of six grayscale images showing a mipmap chain after a threshold operation. The first image is a high-resolution, detailed view of a dense, branching plant structure. As the images progress from left to right, the resolution decreases, showing increasing levels of blurring and loss of fine detail, characteristic of a standard mipmap chain. Regular MIP-CHAIN of our SDF ALPHA after THRESHOLD = 0.5

EXCERPT #C7CCAN p. 41
  Regular MIP-CHAIN of our SDF ALPHA after THRESHOLD = 0.5

EXCERPT #2B8JMA p. 41
  A horizontal sequence of six grayscale images showing a scaled mipmap chain. The first image is a high-resolution, detailed view of a dense, branching plant structure. As the images progress from left to right, the resolution decreases, showing increasing levels of blurring and loss of fine detail, characteristic of a standard mipmap chain. Scaled MIP-CHAIN of our SDF ALPHA

EXCERPT #2RT5DK p. 41
  Scaled MIP-CHAIN of our SDF ALPHA

EXCERPT #UV5AQT p. 41
  A horizontal sequence of six grayscale images showing a scaled mipmap chain after a threshold operation. The first image is a high-resolution, detailed view of a dense, branching plant structure. As the images progress from left to right, the resolution decreases, showing increasing levels of blurring and loss of fine detail, characteristic of a standard mipmap chain. Scaled MIP-CHAIN of our SDF ALPHA after THRESHOLD = 0.5

EXCERPT #549A5B p. 41
  Scaled MIP-CHAIN of our SDF ALPHA after THRESHOLD = 0.5

EXCERPT #4ZR3R7 p. 41

EXCERPT #BBWBA3 p. 42
  Shading

DOCUMENT #ABD2B8
Between Tech and Art: The Vegetation of Horizon Zero Dawn

SECTION #VD4449 Pixel Program: Alpha

SECTION #9ZGYVL Alpha Testing Optimization:

EXCERPT #ZR6WEZ p. 42
  • Make the Alpha fit in Cache! • In our case, Small Assets and Grass

EXCERPT #EWYQCH p. 42
  A small, square texture image showing a repeating pattern of stylized grass blades. The pattern is composed of dark, wavy lines on a lighter background, creating a dense, textured appearance. A 256x128 pixel Alpha Texture showing a repeating pattern of stylized grass blades.

EXCERPT #HS6SR6 p. 42
  256x128 pixel Alpha Texture

EXCERPT #YL7A9H p. 42
  • Need really good Anti-Aliasing!

EXCERPT #GFZ5JX p. 42
  A large, detailed view of a grassy field, showing the result of the Alpha Testing Optimization and Anti-Aliasing. The field is composed of many small, stylized grass blades, creating a dense, textured appearance. The grass is green with some brown patches, and the overall effect is a realistic-looking field of grass. A large, detailed view of a grassy field, showing the result of the Alpha Testing Optimization and Anti-Aliasing.

EXCERPT #FKLNYF p. 42

EXCERPT #82G9U2 p. 43

SECTION #NVPV6U Anti-Aliasing

EXCERPT #YYEPBW p. 43
  SIGGRAPH 2017

EXCERPT #F3YDTQ p. 43
  Giliam de Carpentier - Guerrilla Games

EXCERPT #WKT5L2 p. 43
  Kohei Ishiyama - Kojima Productions

EXCERPT #9V3LZS p. 43
  Decima Engine: Advances in Lighting and AA

EXCERPT #EK7L4Z p. 43
  • Typically 4 Samples in total • Always two Samples per rendered pixel per frame • At most 1 millisecond per frame at 1080 / PS4

EXCERPT #XJRFBN p. 43

EXCERPT #4J53SZ p. 44
  Stylized logo consisting of two white chevrons pointing right on a black background.

EXCERPT #L72URW p. 44
  A composite image showing various stages of vegetation shading in a winter forest. The main image is a realistic scene with snow-covered trees and ground. Overlaid on the right are several semi-transparent rectangular panels showing different rendering techniques: a grayscale depth map, a wireframe view of the trees, a color-coded normal map, and a wireframe view of the ground vegetation.

DOCUMENT #QHMFH2
Improved Alpha Testing Using Hashed Sampling

SECTION #YLJH9N 7.1 Fading in Noise with Distance

EXCERPT #9GA5KM p. 5
  Because of hashed alpha testing's basis in stochastic sampling, it introduces (stable) noise everywhere alpha-tested geometry is used. Developers may want to avoid introducing apparent randomness near the viewer, where existing alpha testing works fairly well, and focus on improving visual quality in the distance.

EXCERPT #RLHEVJ p. 5
  Fortunately, we can fade in hashed noise with distance. Consider the following formulation of our alpha threshold:

EXCERPT #JGUTQW p. 5
  \alpha_r = 0.5 + \delta, \quad (3)

EXCERPT #NZSG76 p. 5
  where \delta = 0 for traditional alpha testing and \delta \in [-0.5..0.5] for hashed and stochastic variants. We suggest modifying this as:

EXCERPT #WVCF2Q p. 5
  \alpha_r = 0.5 + \delta \cdot b(\text{lod}), \quad (4)

EXCERPT #6E2G7Z p. 5
  where b(\text{lod}) slowly blends in the noise, i.e., b(0) = 0 and b(n) = 1 for some \text{lod} = n coarse enough for the developer to rely entirely on hashed alpha tests. Values for n depend on desired texture size and noise tolerance; we found n = 6 worked well in our experiments.

EXCERPT #7GN8JQ p. 6

EXCERPT #G3ENJF p. 6

EXCERPT #8TDNZZ p. 6
  We found that linearly ramping b still kept visible noise too close to the camera. A quadratic ramp gave better results, perhaps because apparent noise depends on solid angle, which changes with the square of distance. We used the following function to transition between traditional and hashed alpha testing:

EXCERPT #XTSQCM p. 6
  b(x) = \begin{cases} 0 & : x \leq 0 \\ (x/n)^2 & : 0 < x < n \\ 1 & : x \geq n. \end{cases} \quad (5)

SECTION #E5XEXG 7.1.1 Fading in Noise With Anisotropic Texture Sampling

EXCERPT #H65LSZ p. 6
  Equation 5 fails for alpha-tested surfaces viewed at a grazing angle. This occurs since anisotropic filtering repeatedly accesses finer mip levels, causing alpha geometry to disappear even at relatively low mip levels. This means the transition to a hashed alpha test needs to occur at lower mip levels than in regions sampled isotropically. Scaling x based on anisotropy, before computing b(x) , fixes this:

EXCERPT #32NBCG p. 6
  // Find degree of anisotropy from texture coords vec2 dTex = vec2( length(dFdx(texCoord.xy)), length(dFdy(texCoord.xy)) ); float aniso = max( dTex.x/dTex.y, dTex.y/dTex.x ); // Modify inputs to b(x) based on degree of aniso x = aniso * x;

EXCERPT #P6SJN6 p. 6
  Higher anisotropy increases x , varying \alpha_r more in Equation 4, avoiding alpha maps disappearing at grazing angles.

### 17. Tool result: search_text

Exact matches

1. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 8
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #WCKSTG 8 Conclusion and Future Work
  Matching excerpt #EEZRRW:
      In this paper, we have proposed a novel grass-rendering technique that is capable of rendering dense fields of grass in real time. In comparison to related work, the field of grass can have any shape or spatial alignment. In addition, our approach renders each blade as geometric object that can react to its environment. This reaction to its environment is performed by evaluating a physically based model for each blade separately. This model includes the influence of gravity, wind, and collisions with both simple and complex objects. We use a sphere-packing approach to represent complex objects during the collision detection. In order to achieve real-time performance, we introduce culling methods that are able to cull single blades based on occlusion and their orientation and distance towards the camera. The culling methods are able to cull up to 75% of all blades of grass in a standard frame without decreasing the density of the field of grass significantly. However, the rendering of each blade of grass is still the bottleneck for the performance. Different level-of-detail representations like in the work of Boulanger et al. [Boulanger et al. 2009] can be introduced as future work to

2. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 0
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #9C7W2D 1 Introduction
  Matching excerpt #QEGJYT:
      In this paper, we propose a rendering technique that is capable of rendering fields of grass on arbitrary 3D models by drawing each blade of grass as geometrical object indexed by a geometry-agnostic acceleration structure . For the rendering of each blade, we use hardware tessellation to apply dynamic level of detail, and the shape of a blade is defined by an analytic function. Each blade of grass is influenced by environmental forces, like gravity, wind and collisions with both simple and complex objects. In addition, several culling methods ensure that only those blades are rendered that have an impact on the visual appearance of the field of grass. In addition to standard occlusion culling, we also use the orientation and the distance to the camera as culling criteria. All of these computations are carried out completely on the GPU through indirect rendering, avoiding costly round-trips between CPU and GPU.

3. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 5
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
        #J5PHD5 6.2 Culling
  Matching excerpt #KDYKVC:
      where d_{\text{proj}} is the projected distance, \mathbf{c} is the position of the camera and \mathbf{up} the blade's up-vector. According to this distance, the blade is classified into one of n distance levels, which are evenly distributed over the interval [0, d_{\text{max}}] , where d_{\text{max}} is a user-defined maximum distance. The lowest level culls no blades. The second-lowest level culls one out of n blades, etc., until the n^{\text{th}} level culls all blades. In order to determine which blades of the same distance level are culled, the index id of each blade is used, which is shown in the following inequality:

4. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 4
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
        #J5PHD5 6.2 Culling
  Matching excerpt #LSJVNS:
      Distance test The third test culls blades of grass according to their distance towards the camera. This is important since a field of grass appears to be more dense near the horizon due to perspective. This high density can cause two problems during the rendering. First, due to the lower precision of depth values in the distance, z-fighting can occur. Second, blades at high distances are smaller than a pixel, which can cause aliasing artifacts. Note that the density increase due to perspective is stronger near the horizon than when the field of grass is viewed from above. Therefore, the distance from the camera to the blade of grass is projected onto the local plane defined by the up-vector before it is used for distance culling:

5. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 4
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
  Matching excerpt #26R36D:
      For rendering a field of grass, we draw each blade as a tessellated 2D object. Similar to the method of Jahrmann et al. [2013], we use the tessellation pipeline to provide dynamic level of detail to the shape of a blade. However, instead of using an alpha texture to create the shape of the blade, we use analytic functions that directly modify the geometry, which is explained in Section 6.3. Since each blade of grass has its individual state and position, we cannot render multiple instances of a single patch. In order to achieve real-time performance, we use culling on the basis of single blades to render only the blades that have an impact on the appearance of the field of grass. The culling of single blades requires a rendering pipeline that allows a varying amount of geometry to be rendered each frame. Therefore, we use an indirect rendering approach, which is described in the following section.

6. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 1
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #SEM8VP 4 Preprocessing
  Matching excerpt #83452W:
      Grass distribution During the generation of the blades of grass, either single blades or whole tufts of grass can be generated. The amount of blades that are generated is defined by a user-defined density value and the total area of the 3D model. In case of generating tufts of grass, we use Poisson-disk sampling on the surface [Cline et al. 2009] to ensure that the tufts are not clumped together. The blades of a tuft are placed randomly in the vicinity of the tuft center, and orientation and attributes are also assigned randomly within certain ranges. In case of generating single blades of grass, the blades are distributed randomly on the surface of the 3D model, without Poisson-disk sampling, since random clumping of blades is beneficial for a natural grass distribution. Single-blade seeding is good for covering fields of grass with equal density, whereas tuft seeding generates a more natural grass distribution. Therefore, a realistic meadow can be generated using a combination of both seeding methods. Each blade of grass is generated in an initial pose where the control points v_1 and v_2 share the same position, which is above the ground position v_0 according to the height and the up-vector.

Approximate matches

1. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 5
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
        #J5PHD5 6.2 Culling
  Score: 0.03
  Related excerpt #KDYKVC:
      where d_{\text{proj}} is the projected distance, \mathbf{c} is the position of the camera and \mathbf{up} the blade's up-vector. According to this distance, the blade is classified into one of n distance levels, which are evenly distributed over the interval [0, d_{\text{max}}] , where d_{\text{max}} is a user-defined maximum distance. The lowest level culls no blades. The second-lowest level culls one out of n blades, etc., until the n^{\text{th}} level culls all blades. In order to determine which blades of the same distance level are culled, the index id of each blade is used, which is shown in the following inequality:

2. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 4
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
        #J5PHD5 6.2 Culling
  Score: 0.029
  Related excerpt #LSJVNS:
      Distance test The third test culls blades of grass according to their distance towards the camera. This is important since a field of grass appears to be more dense near the horizon due to perspective. This high density can cause two problems during the rendering. First, due to the lower precision of depth values in the distance, z-fighting can occur. Second, blades at high distances are smaller than a pixel, which can cause aliasing artifacts. Note that the density increase due to perspective is stronger near the horizon than when the field of grass is viewed from above. Therefore, the distance from the camera to the blade of grass is projected onto the local plane defined by the up-vector before it is used for distance culling:

3. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 8
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #WJRRU9 7 Results
        #5NC82H 7.3 Comparison to related work
  Score: 0.028
  Related excerpt #NMVPYZ:
      ble fragments are discarded. This enables an easy way to generate different shapes. However, the resolution of the texture that is used is crucial for the visual appearance, since texture sampling artifacts can appear if the resolution is too low. The higher the resolution of the alpha, the higher is the memory footprint of the technique and the method becomes slower. In comparison, we generate the shape by modifying directly the geometry of a blade using analytic functions. This reduces the amount of fragments that has to be computed and the edges of the shape have the same smoothness regardless of the distance to the camera. Figure 17 shows a closer view of a blade of grass of both techniques.

4. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 4
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
  Score: 0.027
  Related excerpt #26R36D:
      For rendering a field of grass, we draw each blade as a tessellated 2D object. Similar to the method of Jahrmann et al. [2013], we use the tessellation pipeline to provide dynamic level of detail to the shape of a blade. However, instead of using an alpha texture to create the shape of the blade, we use analytic functions that directly modify the geometry, which is explained in Section 6.3. Since each blade of grass has its individual state and position, we cannot render multiple instances of a single patch. In order to achieve real-time performance, we use culling on the basis of single blades to render only the blades that have an impact on the appearance of the field of grass. The culling of single blades requires a rendering pipeline that allows a varying amount of geometry to be rendered each frame. Therefore, we use an indirect rendering approach, which is described in the following section.

5. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 5
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
        #SYPVZY 6.3 Blade Geometry
  Score: 0.026
  Related excerpt #ECCWQ4:
      During rendering, each blade is drawn as 2D object positioned in 3D space. The generation of the shape of a blade is performed in the tessellation evaluation shader, which uses the information of the hardware-tessellation unit to position the generated vertices. Initially, the blade geometry is a flat quad that is defined by the interpolation parameters u and v , where u indicates the interpolation along the width of the blade and v the interpolation along the height. By evaluating the curve interpolation of the control points for each generated vertex, the quad becomes aligned to the Bézier

6. Source: Responsive Real-Time Grass Rendering for General 3D Scenes (#PQ68ZH), Klemens Jahrmann, Michael Wimmer, p. 6
  Context:
    #5FHX2Q Responsive Real-Time Grass Rendering for General 3D Scenes
      #RDERFQ 6 Rendering
        #SYPVZY 6.3 Blade Geometry
  Score: 0.026
  Related excerpt #MD9A4M:
      3D displacement The 3D displacement is an additional feature that can be added to the shape of a blade, where the middle axis of the blade is translated along the normal vector, resulting in a “v”-shape in its cross-section. If the shape has a tip, it is important that the translation has to decrease the nearer the generated point is to the top. Otherwise, the blade has a depth but no width at the tip. Equation 23 shows the calculation of the displacement vector d , where n is the normal vector and w the width of the blade. By adding this displacement, the shape has approximately a right angle and the unfolded width of the blade increases by the factor \sqrt{2} .

### 18. Assistant tool calls

#### Tool call: `write_note`

```json
{
  "block_ids": [
    "WZDZZW",
    "N6QLYJ",
    "S4F32B",
    "7R6ZNJ",
    "D4D5AP",
    "W8VWLB",
    "X9PHPN",
    "DPJ27C",
    "DJSHKW",
    "4TS8SV",
    "AYX9GT",
    "26R36D",
    "2WBRVG",
    "KDYKVC",
    "EEZRRW",
    "LSJVNS",
    "8VWZJV",
    "UUL95W",
    "FH7HUR",
    "8TDNZZ",
    "XTSQCM",
    "H65LSZ"
  ],
  "text": "Synthesis: continuous LOD for foliage/grass as statistical estimation. Core claim: a coarse LOD should be an unbiased estimator of the fine LOD's expected appearance (coverage, radiance, motion), not a simplified mesh.\n\n1. Conserve coverage across mips: HZD coverage-preserving alpha mip chain rescales each mip so post-threshold coverage matches the source (#WZDZZW). Hashed alpha testing achieves the same invariant stochastically: random threshold makes the alpha test an unbiased coverage estimator (#N6QLYJ, #S4F32B).\n2. Far representation = statistics of near representation: Bruneton–Neyret shader-maps encode expected forest radiance incl. view-light correlations (#7R6ZNJ); seamless transition forces near-model radiances to converge to statistical averages before cross-fading (#D4D5AP, #W8VWLB, #X9PHPN, #DPJ27C). Blends only work after expectations agree.\n3. Continuity via continuous parameter spaces: Tsushima Bézier blades sampled at 15 vs 7 verts (#DJSHKW, #4TS8SV, #AYX9GT); Jahrmann analytic blades + hardware tessellation (#26R36D); GPU tree generation makes LOD a frame-time feedback controller over continuous quality parameters with rate-limited change (#2WBRVG).\n4. Stochastic density reduction: Jahrmann per-blade hashed distance culling, up to 75% culled without visible density loss; distance projected onto ground plane because perspective density rises toward horizon (#KDYKVC, #EEZRRW, #LSJVNS).\n5. LOD the motion too: HZD scales wind animation to zero and pushes vertices down with distance (#8VWZJV, #UUL95W, #FH7HUR); physics ladder from per-blade forces (#EEZRRW) to statistical wind.\n6. Variance budgeted by solid angle: quadratic noise fade-in b(x)=(x/n)^2 and anisotropy scaling in hashed alpha (#8TDNZZ, #XTSQCM, #H65LSZ).\n\nOne-liner: analytic ground truth ⇒ detail is a sampling rate; coarse levels are unbiased estimators; drops in count are dithered; transitions converge expectations before blending; noise metered by solid angle.",
  "title": "Foliage/grass continuous LOD as unbiased statistical estimation"
}
```

### 19. Tool result: write_note

NOTE SAVED #CYDXJ2
IRI: https://m.sheaf.less.rest/CYDXJ2

### 20. Assistant

Note saved as #CYDXJ2. The most actionable gap the corpus itself points at: Jahrmann names per-blade geometry as the bottleneck and cites missing LOD representations as future work (#EEZRRW) — which is exactly what the Tsushima compute-pipeline (indirect draws + variable vertex count) and the work-graph tree paper (#2WBRVG) later solved. If you're building this in moppe, the frame-time-feedback LOD controller from #2WBRVG combined with hashed per-instance culling (#KDYKVC) is the design that satisfies "continuousish" with the least machinery.

### 21. User

I'm pretty sure that, in human experience and animal experience, the world that I experience, the environment that I experience, is actually procedurally generated, in a sense. Isn't that more or less true? I don't see the details of reality even when I look closely at a close-up.

I'm sitting at a desk now that I built out of birch plywood. On that desk is a mouse pad that I made out of granulated cork board and some acrylic lacquer, and I chamfered the edges with sandpaper a bit, etc. The plywood, I didn't really put any kind of finish on, so it has quite a lot of dirt and random discolorations. I'm pretty sure that my experience even of this cork pattern, as I'm looking at it directly right now from 10 cm, is kind of invented by my brain.

I think that's one thing that psychedelic, like LSD, makes relatively blatant: the mind and our perceptual system are incredibly, incredibly extraordinarily good at fabricating detailed textures and patterns that are never quite the same. You never look at the same river twice. You never even look at the same piece of wood twice.

### 22. User

can you discuss all that in more spacious prose aside from teh dense reference note? :)

### 23. Assistant

Happily — here's the same material, but let it breathe.

## What "level of detail" is actually for

Start with the naive picture: you have a beautiful, expensive grass blade or tree, and as it gets farther away you swap it for cheaper and cheaper versions until it's a billboard, then a pixel, then nothing. This picture treats LOD as *degradation* — a sequence of compromises, each one a slightly worse copy of the original. And the perennial artifacts of foliage rendering are exactly the symptoms of that framing: popping when a swap happens, shimmer when sub-pixel detail aliases, and the infamous "melting" where alpha-tested leaves simply vanish into the distance because each mip level eats a little more of them.

The deeper way of thinking — the one I'd call physically based, though it's really *statistically* based — inverts the question. Instead of asking "what's a cheaper version of this mesh?", ask: **what does a distant patch of grass actually *do* to the light arriving at the camera, and what is the cheapest thing that does the same?** A pixel covering a square meter of meadow doesn't care about individual blades. It cares about how much of the ground is occluded, how much light bounces back and in what directions, how the aggregate mass moves. Those are physical quantities — coverage, radiance, motion energy — and they have well-defined averages. The right coarse representation isn't a worse copy of the fine one; it's the fine one's *expected value*.

Once you adopt that view, every technique in the corpus snaps into place as an instance of one principle: **the coarse level should be an unbiased estimator of the fine level, and the invariant being conserved should be something a photon would care about.**

## Why foliage disappears, and the two ways to save it

The classic disappearing-foliage bug is a beautiful case study, because it's not a hack gone wrong — it's a genuine statistical bias. Alpha-tested foliage works by thresholding an opacity texture at 0.5. But mipmapping *averages* the texture, and a leaf texture is mostly transparent background, so averaging drags values down toward transparency. Threshold the blurred result at 0.5 and less and less survives at each level. By the time you're a few mips deep, the bush is gone. The error isn't visual, it's mathematical: the pipeline silently changed the expected coverage.

The Horizon Zero Dawn team fixed it deterministically (#WZDZZW): measure the true coverage of the original image, then rescale every mip level so that after thresholding, the coverage comes out the same. They're treating projected coverage as a conserved quantity — like enforcing conservation of mass through a coarsening operation.

Wyman and McGuire fixed the same bug stochastically, and the elegance is worth savoring. Hashed alpha testing (#S4F32B) replaces the fixed 0.5 threshold with a random one drawn uniformly from $[0,1)$. Now a texel with alpha 0.3 survives 30% of the time — which means the *expected* coverage is exactly right, at every mip level, with no preprocessing at all. The alpha test has become a tiny Monte Carlo estimator. The price is noise, and here's where the physical thinking shows up again: they fade the noise in *quadratically* with distance, reasoning that perceived noise scales with the solid angle an object subtends, and solid angle falls off with the square of distance (#8TDNZZ). That's a perceptual variance budget derived from geometry, not tuned by eye.

So one bug, two cures — one bakes the correct expectation into the data, one samples it at runtime — and both are answers to the same question: *what must be conserved?*

## The far field as literal statistics

Bruneton and Neyret's forest paper (#BDBBL6) takes the idea to its logical conclusion. Near the camera, trees are individual geometric things. Far away, they don't try to render cheap trees at all. The distant forest becomes a "shader-map": a function over the terrain that directly answers "given this view direction and this sun direction, how much light does a forest-covered patch here reflect?" — including subtle aggregate effects like the correlation between what the eye sees and what the sun illuminates, which is what makes a forest look darker or brighter as you orbit it. The far representation *is* the statistics of the near one, precomputed.

And their transition scheme contains what might be the single most transferable insight in this whole cluster. A cross-fade between two representations only looks seamless if, at the moment of the blend, both representations are already producing the same value. So they don't just blend — they first *drive the detailed model toward its own statistical average* as distance grows (#D4D5AP), so that by the handoff point, the individual trees have gracefully converged to exactly what the statistical model will say about them. Then the fade is invisible almost by definition. The blend is the trivial part; **the real work is making the expectations agree before you blend.** Most popping in games is a violation of exactly this: two LODs that disagree about brightness or coverage, cross-faded in the hope nobody notices.

## Continuity by construction

There's a second, independent route to "continuousish," and it comes from procedural generation rather than statistics. If your ground truth is an *analytic object* — Ghost of Tsushima's grass blades are cubic Bézier curves (#DJSHKW), Jahrmann's blades are analytic shape functions fed through hardware tessellation (#26R36D) — then a "level of detail" is just a sampling rate. Fifteen vertices along the curve or seven (#4TS8SV, #AYX9GT): both are honest samplings of the same underlying function, so refining and coarsening is inherently smooth. There's no asset ladder to pop between because there are no discrete assets, only a function and a density.

The recent GPU tree-generation work (#EDURTK) pushes this to a genuinely new place. Because the trees are regenerated from a few kilobytes of parameters *every frame*, detail stops being a property of stored data and becomes a *control variable*. They wire quality parameters — triangle density, leaf count — into a feedback loop against the frame-time budget: if last frame ran hot, degrade the least-noticeable parameter a little; if there's headroom, restore it; and cap the rate of change so no single frame ever visibly jumps (#2WBRVG). That's LOD as control theory. It's the most literal realization of "continuous" in the corpus: detail is a smoothly varying signal responding to load, the way a thermostat responds to temperature.

Between these two poles sits the humbler but essential trick of stochastic thinning. When you must reduce the *count* of things — you can't draw every blade to the horizon — Jahrmann culls individual blades by hashed index in proportion to distance (#KDYKVC), so density decays as a smooth dithered gradient rather than in bands. Up to three quarters of the blades vanish and the field doesn't visibly thin (#EEZRRW). It's the geometric sibling of hashed alpha: kill instances randomly so the expected coverage stays continuous. There's even a nice physically minded refinement: he measures distance projected onto the ground plane, because perspective crowds blades toward the horizon much faster than it does under a top-down view, and the crowding is precisely what creates sub-pixel aliasing (#LSJVNS).

## Don't forget that motion has levels of detail too

The piece people tend to miss: appearance isn't the only thing with a spatial-frequency spectrum. *Motion* has one too. Up close, individual blades whipping in the wind is the whole point. But at distance, all that per-blade jitter integrates within a pixel — and if you keep computing it, it doesn't add life, it adds shimmer. Horizon Zero Dawn handles this explicitly: with distance they scale the whole wind-animation displacement down toward zero and even press the grass geometry itself down toward the ground (#8VWZJV, #FH7HUR). The distant field settles into its mean configuration, which is exactly what filtering theory says it should do — high frequencies that fall below the sampling rate must be removed at the source, not left to alias.

The same ladder applies to simulation cost. Jahrmann's fine end is genuinely physical — per-blade gravity, wind, and collision response (#EEZRRW). Nobody can afford that to the horizon, and nobody should want it: the far end of the ladder is a statistical wind field driving aggregate sway. Physics, like geometry, should coarsen toward its own expectation.

## The shape of the whole idea

So the "insights we can formulate" arrange themselves into a small credo:

**Detail is a sampling rate, not an asset.** Where possible, define foliage analytically or procedurally, so refinement is evaluation density and continuity is free.

**Coarse levels are estimators.** Their job is to reproduce the expected coverage, radiance, and motion of the fine level — bias, not blur, is what the eye reads as wrong.

**When counts must drop, dither.** Stochastic thinning with a stable hash turns discrete removal into a smooth expected gradient.

**Transitions are about agreement, not blending.** Converge both representations to a shared expectation first; the cross-fade then has nothing to hide.

**Budget variance by solid angle.** Noise and degradation are perceptual quantities; geometry tells you how much you can afford where.

What I find satisfying is that this is the same intellectual move renormalization makes in physics: you don't carry microscopic degrees of freedom to macroscopic scales — you ask which effective quantities survive coarse-graining and build the theory at each scale out of those. Grass rendering, done well, is a renormalization scheme for meadows.

### 24. User

This is kind of the way of thinking that is embodied in physically based rendering materials, isn't it? In some way, it seems very much related, and I feel like what grass kinda morphs into is something like a PBR grass material. Somehow, does that make sense?

### 25. Assistant

Yes — it makes complete sense, and you've actually landed on the deepest version of the idea. Let me draw the line explicitly, because it's not just an analogy: **PBR materials are already the endpoint of the LOD logic we discussed. They are what geometry becomes when it falls below the pixel.**

## A BRDF is statistical geometry

Think about what a microfacet material actually claims. Cook–Torrance-style PBR says: this surface is, microscopically, a landscape of tiny mirror facets; we can't see them individually, so we describe them by their *statistics*. The normal distribution function is literally a histogram of sub-pixel surface normals. "Roughness" is the variance of that histogram. The shadowing–masking term $G$ is the probability that a microfacet is occluded by its neighbors, as a function of view and light direction. None of these are properties of a *substance* — they're renormalized descriptions of *shape too small to resolve*. A PBR material is a probability distribution wearing a shader.

Once you see that, the geometry/material distinction stops being ontological and becomes purely scale-relative: **"material" is whatever structure lives below your current pixel footprint; "geometry" is whatever lives above it.** Brushed metal is geometry to a profilometer and a material to a camera. And LOD, in the statistical framing from before, is exactly the act of *migrating structure across that boundary* as the footprint grows with distance.

## So yes: distant grass *is* a grass material

Follow a meadow out to the horizon through that lens. Up close, blades are geometry — Tsushima's Béziers, Jahrmann's tessellated analytic shapes. At middle distance, blade-scale shape detail slides under the pixel and gets absorbed into texture and normal content. And at the far end, the *entire vegetation layer* is sub-pixel: what remains is a spatially varying, view- and light-dependent reflectance function over the terrain surface. That is a BRDF. A weird one — but a BRDF.

The corpus contains this endpoint almost verbatim. Bruneton and Neyret's shader-map is precisely a "PBR forest material": a function on the terrain answering "given this view direction and sun direction, what radiance comes off forest-covered ground here," including the view–light masking correlations (#7R6ZNJ). And notice what that correlation term *is*: it's the shadowing–masking $G$ of a forest whose "microfacets" are entire trees. The math of microfacet theory reappears one scale-octave up, with trees playing the role of facets. Their transition trick — driving individual tree radiances toward the statistical average before the handoff (#D4D5AP) — is then exactly "geometry converging to its material" as it shrinks.

Even the near-field pipeline already whispers this. HZD's grass ends its journey through the renderer as G-buffer entries: roughness, reflectance, translucency (#3JHMBP, #MXZG4T). The blade was born as a curve and dies as material parameters. LOD just moves the point in the pipeline where that death happens — with distance, it happens earlier and earlier, until it happens before rendering at all.

## The honest catch: you can't just mip a material

Here's where your intuition needs one refinement, and it's the part that would make this thesis-grade rather than hand-wavy. The naive version — "so at distance, sample a grass albedo/roughness texture at a lower mip" — fails for exactly the same reason alpha-tested foliage vanished: **filtering material parameters is not the same as filtering appearance.** Averaging normals flattens them, and a flat-but-shiny surface looks nothing like a rough one; the lost normal variance has to be *converted into roughness* (this is the whole point of the appearance-prefiltering literature — LEAN/LEADR mapping and kin). Averaging is a linear operation; shading is violently nonlinear; they don't commute. Bias sneaks in at the commutation failure, and the eye reads bias as "distant terrain looks like plastic."

Grass makes this maximally hard, because a grass "material" is nothing like a nice isotropic dielectric:

- It's **strongly anisotropic** — blades are vertical, so the far-field NDF is a tight distribution of near-horizontal normals with structured azimuth (Tsushima's *clump facing* is literally azimuthal structure in the distribution).
- It's **translucent** — much of the signature look of backlit grass is transmission, so the far material needs a BTDF component, not just reflection.
- It's **deep, not flat** — a meadow is millimeters of surface but decimeters of optical depth. The honest far-field model is closer to a thin anisotropic *volume* (in the microflake tradition: a participating medium whose "particles" have an orientation distribution) than to a coating on a heightfield. This is, not coincidentally, the Neyret lineage — his volumetric textures were grass-at-a-distance thirty years before the shader-map paper.
- Its self-shadowing is **correlated with view and light** in the way that produces the hotspot/opposition effect you see flying over fields — again a $G$-term phenomenon, and exactly what Bruneton–Neyret bother to preserve.

So "grass morphs into a PBR grass material" is right, with the crucial addendum: **the material's parameters must be *computed as statistics of the near representation*, not authored independently and hoped to match.** Otherwise you've rebuilt the popping problem one level up — a geometric near-field and a material far-field that disagree about expected radiance, with a cross-fade papering over the disagreement.

## Why this is a satisfying way to close the loop

It gives your LOD ladder a well-defined terminal object. The ladder isn't "mesh, cheaper mesh, billboard, nothing" — it's "geometry, geometry-with-statistical-garnish, statistics." And it says something concrete for an implementation like moppe: the per-blade parameters that generate the near field (Tsushima's instance data — height, width, tilt, bend, clump facing, color, #66DTCB) are *also* the sufficient statistics of the far field. Aggregate them over a tile — mean and variance of tilt, distribution of facing, coverage from #WZDZZW-style reasoning — and you have derived, not authored, the parameters of the distant grass BRDF. One generative model, evaluated as geometry near and as its own moments far. That's the whole philosophy in one sentence: *a material is what a distribution of geometry looks like from far enough away.*

Saving that link as a note, since it extends the earlier synthesis:

#### Tool call: `write_note`

```json
{
  "block_ids": [
    "7R6ZNJ",
    "D4D5AP",
    "3JHMBP",
    "MXZG4T",
    "66DTCB",
    "WZDZZW",
    "CYDXJ2"
  ],
  "text": "Extension of the LOD-as-statistics synthesis (#CYDXJ2): the terminal object of a continuous foliage LOD ladder is a PBR material, because PBR materials ARE renormalized geometry — the microfacet NDF is a histogram of sub-pixel normals, roughness is its variance, and the shadowing-masking term G is statistical self-occlusion. The geometry/material boundary is scale-relative (set by pixel footprint), so LOD = migrating structure across that boundary.\n\nCorpus evidence: Bruneton–Neyret shader-maps are literally a \"PBR forest material\" — view/light-dependent terrain reflectance with view-light masking correlations, i.e. a G-term whose microfacets are trees (#7R6ZNJ); their pre-transition convergence (#D4D5AP) is geometry converging to its material. HZD grass already terminates as G-buffer material parameters: roughness/reflectance (#3JHMBP), translucency (#MXZG4T).\n\nCaveats for a \"PBR grass material\": cannot naively mip material parameters (filtering ≠ shading; lost normal variance must convert to roughness, cf. LEAN/LEADR appearance prefiltering). Far-field grass BRDF is anisotropic (vertical blades, clump-facing azimuth structure), translucent (needs BTDF), optically deep (better as thin anisotropic microflake volume, Neyret volumetric-texture lineage), with view-light correlated self-shadowing (hotspot effect).\n\nDesign implication (e.g. for moppe): per-blade instance parameters (#66DTCB: height, width, tilt, bend, clump facing, color) are the sufficient statistics of the far field — aggregate their moments per tile plus coverage (#WZDZZW) to DERIVE the distant material's parameters from the same generative model, ensuring near/far expectations agree by construction. Slogan: a material is what a distribution of geometry looks like from far enough away.",
  "title": "Distant foliage LOD terminates in a PBR material (geometry→material continuum)"
}
```

### 26. Tool result: write_note

NOTE SAVED #USARUR
IRI: https://m.sheaf.less.rest/USARUR

### 27. Assistant

Saved as #USARUR, linked back to the earlier synthesis note #CYDXJ2.

So, to answer you directly: it doesn't just make sense — it's the correct closure of the whole argument. PBR was never really a theory of *materials*; it's a theory of *unresolvable geometry*, and it only feels like a theory of materials because at ordinary viewing distances the unresolvable geometry happens to be microscopic. Grass at a kilometer is in exactly the same epistemic position as brushed metal at arm's length. The one thing to hold onto is that the grass material at the far end must be *derived* — the moments of the same distribution that generates the blades up close — rather than independently authored, or you reintroduce the near/far disagreement that all the seamlessness machinery exists to prevent.

### 28. User

Now you can look in the repository of our project and look at how the forest shader works, and you can describe that. I would also be interested to read your description of that.

### 29. Assistant tool calls

#### Tool call: `read`

```json
{
  "blocks": [
    "/3CV92Z/source-directories/bW9wcGU"
  ]
}
```

#### Tool call: `search_text`

```json
{
  "document_id": "3CV92Z",
  "limit": 10,
  "query": "forest shader"
}
```

### 30. Tool result: read

SOURCE DIRECTORY
Path: moppe
Resource: /3CV92Z/source-directories/bW9wcGU

Children:
  - /3CV92Z/source-directories/bW9wcGUvZ2FtZQ source directory: game
  - /3CV92Z/source-directories/bW9wcGUvZ2Z4 source directory: gfx
  - /3CV92Z/source-directories/bW9wcGUvbWFw source directory: map
  - /3CV92Z/source-directories/bW9wcGUvbW92 source directory: mov
  - /3CV92Z/source-directories/bW9wcGUvcGxhdGZvcm0 source directory: platform
  - /3CV92Z/source-directories/bW9wcGUvcmVuZGVy source directory: render
  - /3CV92Z/source-directories/bW9wcGUvc2hhZGVycw source directory: shaders
  - /3CV92Z/source-directories/bW9wcGUvc3BhdGlhbA source directory: spatial
  - /3CV92Z/source-directories/bW9wcGUvdGVycmFpbg source directory: terrain
  - /3CV92Z/source-files/bW9wcGUvY29sb3IuaGg source file: moppe/color.hh
  - /3CV92Z/source-files/bW9wcGUvcGFjaW9saS5oaA source file: moppe/pacioli.hh
  - /3CV92Z/source-files/bW9wcGUvcGFydGl0aW9uLmho source file: moppe/partition.hh
  - /3CV92Z/source-files/bW9wcGUvcHJvZmlsZS5oaA source file: moppe/profile.hh
  - /3CV92Z/source-files/bW9wcGUvcXVhbnRpdGllcy5oaA source file: moppe/quantities.hh

### 31. Tool result: search_text

Exact matches

1. Source file: moppe/render/metal/shader_types.h
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvcmVuZGVyL21ldGFsL3NoYWRlcl90eXBlcy5o#content
   Size: 15917 bytes, 374 lines
   Matching excerpt:
      // Uniform structs shared between C++ and MSL. Every vector slot is // a float4 and matrices are 64-byte column-major, so the layouts // agree on both sides without packed-type tricks. Keep scalars in // groups of four. #ifndef MOPPE_SHADER_TYPES_H #define MOPPE_SHADER_TYPES_H #ifdef __METAL_VERSION__ #include <metal_stdlib> #define MOPPE_SHADER_ALIGN typedef metal::float4x4 MoppeMat4; typedef metal::float4 MoppeFloat4; typedef metal::uint4 MoppeUint4; #else #include <cstdint> #define MOPPE_SHADER_ALIGN alignas (16) struct MOPPE_SHADER_ALIGN MoppeMat4 { float m[16]; }; struct MoppeFloat4 { float x, y, z, w; }; struct MoppeUint4 { std::uint32_t x, y, z, w; }; #endif // Buffer indices (vertex stage). #define MOPPE_BUF_VERTICES 0 #define MOPPE_BUF_FRAME 1 #define MOPPE_BUF_DRAW 2 #define MOPPE_BUF_CHUNK 3 #define MOPPE_BUF_PREVIOUS_VERTICES 4 #define MOPPE_BUF_FOREST 5 // Texture indices (fragment stage). #define MOPPE_TEX_COLOR 0 #define MOPPE_TEX_GRASS 0 #define MOPPE_TEX_DIRT 1 #define MOPPE_TEX_SNOW 2 #define MOPPE_TEX_SHADOW 3 #define MOPPE_TEX_ROCK 4 #define MOPPE_TEX_TERRAIN_OVERLAY 6 #define MOPPE_TEX_TERRAIN_MOISTURE 7 #define MOPPE_TEX_TERRAIN_WATER 8 #define MOPPE_TEX_TERRA
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvcmVuZGVyL21ldGFsL3NoYWRlcl90eXBlcy5o#content"]

2. Source file: moppe/game/surface_presentation.cc
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvZ2FtZS9zdXJmYWNlX3ByZXNlbnRhdGlvbi5jYw#content
   Size: 1917 bytes, 44 lines
   Matching excerpt:
      #include <moppe/game/surface_presentation.hh> #include <moppe/profile.hh> #include <moppe/render/texture_pixels.hh> namespace moppe::game { void upload_surface_readings (render::Renderer& renderer, const map::SurfaceGeometry& geometry, const map::SurfaceReadings& readings, bool include_forest) { MOPPE_PROFILE_ZONE ("surface.upload_readings"); using render::PixelFormat; using render::planar_texture_pixels; using render::texture_pixels; // Both fields are bounded proportions. Half precision preserves far more // resolution than their presentation thresholds can reveal, halves their // bandwidth, and lets Metal filter them in hardware on every supported // Apple GPU. renderer.set_terrain_moisture ( texture_pixels<map::surface_moisture> (readings, PixelFormat::r16f)); renderer.set_terrain_geology ( texture_pixels<map::erosion_exposure, map::deposition_cover> ( readings, PixelFormat::rg16f)); renderer.set_terrain_shore ( texture_pixels<map::waterline_distance> (readings, PixelFormat::r16f)); renderer.set_terrain_snow_support ( texture_pixels<map::snow_support> (geometry, PixelFormat::r16f)); renderer.set_terrain_paths ( texture_pixels<map::trail_influence, map::home_base_influence> ( re
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvZ2FtZS9zdXJmYWNlX3ByZXNlbnRhdGlvbi5jYw#content"]

3. Source file: docs/forest-density-and-aggregates.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtZGVuc2l0eS1hbmQtYWdncmVnYXRlcy5tZA#content
   Size: 6732 bytes, 125 lines
   Matching excerpt:
      # Forest density and aggregates: the road to "it feels like a forest" Status: design intent, August 2026. The implemented LOD system this builds on is recorded in [forest LOD](forest-lod.md). The reference image remains the golden-hour spruce mockup (dense canopy, long shadows, sunbeams, dark interior); the gazetteer's forest-sunward / forest-shadowplay / forest-interior studies are the standing comparison views. ## The thesis: density is an unlock, not a cost The current stands are open enough to see through, so every tree presents as an individual silhouette -- the world reads as "a bunch of spiny trees", not a forest. A real spruce forest at any distance is mostly occlusion: a closed canopy surface on hillsides, dark interior gloom between trunks up close, individuals legible only at stand edges. Density also changes what the far field is allowed to be. A sparse stand seen from 500 m is still "several distinguishable individuals", and only per-tree geometry renders that honestly. A *closed* canopy seen from 500 m is statistically uniform -- a bumpy dark-green surface with known roughness and lighting -- and can be rendered as essentially one thing at a cost proportional to pixel
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtZGVuc2l0eS1hbmQtYWdncmVnYXRlcy5tZA#content"]

4. Source file: moppe/game/generated_world.cc
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvZ2FtZS9nZW5lcmF0ZWRfd29ybGQuY2M#content
   Size: 5280 bytes, 120 lines
   Matching excerpt:
      #include <moppe/game/generated_world.hh> #include <moppe/profile.hh> #include <moppe/terrain/moisture.hh> #include <moppe/terrain/readings.hh> #include <moppe/terrain/river.hh> #include <moppe/terrain/waterline.hh> #include <utility> namespace moppe::game { WorldParams bind_world_params (WorldParams params, const terrain::WorldRecipe& recipe) { params.map_size = recipe.extent (); params.resolution = recipe.resolution (); params.water_level = recipe.water_datum (); return params; } HydrologyAnalysis analyze_hydrology (const map::SurfaceGeometry& geometry, const terrain::WorldRecipe& recipe, const HydrologyProgress& progress) { MOPPE_PROFILE_ZONE ("game::analyze_hydrology"); const auto report = [&progress] (HydrologyStage stage) { if (progress) progress (stage); }; report (HydrologyStage::StandingWater); terrain::FloodField standing_water = terrain::analyze_standing_water ( geometry, (recipe.water_datum ()).numerical_value_in (moppe::u::m)); report (HydrologyStage::Lakes); terrain::LakeCensus lakes = terrain::census_lakes (standing_water); report (HydrologyStage::Drainage); terrain::DrainageGraph drainage = terrain::analyze_wet_drainage (standing_water, lakes); report (HydrologyStage
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvZ2FtZS9nZW5lcmF0ZWRfd29ybGQuY2M#content"]

5. Source file: docs/forest-lod.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtbG9kLm1k#content
   Size: 7424 bytes, 128 lines
   Matching excerpt:
      # Forest LOD: the continuous-assembly design Status: implementation record of the conifer LOD system in `moppe/shaders/metal/forest.metal` as of August 2026, and of the principles and instruments that shaped it. The forward-looking plan is in [forest density and aggregates](forest-density-and-aggregates.md). ## The bet No tree mesh exists in CPU or GPU memory. Every frame, the object stage chooses each organism's detail from its projected size and the mesh stage grows boughs, tufts, and blades from a seed (Kuth et al. 2025, "Real-Time GPU Tree Generation", HPG; Sheaf `#EDURTK`, notes `#6S29CJ`). The memory saving is the headline but not the point: because nothing is retained, detail can be exactly right for the current frame and vary continuously -- the whole temporal-stability design below is only expressible because no baked LOD meshes exist. The costs are structural: generation is paid every frame even when the camera is still, stability of every generation input becomes a discipline instead of a given, and ray tracing would need per-frame acceleration-structure rebuilds. ## Principles, each learned the hard way 1. **Transitions finish while the tree is small in the frame.** The
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtbG9kLm1k#content"]

6. Source file: docs/atelier-tree.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9hdGVsaWVyLXRyZWUubWQ#content
   Size: 9000 bytes, 168 lines
   Matching excerpt:
      # The Atelier tree The Atelier tree is a small proof that an organism can remain itself while its presentation changes completely. Run it as a wind-bent object: ```sh cmake --build build --target atelier ./build/atelier.app/Contents/MacOS/atelier --tree ``` or as a diagram of the same organism: ```sh ./build/atelier.app/Contents/MacOS/atelier --tree-diagram ``` Deterministic stills can be made without opening a window: ```sh ./build/atelier.app/Contents/MacOS/atelier \ --tree --capture /tmp/tree.png 7 ./build/atelier.app/Contents/MacOS/atelier \ --tree-diagram --capture /tmp/tree-diagram.png 7 ``` ## Three storeys `DirectedTreeTopology` is the combinatorial storey. It owns vertices, edges, incidence, generation, lineage, branch order, and the distinction between the shoot and root trees. It has no positions. `Tree::VertexState`, `Tree::EdgeState`, and `TreeEdgeForm` are the intrinsic storey. They are typed `Bundle`s over the vertex and edge domains. Rest length, radius, flexibility, azimuth, elevation, water potential, sugar potential, and bud vigor all belong here. Radius is derived from the terminal mass supported by an edge, so thickening is a property of the organism rather tha
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9hdGVsaWVyLXRyZWUubWQ#content"]

7. Source file: CMakeLists.txt
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/Q01ha2VMaXN0cy50eHQ#content
   Size: 30546 bytes, 833 lines
   Matching excerpt:
      cmake_minimum_required(VERSION 3.24) set(MOPPE_IOS FALSE) set(MOPPE_TVOS FALSE) set(MOPPE_MOBILE_APPLE FALSE) set(MOPPE_APPLE_SIMULATOR FALSE) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(MOPPE_IOS TRUE) set(MOPPE_MOBILE_APPLE TRUE) elseif(CMAKE_SYSTEM_NAME STREQUAL "tvOS") set(MOPPE_TVOS TRUE) set(MOPPE_MOBILE_APPLE TRUE) endif() if(CMAKE_OSX_SYSROOT MATCHES "[Ss]imulator") set(MOPPE_APPLE_SIMULATOR TRUE) endif() # The normal build is an edit build: do no optimization, and keep only the line # tables needed to turn a crash address into a source location. Optimized # gameplay, profiling, and distribution builds select RelWithDebInfo or Release # explicitly. if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type (Debug, Release, RelWithDebInfo, or MinSizeRel)" FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel) endif() # Moppe's Apple renderer is Metal 4-only. Metal 4 arrived with the 26.0 # platform generation, so every Apple target names that contract explicitly # instead of inheriting an SDK-dependent desktop default or carrying an older # Metal compatibility path. FORCE also upgr
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/Q01ha2VMaXN0cy50eHQ#content"]

8. Source file: docs/ideal-dream-graphics.org
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9pZGVhbC1kcmVhbS1ncmFwaGljcy5vcmc#content
   Size: 9145 bytes, 208 lines
   Matching excerpt:
      #+title: Moppe Ideal Dream Graphics #+startup: overview #+todo: TODO(t) ACTIVE(a) VERIFY(v) | DONE(d) DROPPED(x) * Purpose This is the living plan and completion record for moving Moppe's production renderer toward the visual target below. It is deliberately about the actual game path, not the Atelier. A task becomes DONE only after it is present in a gameplay or gazetteer capture and survives the performance gate. [[file:../ideal-dream-gfx.png][Ideal dream graphics concept, 2026-08-05]] The reference is not primarily a request for Nanite. Its strongest cues are composition, directional light, readable shadow hierarchy, varied conifer silhouettes, dense multi-scale ground cover, terrain material structure, and depth from atmosphere. Geometry streaming and mesh shaders matter because they let those cues remain dense and stable, not because they create the look by themselves. * Visual contract - The rider and route remain the focus; the chase lens does not flatten the entire landscape into a wide-angle panorama. - Nearby trees read as living organisms: trunk, tiered boughs, smaller sprays, gaps, asymmetry, age variation, warm transmitted light, and cool interior shade. - Forests make
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9pZGVhbC1kcmVhbS1ncmFwaGljcy5vcmc#content"]

9. Source file: research/vegetation/README.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/cmVzZWFyY2gvdmVnZXRhdGlvbi9SRUFETUUubWQ#content
   Size: 14261 bytes, 225 lines
   Matching excerpt:
      # Vegetation rendering research This shelf collects papers and production material for dense vegetation that looks alive without making the renderer revolve around it. It is deliberately matched to Moppe's current split: - the terrain has a filtered grass material; the former per-blade mesh-shader experiment remains useful history but is not in the current game; - trees have a global habitat-driven canopy field and cheap chunked population, while distinct Atelier organisms form the detailed mixed-age stand; - moisture, elevation, slope, shore clearance, and tree line already provide ecological placement fields; - grass and tree vertices already share a continuous wind vocabulary. The most useful conclusion is not one representation for every distance. Keep individual geometry where its silhouette, parallax, interaction, or identity is visible; progressively turn it into filtered coverage and canopy appearance as it becomes subpixel. ## Start here 1. `wohllaib-2021-ghost-grass.pdf` is the closest production analogue to the current grass renderer: tile-local GPU generation, field sampling, culling, per-blade variation, animation, and LOD. 2. `kuth-2025-gpu-tree-generation.pdf` is the
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/cmVzZWFyY2gvdmVnZXRhdGlvbi9SRUFETUUubWQ#content"]

10. Source file: moppe/render/renderer.hh
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvcmVuZGVyL3JlbmRlcmVyLmho#content
   Size: 16980 bytes, 412 lines
   Matching excerpt:
      #ifndef MOPPE_RENDER_RENDERER_HH #define MOPPE_RENDER_RENDERER_HH #include <moppe/color.hh> #include <moppe/gfx/mat4.hh> #include <moppe/gfx/math.hh> #include <moppe/render/draw.hh> #include <moppe/render/texture_pixels.hh> #include <moppe/render/types.hh> #include <moppe/terrain/domain.hh> #include <cstdint> #include <span> #include <string> namespace moppe { namespace render { // Per-frame environment. The view matrix already includes the // camera-shake rotation; the right/up/forward basis is derived // from it and replaces the old GL_MODELVIEW_MATRIX readback for // billboards. struct FrameParams { Mat4 view; Mat4 proj; // reversed-Z perspective Vec3 camera_pos; Vec3 cam_right, cam_up, cam_forward; DisplayColor clear_color; // also the fog/haze color float fog_scale = 0.0f; Vec3 sun_dir; // world space, toward the sun // Art-directed sun products. Ambient is the strength/color fed // into the shaders' cool-sky / warm-ground hemisphere fill. DisplayColor sun_diffuse; DisplayColor sun_specular; DisplayColor ambient; // Art-direction multiplier applied after automatic exposure. Tools can // favor legibility without changing adaptation for normal gameplay. float exposure_bias = 1.0
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvcmVuZGVyL3JlbmRlcmVyLmho#content"]

Approximate matches

1. Source file: moppe/shaders/metal/forest.metal
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC9mb3Jlc3QubWV0YWw#content
   Size: 42342 bytes, 919 lines
  Score: 0.074
   Related excerpt:
      own = tree.up_radius.w; const float heading = 6.2831853 * forest_hash (organ.seed, 3u); const float3 reference = abs (organ.up.y) > 0.92 ? float3 (0.0, 0.0, 1.0) : float3 (0.0, 1.0, 0.0); const float3 basis = normalize (cross (organ.up, reference)); const float3 tangent = normalize (cross (basis, organ.up)); organ.across = basis * cos (heading) + tangent * sin (heading); organ.forward = normalize (cross (organ.across, organ.up)); const float crown = tree.up_radius.w; if (organ.proxy) { organ.centre = organ.root + organ.up * (organ.conifer ? 0.55 : 0.62) * organ.tree_height; organ.radius_x = crown * (organ.conifer ? 0.86 : 1.08); organ.radius_z = crown * (organ.conifer ? 0.80 : 0.98); organ.half_height = organ.tree_height * (organ.conifer ? 0.52 : 0.35); organ.bend = 0.38; organ.flutter = 0.04; } else if (organ.wood) { const float trunk_rise = organ.conifer ? 0.46 : 0.40; organ.centre = organ.root + organ.up * trunk_rise * organ.tree_height; // A spruce bole is slender for its height; the broadleaf keeps its // stouter stem. organ.radius_x = organ.tree_height * mix (0.018, 0.030, float (tree.identity.z) / 3.0) * (organ.conifer ? 0.62 : 1.0); organ.radius_z = organ.radius_x; organ.ha
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC9mb3Jlc3QubWV0YWw#content"]

2. Source file: moppe/shaders/metal/undergrowth.metal
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC91bmRlcmdyb3d0aC5tZXRhbA#content
   Size: 32205 bytes, 663 lines
  Score: 0.069
   Related excerpt:
      s.xy)) + float2 (tile.index)) * tile_world; const float2 root_xz = base + tile_world * float2 (0.03 + 0.94 * undergrowth_hash (identity, 1u), 0.03 + 0.94 * undergrowth_hash (identity, 2u)); const float3 ground_normal = undergrowth_ground_normal (root_xz, u, normals); const float ground = undergrowth_ground (root_xz, u, heights); const float3 root = float3 (root_xz.x, ground, root_xz.y); const float canopy = saturate (undergrowth_field (root_xz, u, forest).r); const float wet = saturate (undergrowth_field (root_xz, u, moisture).r); const float2 worn = saturate (undergrowth_field (root_xz, u, paths).rg); const float root_clear = 1.0 - saturate (max (worn.x, worn.y) * 1.6); const float root_water_depth = u.relief.w > 0.5 ? undergrowth_field (root_xz, u, water_levels).r - ground : 0.0; const float root_dry = 1.0 - smoothstep (0.002, 0.030, root_water_depth); const float root_shore = u.relief.w > 0.5 ? 1.0 - smoothstep (0.05, 1.35, abs (root_water_depth)) : 0.0; const float riparian = root_shore * root_dry * smoothstep (0.52, 0.80, ground_normal.y); // Grass is the ordinary answer. Ferns are an accent reserved for damp shade, // not a second carpet competing with it. const float fern_ha
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC91bmRlcmdyb3d0aC5tZXRhbA#content"]

3. Source file: moppe/shaders/metal/terrain.metal
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC90ZXJyYWluLm1ldGFs#content
   Size: 57815 bytes, 1189 lines
  Score: 0.041
   Related excerpt:
      onst float3 sand_c = mix (scree_c, sand_value * float3 (1.12, 1.03, 0.82), 0.82); // The extracted waterline describes lakes and rivers as well as the global // sea. Give every gentle shore a narrow water-worked margin instead of // letting turf meet translucent water at a mathematically sharp edge. const float shore_material = max (beach_coef, 0.78 * swash_zone) * (1.0 - submerged) * (1.0 - snow_coef) * smoothstep (0.48, 0.74, n.y); texel = mix (texel, sand_c, shore_material); // The sediment ledger is material information the simulation already // proved: fresh cuts expose raw regolith, deposition builds smooth // pale alluvium on gentle ground. Both defer to snow. if (u.params5.w > 0.5) { const float2 geo = geology; const float cut = smoothstep (0.12, 0.72, geo.r); const float fill = smoothstep (0.12, 0.72, geo.g); const float3 cut_c = mix (scree_c, scree_value * float3 (0.94, 0.84, 0.72), 0.55); texel = mix (texel, cut_c, cut * (1.0 - snow_coef) * 0.42); const float3 alluvium_c = mix (scree_c, sand_value * float3 (1.05, 1.00, 0.88), 0.70); const float fill_flat = smoothstep (0.78, 0.93, n.y); texel = mix (texel, alluvium_c, fill * fill_flat * (1.0 - snow_coef) * 0.5); } // Conc
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC90ZXJyYWluLm1ldGFs#content"]

4. Source file: docs/atelier-tree.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9hdGVsaWVyLXRyZWUubWQ#content
   Size: 9000 bytes, 168 lines
  Score: 0.032
   Related excerpt:
      # The Atelier tree The Atelier tree is a small proof that an organism can remain itself while its presentation changes completely. Run it as a wind-bent object: ```sh cmake --build build --target atelier ./build/atelier.app/Contents/MacOS/atelier --tree ``` or as a diagram of the same organism: ```sh ./build/atelier.app/Contents/MacOS/atelier --tree-diagram ``` Deterministic stills can be made without opening a window: ```sh ./build/atelier.app/Contents/MacOS/atelier \ --tree --capture /tmp/tree.png 7 ./build/atelier.app/Contents/MacOS/atelier \ --tree-diagram --capture /tmp/tree-diagram.png 7 ``` ## Three storeys `DirectedTreeTopology` is the combinatorial storey. It owns vertices, edges, incidence, generation, lineage, branch order, and the distinction between the shoot and root trees. It has no positions. `Tree::VertexState`, `Tree::EdgeState`, and `TreeEdgeForm` are the intrinsic storey. They are typed `Bundle`s over the vertex and edge domains. Rest length, radius, flexibility, azimuth, elevation, water potential, sugar potential, and bud vigor all belong here. Radius is derived from the terminal mass supported by an edge, so thickening is a property of the organism rather tha
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9hdGVsaWVyLXRyZWUubWQ#content"]

5. Source file: moppe/shaders/metal/uber.metal
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC91YmVyLm1ldGFs#content
   Size: 10615 bytes, 233 lines
  Score: 0.03
   Related excerpt:
      smoothstep (0.82, 0.98, base.a) * (1.0 - saturate (in.foliage)); lit += specular_tint * frame.sun_specular.rgb * sun_visibility * 0.22 * pow (max (dot (n, h), 0.0), 64.0) * specular_coverage; // Thin foliage transmits sunlight through its back face. This is not an // ambient rim: it follows the actual sun, respects terrain shadowing, and // is strongest at grazing view angles where a crown contains the most // overlapping leaves. Trunks and ordinary props carry zero foliage. const float leaf_back = pow (max (dot (-n, l), 0.0), 1.6) * saturate (in.foliage); const float leaf_depth = 0.35 + 0.65 * pow (1.0 - max (dot (n, v), 0.0), 2.0); const float3 transmission_tint (1.12, 0.92, 0.62); lit += base.rgb * frame.sun_diffuse.rgb * transmission_tint * sun_visibility * leaf_back * leaf_depth * 0.34; // A restrained sky rim separates moving silhouettes from the // landscape, especially on their shadowed side. Foliage keeps only a // trace of it: a crown already has its own back-light term above, and a // rim on every tree in a forest is not a silhouette, it is a haze. const float rim = pow (1.0 - max (dot (n, v), 0.0), 3.0) * (0.35 + 0.65 * max (n.y, 0.0)); lit += base.rgb * float3 (0.025, 
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC91YmVyLm1ldGFs#content"]

6. Source file: docs/renderer-design.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9yZW5kZXJlci1kZXNpZ24ubWQ#content
   Size: 53705 bytes, 862 lines
  Score: 0.014
   Related excerpt:
      OPPE_WATER_REFLECTION_SIGNAL=/tmp/signal.png` allocates quarter-linear- resolution targets per in-flight slot, rerasterizes the same standing-water surface into float world origins and shared optical normals, and traces the bounded Goal 0 terrain proxy. Raw radiance, hit normal, hit distance, and input/visibility/hit validity remain separate textures. The command `tools/capture-water-reflection-signal /tmp/signal.png` writes an untouched lake scene, a six-panel diagnostic, and a text report. Running water, composition, denoising, temporal history, and scene instances are excluded. This is a measured representation proof, not a graphics setting. A height field cannot express vertical water. `WaterfallSurface` therefore builds only a small explicit curtain for each selected hydrological nickpoint. The curtain follows the lip-to-foot direction, accelerates ballistically down the drop, widens toward the plunge pool, and uses the flowing-water shader's falling detail. Its cost is 216 vertices per waterfall, independent of river-alignment length. Falling spans are omitted from the horizontal field, so the curtain bridges lip and foot instead of overlapping a sloped water ramp. This is th
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9yZW5kZXJlci1kZXNpZ24ubWQ#content"]

7. Source file: research/vegetation/README.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/cmVzZWFyY2gvdmVnZXRhdGlvbi9SRUFETUUubWQ#content
   Size: 14261 bytes, 225 lines
  Score: 0.014
   Related excerpt:
      # Vegetation rendering research This shelf collects papers and production material for dense vegetation that looks alive without making the renderer revolve around it. It is deliberately matched to Moppe's current split: - the terrain has a filtered grass material; the former per-blade mesh-shader experiment remains useful history but is not in the current game; - trees have a global habitat-driven canopy field and cheap chunked population, while distinct Atelier organisms form the detailed mixed-age stand; - moisture, elevation, slope, shore clearance, and tree line already provide ecological placement fields; - grass and tree vertices already share a continuous wind vocabulary. The most useful conclusion is not one representation for every distance. Keep individual geometry where its silhouette, parallax, interaction, or identity is visible; progressively turn it into filtered coverage and canopy appearance as it becomes subpixel. ## Start here 1. `wohllaib-2021-ghost-grass.pdf` is the closest production analogue to the current grass renderer: tile-local GPU generation, field sampling, culling, per-blade variation, animation, and LOD. 2. `kuth-2025-gpu-tree-generation.pdf` is the
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/cmVzZWFyY2gvdmVnZXRhdGlvbi9SRUFETUUubWQ#content"]

8. Source file: moppe/render/metal/shader_types.h
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvcmVuZGVyL21ldGFsL3NoYWRlcl90eXBlcy5o#content
   Size: 15917 bytes, 374 lines
  Score: 0.013
   Related excerpt:
      eOceanUniforms { MoppeMat4 view_proj; MoppeMat4 unjittered_view_proj; MoppeMat4 previous_view_proj; MoppeMat4 light_matrix; // world -> biased shadow uv/z MoppeFloat4 camera_pos; MoppeFloat4 sun_dir; MoppeFloat4 sun_diffuse; MoppeFloat4 sun_specular; MoppeFloat4 ambient; MoppeFloat4 fog_color; // rgb; w = fog_scale MoppeFloat4 params; // x=time, y=sea level, z=cloudiness, // w=standing-water raster enabled MoppeFloat4 shore; // x=1/step_x, y=1/step_z, // z=height_scale, w=grid width (0=off) MoppeFloat4 world_offset; MoppeFloat4 shadow; // x=strength, y=shadow texel MoppeFloat4 tiles; // xy=origin tile indices, z=tiles per side, // w=fine radius (+: coarse pass discards // inside; -: lattice pass discards outside) MoppeFloat4 current; // x=flow raster enabled, y=geology raster enabled MoppeFloat4 temporal; // xy=input pixels, z=previous time, w=enabled }; // Undergrowth is generated, never stored. The object stage walks a window of // ground tiles around the camera and keeps the ones whose fields say // something grows there; the mesh stage turns each survivor into shoots. So // what crosses this boundary is where the camera is and how the world's // lattice is laid out -- never a p
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvcmVuZGVyL21ldGFsL3NoYWRlcl90eXBlcy5o#content"]

9. Source file: docs/forest-density-and-aggregates.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtZGVuc2l0eS1hbmQtYWdncmVnYXRlcy5tZA#content
   Size: 6732 bytes, 125 lines
  Score: 0.012
   Related excerpt:
      # Forest density and aggregates: the road to "it feels like a forest" Status: design intent, August 2026. The implemented LOD system this builds on is recorded in [forest LOD](forest-lod.md). The reference image remains the golden-hour spruce mockup (dense canopy, long shadows, sunbeams, dark interior); the gazetteer's forest-sunward / forest-shadowplay / forest-interior studies are the standing comparison views. ## The thesis: density is an unlock, not a cost The current stands are open enough to see through, so every tree presents as an individual silhouette -- the world reads as "a bunch of spiny trees", not a forest. A real spruce forest at any distance is mostly occlusion: a closed canopy surface on hillsides, dark interior gloom between trunks up close, individuals legible only at stand edges. Density also changes what the far field is allowed to be. A sparse stand seen from 500 m is still "several distinguishable individuals", and only per-tree geometry renders that honestly. A *closed* canopy seen from 500 m is statistically uniform -- a bumpy dark-green surface with known roughness and lighting -- and can be rendered as essentially one thing at a cost proportional to pixel
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtZGVuc2l0eS1hbmQtYWdncmVnYXRlcy5tZA#content"]

10. Source file: docs/forest-lod.md
   Resource: https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtbG9kLm1k#content
   Size: 7424 bytes, 128 lines
  Score: 0.012
   Related excerpt:
      # Forest LOD: the continuous-assembly design Status: implementation record of the conifer LOD system in `moppe/shaders/metal/forest.metal` as of August 2026, and of the principles and instruments that shaped it. The forward-looking plan is in [forest density and aggregates](forest-density-and-aggregates.md). ## The bet No tree mesh exists in CPU or GPU memory. Every frame, the object stage chooses each organism's detail from its projected size and the mesh stage grows boughs, tufts, and blades from a seed (Kuth et al. 2025, "Real-Time GPU Tree Generation", HPG; Sheaf `#EDURTK`, notes `#6S29CJ`). The memory saving is the headline but not the point: because nothing is retained, detail can be exactly right for the current frame and vary continuously -- the whole temporal-stability design below is only expressible because no baked LOD meshes exist. The costs are structural: generation is paid every frame even when the camera is still, stability of every generation input becomes a discipline instead of a given, and ray tracing would need per-frame acceleration-structure rebuilds. ## Principles, each learned the hard way 1. **Transitions finish while the tree is small in the frame.** The
   Read the complete file with read blocks=["https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtbG9kLm1k#content"]

### 32. Assistant tool calls

#### Tool call: `read`

```json
{
  "blocks": [
    "https://m.sheaf.less.rest/3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC9mb3Jlc3QubWV0YWw#content",
    "https://m.sheaf.less.rest/3CV92Z/source-files/ZG9jcy9mb3Jlc3QtbG9kLm1k#content"
  ]
}
```

### 33. Tool result: read

SOURCE FILE
Path: moppe/shaders/metal/forest.metal
Resource: /3CV92Z/source-files/bW9wcGUvc2hhZGVycy9tZXRhbC9mb3Jlc3QubWV0YWw#content
Size: 42342 bytes, 919 lines

// Production trees are assembled on the GPU from compact individuals.
//
// The object stage chooses an organism's projected detail and schedules a
// bounded set of reusable organs. The mesh stage expands one organ -- a stem,
// conifer bough tier, broadleaf crown lobe, or distant crown proxy. No complete
// tree mesh exists in CPU or GPU memory, and no alpha-tested foliage is used.

#include "common.h"

struct ForestVaryings {
  float4 position [[position]];
  float3 world_pos;
  float3 normal;
  float3 albedo;
  float exposure;
  float leaf;
  float2 motion [[center_no_perspective]];
};

struct ForestShadowVaryings {
  float4 position [[position]];
};

struct ForestPart {
  uint tree;
  uint part;
  uint lod;
  uint copy;
};

struct ForestPayload {
  uint count;
  ForestPart parts[MOPPE_FOREST_PAYLOAD_PARTS];
};

struct ForestShadowPayload {
  uint count;
  ForestPart parts[MOPPE_FOREST_OBJECT_THREADS];
};

using ForestMesh = metal::mesh<ForestVaryings,
                               void,
                               MOPPE_FOREST_MESH_VERTICES,
                               MOPPE_FOREST_MESH_PRIMITIVES,
                               metal::topology::triangle>;
using ForestShadowMesh = metal::mesh<ForestShadowVaryings,
                                     void,
                                     MOPPE_FOREST_MESH_VERTICES,
                                     MOPPE_FOREST_MESH_PRIMITIVES,
                                     metal::topology::triangle>;

static inline uint forest_mix (uint value) {
  value ^= value >> 16;
  value *= 0x7feb352du;
  value ^= value >> 15;
  value *= 0x846ca68bu;
  value ^= value >> 16;
  return value;
}

static inline float forest_hash (uint seed, uint lane) {
  return float (forest_mix (seed ^ lane * 0x9e3779b9u) & 0x00ffffffu) /
         float (0x01000000u);
}

static inline float3 forest_root (thread const MoppeForestInstance& tree,
                                  constant MoppeForestUniforms& u,
                                  uint copy,
                                  bool shadow) {
  float3 root = tree.root_height.xyz;
  const float2 period = u.world.xy;
  if (shadow) {
    const int tile = int (copy);
    root.x += float (tile % 3 - 1) * period.x;
    root.z += float (tile / 3 - 1) * period.y;
  } else {
    if (period.x > 0.0)
      root.x += round ((u.camera_pos.x - root.x) / period.x) * period.x;
    if (period.y > 0.0)
      root.z += round ((u.camera_pos.z - root.z) / period.y) * period.y;
  }
  return root;
}

static inline float3 forest_up (thread const MoppeForestInstance& tree) {
  return normalize (mix (float3 (0.0, 1.0, 0.0),
                         tree.up_radius.xyz,
                         tree.identity.y == 1u ? 0.20 : 0.28));
}

// A stable per-individual threshold avoids a circular LOD front in which a
// whole stand changes construction on the same frame.
static inline float forest_lod_threshold (uint seed) {
  return 0.88 + 0.24 * forest_hash (seed, 191u);
}

// A conifer's bough count as a continuous function of projected size. Detail
// must arrive organ by organ, never construction by construction: a discrete
// representation switch concentrates its whole visual error into one frame,
// while organs that grow in one at a time spread it below notice (Kuth 2025,
// measured in Fig. 12 of the paper; #6S29CJ in the research library).
static inline float forest_bough_count (float pixels, float threshold) {
  // The ramp saturates by ninety projected scene pixels -- at the game's
  // lens, a tree around a tenth of the frame's height. Perception, not
  // geometry, sets this point: a rider's eye is exquisitely sensitive to
  // elements appearing on a tree large enough to watch, so every arrival
  // must happen while the whole crown is a small figure in the frame.
  // Saturating at three hundred meant crowns still assembling while they
  // filled a third of the screen, which read as the forest morphing. The
  // floor of twenty-one keeps the sparsest assembly reading as a small
  // solid tree: below that a spruce degenerates into a pole with stubs,
  // which no distance excuses.
  return clamp (
    (pixels - 14.0 * threshold) * 63.0 / (76.0 * threshold), 21.0, 63.0);
}

// The ramp measures projected pixels, so on its own a completion point is a
// completion DISTANCE proportional to tree height: a twenty-metre tree
// finishes at forty metres, but a five-metre sapling keeps growing until the
// walker stands beside it. Boosting a short tree's measure makes every tree
// complete near forty-five metres, so growth happens where a bough is around
// a pixel, never in front of the walker. Gates and bundling keep the raw
// measure: a distant sapling is still a distant sapling.
static inline float forest_bough_measure (float pixels, float tree_height) {
  return pixels * clamp (22.5 / max (tree_height, 0.01), 1.0, 4.5);
}

// Emission rank to whorl-grid slot. The first nine ranks sketch the whole
// silhouette with one bough per whorl, bottom to top. Later ranks add one
// spoke per whorl per round, top whorl first, so the crown densifies
// evenly everywhere instead of completing region by region: filling the
// top whorls last read as the tree growing taller against the sky, and
// filling any whorl's spokes consecutively read as a lopsided branch.
// Consecutive arrivals within a whorl land on opposite azimuths.
//
// TOTAL over any rank: bundling rounds the scheduled rank range up past
// the sixty-three real slots (a two-bough meshlet on a full crown asks
// for rank sixty-three), and an out-of-range read here fed NaN through
// the bough frame -- zero grow times NaN is still NaN -- which rasterized
// as screen-sized stretched triangles. Phantom ranks wrap onto valid
// slots and their zero grow keeps them degenerate.
static inline uint forest_bough_slot (uint rank) {
  if (rank < 9u)
    return rank * 7u;
  const uint fill = (rank - 9u) % 54u;
  const uint spread[6] = { 4u, 2u, 6u, 1u, 5u, 3u };
  return (8u - fill % 9u) * 7u + spread[fill / 9u];
}

// Mesh-group coalescing for the middle band: a small distant assembly
// bundles four boughs of three tufts into each meshlet instead of paying
// a mostly idle meshlet per bough, the same cure Kuth 2025 applies to
// leaves. A hero bough keeps its own meshlet for now: two attempts at
// packing a pair (234 vertices of output) produced garbage triangles in
// motion even with fifty-six-byte varyings, so the real interpolant
// allocation evidently sits closer to Metal's silent 16 KB mesh-output
// ceiling than the struct arithmetic suggests. Retry only under the
// Metal debugger, not by arithmetic. The boundary aligns with ramp
// saturation, so BOTH representation changes finish while the tree is
// around a tenth of the frame, and the per-individual threshold keeps a
// stand from crossing on the same frame.
static inline uint forest_bough_bundle (uint pixel_code, float threshold) {
  return float (pixel_code) < 90.0 * threshold ? 4u : 1u;
}

static inline uint forest_bough_tufts (uint bundle) {
  return bundle == 4u ? 3u : 13u;
}

static inline uint forest_part_count (
  float pixels, uint pixel_code, float height, uint seed, bool conifer) {
  const float threshold = forest_lod_threshold (seed);
  if (pixels < 2.0)
    return 0u;
  if (pixels < 14.0 * threshold)
    return 1u;
  if (conifer) {
    const float count = forest_bough_count (
      forest_bough_measure (float (pixel_code), height), threshold);
    const float bundle = float (forest_bough_bundle (pixel_code, threshold));
    return 1u + uint (ceil (count / bundle));
  }
  if (pixels < 48.0 * threshold)
    return 3u;
  return MOPPE_FOREST_PARTS_PER_TREE;
}

// One object threadgroup considers one individual: thread zero reads the
// organism and chooses its projected detail, then all threads cooperate to
// schedule its organs. A hero assembly owns the whole payload, so dense
// stands never make neighbouring trees drop their boughs.
[[object]] void forest_object (object_data ForestPayload& payload [[payload]],
                               metal::mesh_grid_properties mesh_grid,
                               uint thread_id [[thread_index_in_threadgroup]],
                               uint3 group [[threadgroup_position_in_grid]],
                               constant MoppeForestUniforms& u
                               [[buffer (MOPPE_BUF_FRAME)]],
                               device const MoppeForestInstance* trees
                               [[buffer (MOPPE_BUF_FOREST)]]) {
  // Every thread derives the same cheap verdict for its tree, so the
  // organ schedule needs no shared memory and no barrier.
  const uint tree_index = group.x;
  const uint tree_count = uint (u.world.z);
  uint parts = 0u;
  uint pixel_code = 0u;
  if (tree_index < tree_count) {
    const MoppeForestInstance tree = trees[tree_index];
    const float3 root = forest_root (tree, u, 4u, false);
    const float3 up = forest_up (tree);
    const float height = tree.root_height.w;
    // The bound must contain the whole organism, root to tip to bough
    // reach: a crown-sized sphere at mid-height excludes the trunk, and a
    // tree the rider passes under then fails the frustum test and
    // vanishes while filling the screen.
    const float radius = 0.55 * height + 1.4 * tree.up_radius.w;
    const float3 centre = root + up * (0.52 * height);
    const float4 clip = u.view_proj * float4 (centre, 1.0);
    const float clip_radius =
      radius * max (abs (u.view_proj[0][0]), abs (u.view_proj[1][1]));
    const bool visible = clip.w > -radius &&
                         abs (clip.x) < clip.w + clip_radius &&
                         abs (clip.y) < clip.w + clip_radius;
    float pixels = 0.0;
    if (visible) {
      // Projected size from the centre's view depth rather than endpoint
      // projection: the same smooth, monotone measure at every approach
      // angle. Endpoint differences collapse discontinuously the moment the
      // root or tip crosses the camera plane, which used to pop a passing
      // tree's whole complement of boughs in one frame. The clamped depth
      // saturates the measure once the rider is beside the organism.
      const float focal = abs (u.view_proj[1][1]);
      pixels = height * focal * 0.5 * u.temporal.y / max (clip.w, 0.6);
      // The scene path never tiles periodic copies, so the copy slot carries
      // the projected size instead: the mesh stage reads it back to grow the
      // newest boughs in continuously.
      pixel_code = min (uint (pixels), 65535u);
      parts = forest_part_count (
        pixels, pixel_code, height, tree.identity.x, tree.identity.y == 1u);
    }
  }
  if (thread_id == 0u) {
    payload.count = parts;
    mesh_grid.set_threadgroups_per_grid (uint3 (parts, 1, 1));
  }
  for (uint part = thread_id; part < parts; part += MOPPE_FOREST_OBJECT_THREADS)
    payload.parts[part] = { tree_index, part, parts, pixel_code };
}

// Shadow detail is deliberately coarser: every periodic image contributes one
// opaque crown proxy. This is enough to put forest-scale occlusion on the
// ground without multiplying the scene-detail budget by the shadow map.
[[object]] void forest_shadow_object (
  object_data ForestShadowPayload& payload [[payload]],
  metal::mesh_grid_properties mesh_grid,
  uint thread_id [[thread_index_in_threadgroup]],
  uint3 group [[threadgroup_position_in_grid]],
  constant MoppeForestUniforms& u [[buffer (MOPPE_BUF_FRAME)]],
  device const MoppeForestInstance* trees [[buffer (MOPPE_BUF_FOREST)]]) {
  threadgroup atomic_uint emitted;
  if (thread_id == 0u)
    atomic_store_explicit (&emitted, 0u, metal::memory_order_relaxed);
  threadgroup_barrier (metal::mem_flags::mem_threadgroup);

  const uint tree_count = uint (u.world.z);
  const bool local = u.world.w > 0.5;
  const uint image_count = local ? 1u : 9u;
  const uint candidate = group.x * MOPPE_FOREST_OBJECT_THREADS + thread_id;
  if (candidate < tree_count * image_count) {
    const uint tree = candidate % tree_count;
    const uint copy = local ? 4u : candidate / tree_count;
    const MoppeForestInstance individual = trees[tree];
    const float3 root = forest_root (individual, u, copy, !local);
    const float3 centre =
      root + forest_up (individual) * 0.56 * individual.root_height.w;
    const float4 clip = u.view_proj * float4 (centre, 1.0);
    const float radius =
      max (individual.up_radius.w, 0.5 * individual.root_height.w);
    const float clip_radius =
      radius * max (abs (u.view_proj[0][0]), abs (u.view_proj[1][1]));
    if (clip.w > -radius && abs (clip.x) < clip.w + clip_radius &&
        abs (clip.y) < clip.w + clip_radius) {
      const uint slot =
        atomic_fetch_add_explicit (&emitted, 1u, metal::memory_order_relaxed);
      payload.parts[slot] = { tree, 0u, 1u, copy };
    }
  }

  threadgroup_barrier (metal::mem_flags::mem_threadgroup);
  if (thread_id == 0u) {
    payload.count =
      atomic_load_explicit (&emitted, metal::memory_order_relaxed);
    mesh_grid.set_threadgroups_per_grid (uint3 (payload.count, 1, 1));
  }
}

struct ForestOrgan {
  float3 root;
  float3 centre;
  float3 up;
  float3 across;
  float3 forward;
  float radius_x;
  float radius_z;
  float half_height;
  float tree_height;
  float bend;
  float flutter;
  uint seed;
  bool wood;
  bool conifer;
  bool proxy;
  // A frond organ carries feathered conifer boughs: combs of needle tufts
  // along drooping axes. Every non-proxy conifer crown is made of these and
  // nothing else. Bough geometry is evaluated per vertex from a stable
  // rank, so one meshlet holds one near bough at thirteen tufts or bundles
  // four distant boughs at three.
  bool frond;
  uint bundle;
  uint rank;
  // Continuous LOD: the fractional bough count this crown has reached, and
  // how much the surviving tufts widen to hold coverage while neighbours
  // are still absent.
  float count;
  float boost;
  float crown;
};

static inline ForestOrgan forest_organ (thread const MoppeForestInstance& tree,
                                        constant MoppeForestUniforms& u,
                                        ForestPart part,
                                        bool shadow) {
  ForestOrgan organ;
  organ.root = forest_root (tree, u, part.copy, shadow);
  organ.up = forest_up (tree);
  organ.tree_height = tree.root_height.w;
  organ.seed = tree.identity.x;
  organ.conifer = tree.identity.y == 1u;
  organ.proxy = part.lod == 1u;
  organ.wood = part.part == 0u && !organ.proxy;
  // A conifer above proxy size is trunk plus boughs and nothing else: no
  // shell or cone primitive exists at any visible tier.
  organ.frond = organ.conifer && !organ.proxy && part.part >= 1u;
  organ.bundle = 1u;
  organ.rank = 0u;
  organ.count = 0.0;
  organ.boost = 1.0;
  organ.crown = tree.up_radius.w;
  const float heading = 6.2831853 * forest_hash (organ.seed, 3u);
  const float3 reference =
    abs (organ.up.y) > 0.92 ? float3 (0.0, 0.0, 1.0) : float3 (0.0, 1.0, 0.0);
  const float3 basis = normalize (cross (organ.up, reference));
  const float3 tangent = normalize (cross (basis, organ.up));
  organ.across = basis * cos (heading) + tangent * sin (heading);
  organ.forward = normalize (cross (organ.across, organ.up));

  const float crown = tree.up_radius.w;
  if (organ.proxy) {
    organ.centre =
      organ.root + organ.up * (organ.conifer ? 0.55 : 0.62) * organ.tree_height;
    organ.radius_x = crown * (organ.conifer ? 0.86 : 1.08);
    organ.radius_z = crown * (organ.conifer ? 0.80 : 0.98);
    organ.half_height = organ.tree_height * (organ.conifer ? 0.52 : 0.35);
    organ.bend = 0.38;
    organ.flutter = 0.04;
  } else if (organ.wood) {
    const float trunk_rise = organ.conifer ? 0.46 : 0.40;
    organ.centre = organ.root + organ.up * trunk_rise * organ.tree_height;
    // A spruce bole is slender for its height; the broadleaf keeps its
    // stouter stem.
    organ.radius_x = organ.tree_height *
                     mix (0.018, 0.030, float (tree.identity.z) / 3.0) *
                     (organ.conifer ? 0.62 : 1.0);
    organ.radius_z = organ.radius_x;
    organ.half_height = trunk_rise * organ.tree_height;
    organ.bend = 0.12;
    organ.flutter = 0.0;
  } else if (organ.frond) {
    // The organ keeps the tree frame; each vertex derives its bough from a
    // stable rank. Emission ranks permute through the nine-by-seven whorl
    // grid by a coprime stride, so a partially grown crown samples every
    // whorl evenly and each bough keeps its geometry at every count. The
    // newest boughs grow in from nothing as projected size increases, and
    // while the crown is sparse the surviving tufts widen to hold its
    // coverage: detail migrates continuously, never switching.
    organ.count = forest_bough_count (
      forest_bough_measure (float (part.copy), organ.tree_height),
      forest_lod_threshold (organ.seed));
    organ.bundle =
      forest_bough_bundle (part.copy, forest_lod_threshold (organ.seed));
    organ.rank = (part.part - 1u) * organ.bundle;
    // Survivor widening: conservation of foliage. A crown must never thin
    // with distance -- a removed bough's mass moves into the survivors, so
    // the square-root area ratio applies at FULL strength at every count.
    // It converges to exactly one as the complement completes, so near
    // geometry is stable without any fade, and mid-band trees keep the
    // same visual density they have up close.
    const float sparse = clamp (sqrt (63.0 / max (organ.count, 1.0)), 1.0, 2.0);
    organ.boost =
      sparse * sqrt (13.0 / float (forest_bough_tufts (organ.bundle)));
    organ.centre = organ.root;
    organ.radius_x = crown;
    organ.radius_z = crown;
    organ.half_height = 0.0;
    organ.bend = 0.55;
    organ.flutter = 0.38;
  } else {
    const float lobe = float (part.part - 1u);
    const float count = float (max (part.lod - 1u, 1u));
    const float turn =
      2.3999632 * lobe + 0.55 * forest_hash (organ.seed, part.part + 51u);
    const float ring =
      count > 2.0 ? mix (0.26, 0.58, fract (lobe * 0.61)) : 0.30;
    const float rise = 0.56 + 0.26 * forest_hash (organ.seed, part.part + 61u);
    organ.centre =
      organ.root + organ.up * rise * organ.tree_height +
      (organ.across * cos (turn) + organ.forward * sin (turn)) * crown * ring;
    const float scale = count > 2.0 ? 0.37 : 0.68;
    organ.radius_x =
      crown * scale * (0.84 + 0.28 * forest_hash (organ.seed, part.part + 71u));
    organ.radius_z =
      crown * scale * (0.82 + 0.30 * forest_hash (organ.seed, part.part + 79u));
    organ.half_height =
      crown * scale * (0.78 + 0.38 * forest_hash (organ.seed, part.part + 83u));
    organ.bend = 0.40 + 0.48 * rise;
    organ.flutter = 0.34;
  }
  return organ;
}

static inline float3 forest_palette (thread const MoppeForestInstance& tree,
                                     thread const ForestOrgan& organ,
                                     float exposure) {
  const float wet = tree.ecology.y;
  const float cover = tree.ecology.x;
  const float variation = forest_hash (tree.identity.x, 97u) - 0.5;
  if (organ.wood) {
    float3 bark = organ.conifer ? float3 (0.170, 0.140, 0.115)
                                : float3 (0.235, 0.195, 0.150);
    bark *= 0.88 + 0.18 * wet + 0.16 * variation;
    return bark * (0.60 + 0.40 * exposure);
  }
  // Individuals sit on a warm-olive to cool blue-green axis in addition to
  // the brightness spread; a stand of one green reads as painted, not grown.
  const float hue = forest_hash (tree.identity.x, 113u) - 0.5;
  float3 leaf =
    organ.conifer ? float3 (0.148, 0.280, 0.152) : float3 (0.275, 0.455, 0.165);
  leaf *= float3 (1.08 - 0.20 * wet, 0.88 + 0.26 * wet, 0.90 + 0.16 * cover);
  leaf *= float3 (1.0 + 0.30 * hue, 1.0, 1.0 - 0.34 * hue);
  leaf *= 0.90 + 0.24 * variation;
  // Spruce is read by the contrast between dark needle mass and its lit
  // fringe, so the conifer exposure range runs deeper and brighter.
  return leaf *
         (organ.conifer ? 0.50 + 0.58 * exposure : 0.55 + 0.50 * exposure);
}

static inline float forest_ring_level (uint ring, bool conifer, bool proxy) {
  // A conifer volume is a spire: wide low skirt, straight taper, high apex.
  if (conifer)
    return float3 (-0.52, 0.02, 0.54)[ring];
  return float3 (-0.42, -0.02, 0.40)[ring];
}

static inline float forest_ring_radius (uint ring, bool conifer, bool proxy) {
  if (conifer)
    return float3 (1.00, 0.58, 0.22)[ring];
  return float3 (0.72, 1.00, 0.76)[ring];
}

struct ForestPoint {
  float3 position;
  float3 normal;
  float exposure;
};

static inline ForestPoint forest_vertex (thread const ForestOrgan& organ,
                                         uint vertex_index) {
  ForestPoint point;
  if (organ.wood) {
    // Twelve slightly irregular sides: a trunk is the one organ the camera
    // meets at arm's length, where an even octagonal cone reads as a bollard.
    const uint side = vertex_index % 12u;
    const uint ring = vertex_index / 12u;
    const float turn = 6.2831853 * float (side) / 12.0 +
                       0.06 * (forest_hash (organ.seed, side + 171u) - 0.5);
    const float taper = ring == 0u ? 1.70 : 0.30;
    const float girth =
      0.91 + 0.18 * forest_hash (organ.seed, side * 7u + ring + 177u);
    const float3 radial =
      organ.across * cos (turn) + organ.forward * sin (turn);
    point.position =
      organ.centre +
      organ.up * (ring == 0u ? -organ.half_height : organ.half_height) +
      radial * organ.radius_x * taper * girth;
    point.normal = radial;
    point.exposure = ring == 0u ? 0.18 : 0.72;
    return point;
  }

  if (organ.frond) {
    // Each bough is a comb of needle tufts along a drooping axis: an apex
    // plus four separated two-vertex blades per tuft. A twig is mostly air,
    // so the blades stay disconnected: connected fans read as paddles, and
    // overlapping paddles rebuild the very cone this tier replaced. The
    // bough frame derives per vertex from the stable rank, which is what
    // lets a bundle pack several distant boughs into one meshlet. In the
    // bundled band a whole tuft is a few pixels, where four overlapping
    // micro-triangles cost quadruple fragment work for no resolvable
    // difference, so the coarse tuft is one quad spanning the same splay
    // (Kuth 2025's pixels-per-triangle discipline at organ scale).
    const bool coarse = organ.bundle == 4u;
    const uint per_tuft = coarse ? 4u : 9u;
    const uint tufts = forest_bough_tufts (organ.bundle);
    const uint stride = tufts * per_tuft;
    const uint rank = organ.rank + vertex_index / stride;
    const uint rem = vertex_index % stride;
    const uint fan = rem / per_tuft;
    const uint local = rem % per_tuft;
    const uint slot = forest_bough_slot (rank);
    const uint whorl = slot / 7u;
    const uint spoke = slot % 7u;
    const float t = float (whorl) / 8.0;
    const float rise = mix (0.22, 0.97, t);
    const float turn =
      6.2831853 * (float (spoke) / 7.0 + 0.618034 * float (whorl) +
                   0.07 * (forest_hash (organ.seed, slot + 211u) - 0.5));
    const float3 along =
      normalize (organ.across * cos (turn) + organ.forward * sin (turn));
    const float3 side = normalize (cross (organ.up, along));
    const float reach = 0.72 + 0.50 * forest_hash (organ.seed, slot + 223u);
    // Large low boughs fade in over many ranks and small high ones over
    // few, so whatever arrives while the rider is close changes the crown
    // imperceptibly per frame. The floor complement never arrives -- those
    // boughs exist at every distance -- so it stands at full growth;
    // half-grown permanent boughs would leak crown mass at the far end.
    const float grow =
      rank < 21u
        ? 1.0
        : saturate ((organ.count - float (rank)) / (3.0 + 10.0 * (1.0 - t)));
    const float length = organ.crown * mix (1.35, 0.18, t) * reach * grow;
    const float3 origin = organ.root + organ.up * rise * organ.tree_height;
    const float s = mix (0.08, 1.0, float (fan) / float (max (tufts - 1u, 1u)));
    // The axis droops in proportion to its reach, so long lower boughs
    // sweep down through the band beneath their whorl.
    const float droop = 0.10 - 0.40 * s * s;
    const float wobble = fan % 2u == 0u ? 1.0 : -1.0;
    const float3 axis_point =
      origin + along * length * s +
      side * length * 0.12 * wobble * forest_hash (organ.seed, fan + 17u) +
      organ.up * length * droop;
    const float radius = length * mix (0.22, 0.10, s) * organ.boost *
                         (0.80 + 0.40 * forest_hash (organ.seed, fan + 31u));
    if (coarse) {
      // Two triangles spanning the arc the blades splay over: outer
      // corners at full reach and rim depth, inner corners high and
      // tucked toward the axis, so the sheet is TENTED like the fan --
      // a flat quad in the bough plane disappears edge-on, and the
      // tuft's silhouette lives in the apex-to-rim drop.
      const bool inner = local == 1u || local == 2u;
      const float arc =
        mix (-2.2, 2.2, float (local) / 3.0) +
        0.20 * (forest_hash (organ.seed, fan * 13u + local + 41u) - 0.5);
      const float needle =
        0.62 + 0.55 * forest_hash (organ.seed, fan * 29u + local + 57u);
      const float3 rim_dir = normalize (along * cos (arc) + side * sin (arc));
      point.position = axis_point +
                       rim_dir * radius * needle * (inner ? 0.40 : 1.0) -
                       organ.up * radius * (inner ? 0.06 : 0.55);
      point.normal = normalize (organ.up + rim_dir * 0.35);
      point.exposure = saturate (0.34 + 0.50 * s + 0.14 * needle);
      return point;
    }
    if (local == 0u) {
      point.position = axis_point;
      point.normal = normalize (organ.up + along * 0.2);
      point.exposure = 0.32;
      return point;
    }
    const uint rim = local - 1u;
    const uint blade = rim / 2u;
    const float edge = rim % 2u == 0u ? -1.0 : 1.0;
    const float arc =
      mix (-2.5, 2.5, float (blade) / 3.0) + edge * 0.42 +
      0.20 * (forest_hash (organ.seed, fan * 13u + blade + 41u) - 0.5);
    const float needle =
      0.62 + 0.55 * forest_hash (organ.seed, fan * 29u + blade + 57u);
    const float3 rim_dir = normalize (along * cos (arc) + side * sin (arc));
    point.position =
      axis_point + rim_dir * radius * needle - organ.up * radius * 0.55;
    point.normal = normalize (organ.up + rim_dir * 0.35);
    point.exposure = saturate (0.34 + 0.50 * s + 0.14 * needle);
    return point;
  }

  if (vertex_index == 0u || vertex_index == 31u) {
    const float sign = vertex_index == 0u ? -1.0 : 1.0;
    point.position = organ.centre + organ.up * organ.half_height * sign;
    point.normal = organ.up * sign;
    point.exposure = vertex_index == 0u ? 0.28 : 1.0;
    return point;
  }

  const uint offset = vertex_index - 1u;
  const uint ring = offset / 10u;
  const uint side = offset % 10u;
  const float base_turn = 6.2831853 * float (side) / 10.0;
  const float turn =
    base_turn +
    0.09 * (forest_hash (organ.seed, ring * 17u + side + 111u) - 0.5);
  // Conifer shells wear a rougher edge than broadleaf lobes: the cone is a
  // mass reading, and an even rim is what makes it read as felt.
  const float jitter = organ.conifer ? 0.40 : 0.22;
  const float r = forest_ring_radius (ring, organ.conifer, organ.proxy) *
                  (1.0 - 0.5 * jitter +
                   jitter * forest_hash (organ.seed, ring * 23u + side + 131u));
  const float y = forest_ring_level (ring, organ.conifer, organ.proxy) +
                  (organ.conifer ? 0.10 : 0.07) *
                    (forest_hash (organ.seed, ring * 29u + side + 151u) - 0.5);
  const float x = cos (turn) * organ.radius_x * r;
  const float z = sin (turn) * organ.radius_z * r;
  point.position = organ.centre + organ.across * x + organ.forward * z +
                   organ.up * organ.half_height * y;
  point.normal = normalize (organ.across * (x / max (organ.radius_x, 0.001)) +
                            organ.forward * (z / max (organ.radius_z, 0.001)) +
                            organ.up * y * 0.72);
  point.exposure = saturate (0.40 + 0.38 * y + 0.22 * point.normal.y);
  return point;
}

template <typename Mesh>
static inline void forest_indices (thread Mesh& out,
                                   thread const ForestOrgan& organ,
                                   uint primitive) {
  uint3 triangle;
  if (organ.wood) {
    const uint side = primitive / 2u;
    const uint next = (side + 1u) % 12u;
    triangle = primitive % 2u == 0u ? uint3 (side, next, 12u + next)
                                    : uint3 (side, 12u + next, 12u + side);
  } else if (organ.frond) {
    const bool coarse = organ.bundle == 4u;
    const uint tufts = forest_bough_tufts (organ.bundle);
    const uint per_prims = coarse ? 2u : 4u;
    const uint per_verts = coarse ? 4u : 9u;
    const uint bough = primitive / (tufts * per_prims);
    const uint rem = primitive % (tufts * per_prims);
    const uint fan = rem / per_prims;
    const uint blade = rem % per_prims;
    const uint base = bough * tufts * per_verts + fan * per_verts;
    triangle = coarse
                 ? (blade == 0u ? uint3 (base, base + 1u, base + 2u)
                                : uint3 (base, base + 2u, base + 3u))
                 : uint3 (base, base + 1u + 2u * blade, base + 2u + 2u * blade);
  } else if (primitive < 10u) {
    const uint side = primitive;
    triangle = uint3 (0u, 1u + (side + 1u) % 10u, 1u + side);
  } else if (primitive < 50u) {
    const uint bridge = (primitive - 10u) / 20u;
    const uint local = (primitive - 10u) % 20u;
    const uint side = local / 2u;
    const uint a = 1u + bridge * 10u + side;
    const uint b = 1u + bridge * 10u + (side + 1u) % 10u;
    const uint c = a + 10u;
    const uint d = b + 10u;
    triangle = local % 2u == 0u ? uint3 (a, b, d) : uint3 (a, d, c);
  } else {
    const uint side = primitive - 50u;
    triangle = uint3 (31u, 21u + side, 21u + (side + 1u) % 10u);
  }
  const uint slot = primitive * 3u;
  out.set_index (slot + 0u, triangle.x);
  out.set_index (slot + 1u, triangle.y);
  out.set_index (slot + 2u, triangle.z);
}

[[mesh]] void forest_mesh (ForestMesh out,
                           object_data const ForestPayload& payload [[payload]],
                           uint mesh_id [[threadgroup_position_in_grid]],
                           uint thread_id [[thread_index_in_threadgroup]],
                           constant MoppeForestUniforms& u
                           [[buffer (MOPPE_BUF_FRAME)]],
                           device const MoppeForestInstance* trees
                           [[buffer (MOPPE_BUF_FOREST)]]) {
  const ForestPart part = payload.parts[min (mesh_id, payload.count - 1u)];
  const MoppeForestInstance tree = trees[part.tree];
  const ForestOrgan organ = forest_organ (tree, u, part, false);
  const uint tufts = organ.frond ? forest_bough_tufts (organ.bundle) : 0u;
  const uint per_tuft_verts = organ.bundle == 4u ? 4u : 9u;
  const uint per_tuft_prims = organ.bundle == 4u ? 2u : 4u;
  const uint vertices = organ.wood    ? 24u
                        : organ.frond ? organ.bundle * tufts * per_tuft_verts
                                      : 32u;
  const uint primitives = organ.wood    ? 24u
                          : organ.frond ? organ.bundle * tufts * per_tuft_prims
                                        : 60u;
  if (thread_id == 0u)
    out.set_primitive_count (primitives);

  if (thread_id < vertices) {
    const ForestPoint base = forest_vertex (organ, thread_id);
    const float rise = saturate (dot (base.position - organ.root, organ.up) /
                                 max (organ.tree_height, 0.01));
    const float bend = organ.bend * rise * rise;
    const float flutter = organ.flutter * rise;
    const float3 current =
      moppe_wind (base.position, bend, flutter, u.params.x);
    const float3 previous =
      moppe_wind (base.position, bend, flutter, u.temporal.z);
    ForestVaryings v;
    v.position = u.view_proj * float4 (current, 1.0);
    v.world_pos = current;
    v.normal = base.normal;
    v.albedo = forest_palette (tree, organ, base.exposure);
    v.exposure = base.exposure;
    v.leaf = organ.wood ? 0.0 : 1.0;
    v.motion =
      moppe_motion_vector (u.unjittered_view_proj * float4 (current, 1.0),
                           u.previous_view_proj * float4 (previous, 1.0),
                           u.temporal.xy);
    out.set_vertex (thread_id, v);
  }
  if (thread_id < primitives)
    forest_indices (out, organ, thread_id);
}

[[mesh]] void forest_shadow_mesh (
  ForestShadowMesh out,
  object_data const ForestShadowPayload& payload [[payload]],
  uint mesh_id [[threadgroup_position_in_grid]],
  uint thread_id [[thread_index_in_threadgroup]],
  constant MoppeForestUniforms& u [[buffer (MOPPE_BUF_FRAME)]],
  device const MoppeForestInstance* trees [[buffer (MOPPE_BUF_FOREST)]]) {
  const ForestPart part = payload.parts[min (mesh_id, payload.count - 1u)];
  const MoppeForestInstance tree = trees[part.tree];
  const bool world_map = u.world.w <= 0.5;
  const ForestOrgan organ = forest_organ (tree, u, part, world_map);
  // The camera-local map also carries a trunk prism. A ground shadow reads
  // as a shadow only when it can be attributed: the sun-elongated trunk line
  // attaches the crown's shade to its tree. Whole-world images stay
  // crown-only, where a trunk is sub-texel anyway.
  const bool trunk = !world_map;
  ForestOrgan stem = organ;
  if (trunk)
    stem = forest_organ (tree, u, { part.tree, 0u, 2u, part.copy }, world_map);
  const uint vertices = trunk ? 28u : 12u;
  const uint primitives = trunk ? 36u : 20u;
  if (thread_id == 0u)
    out.set_primitive_count (primitives);
  if (thread_id < vertices) {
    float3 position;
    if (thread_id == 0u || thread_id == 11u) {
      position = organ.centre +
                 organ.up * organ.half_height * (thread_id == 0u ? -1.0 : 1.0);
    } else if (thread_id < 11u) {
      const uint side = thread_id - 1u;
      const float turn = 6.2831853 * float (side) / 10.0;
      position = organ.centre + organ.across * cos (turn) * organ.radius_x +
                 organ.forward * sin (turn) * organ.radius_z;
    } else {
      const uint index = thread_id - 12u;
      const uint side = index % 8u;
      const uint ring = index / 8u;
      const float turn = 6.2831853 * float (side) / 8.0;
      const float taper = ring == 0u ? 1.70 : 0.30;
      const float3 radial =
        stem.across * cos (turn) + stem.forward * sin (turn);
      position = stem.centre +
                 stem.up * (ring == 0u ? -stem.half_height : stem.half_height) +
                 radial * stem.radius_x * taper;
    }
    ForestShadowVaryings v;
    v.position = u.view_proj * float4 (position, 1.0);
    out.set_vertex (thread_id, v);
  }
  if (thread_id < primitives) {
    uint3 triangle;
    if (thread_id < 20u) {
      const uint side = thread_id / 2u;
      const uint next = (side + 1u) % 10u;
      triangle = thread_id % 2u == 0u ? uint3 (0u, 1u + next, 1u + side)
                                      : uint3 (11u, 1u + side, 1u + next);
    } else {
      const uint prim = thread_id - 20u;
      const uint side = prim / 2u;
      const uint next = (side + 1u) % 8u;
      triangle = (prim % 2u == 0u ? uint3 (side, next, 8u + next)
                                  : uint3 (side, 8u + next, 8u + side)) +
                 12u;
    }
    const uint slot = thread_id * 3u;
    out.set_index (slot + 0u, triangle.x);
    out.set_index (slot + 1u, triangle.y);
    out.set_index (slot + 2u, triangle.z);
  }
}

fragment MoppeTemporalOutput forest_fragment (ForestVaryings in [[stage_in]],
                                              bool front_facing
                                              [[front_facing]],
                                              constant MoppeForestUniforms& u
                                              [[buffer (MOPPE_BUF_FRAME)]],
                                              depth2d<float> shadow_map
                                              [[texture (MOPPE_TEX_SHADOW)]]) {
  const float3 n = normalize (front_facing ? in.normal : -in.normal);
  const float3 l = normalize (u.sun_dir.xyz);
  const float3 to_eye = u.camera_pos.xyz - in.world_pos;
  const float distance = length (to_eye);
  const float fog =
    moppe_relief_haze (moppe_distance_fog (distance, u.fog_color.w),
                       in.world_pos.y,
                       u.params.z,
                       u.params.w);
  // The shared shadow map contains deliberately coarse tree proxies, so a
  // plain sample from inside a detailed crown would interpret that proxy as
  // precise self-occlusion and black out the organism. Instead the compare
  // gets a margin of several metres of light depth: the tree's own blob and
  // its close neighbours fall inside the margin, while a hillside or a
  // distant stand still puts the whole organism in shade. Fine-grained crown
  // shaping stays with the continuous exposure signal and the cloud field.
  float visibility =
    moppe_cloud_transmission (in.world_pos, l, u.params.x, u.params.y);
  if (u.shadow.x > 0.01) {
    const float4 shadow_coord = u.light_matrix * float4 (in.world_pos, 1.0);
    const float3 proj = shadow_coord.xyz / shadow_coord.w;
    if (all (proj >= 0.0) && all (proj <= 1.0)) {
      constexpr sampler shadow_smp (coord::normalized,
                                    address::clamp_to_edge,
                                    filter::linear,
                                    compare_func::less_equal);
      // The local light frustum spans ~1240 m of depth; 9 m in that range.
      const float margin = 9.0 / 1240.0;
      const float lit =
        shadow_map.sample_compare (shadow_smp, proj.xy, proj.z - margin);
      visibility *= mix (1.0, mix (0.22, 1.0, lit), u.shadow.x);
    }
  }

  float3 base = moppe_srgb (in.albedo);
  if (in.leaf < 0.5) {
    // Bark fissures: two vertical planes of stretched value noise blended by
    // facing, so striation follows any trunk without a UV seam.
    const float2 stretch = float2 (3.1, 0.33);
    const float2 px = float2 (in.world_pos.z, in.world_pos.y) * stretch;
    const float2 pz = float2 (in.world_pos.x, in.world_pos.y) * stretch;
    const float sx =
      0.65 * moppe_value_noise (px) + 0.35 * moppe_value_noise (px * 3.13);
    const float sz =
      0.65 * moppe_value_noise (pz) + 0.35 * moppe_value_noise (pz * 3.13);
    const float wx = n.x * n.x;
    const float wz = n.z * n.z;
    const float stria = (wx * sx + wz * sz) / max (wx + wz, 0.001);
    base *= 0.62 + 0.74 * stria;
  }
  // Transmission through the crown. On a thin backlit leaf the transmitted
  // radiance exceeds everything reflected around it, so the term is allowed
  // past the bloom bright-pass rather than staying inside the diffuse range.
  // Sky exposure doubles as optical depth -- Beer-Lambert keeps the glow on
  // the thin lit fringe and leaves the needle mass dark -- and sun
  // visibility gates it, so a backlit crown inside a mountain's shadow
  // stays dark. Rare is what keeps it precious: lit, thin, against the
  // light, or nothing.
  // How many input pixels a needle tuft's blade spans here. Per-blade
  // lighting variance must not outlive the blade's own pixels: a jittered
  // single sample cannot revisit a feature it cannot resolve, so once the
  // fringe geometry is subpixel its individual backlight term collapses to
  // the ensemble mean and the crown keeps a smooth aggregate glow.
  const float focal_px = abs (u.view_proj[1][1]) * 0.5 * u.temporal.y;
  const float resolvable =
    smoothstep (1.5, 4.0, 0.3 * focal_px / max (distance, 0.5));
  float trans = 0.0;
  float toward = 0.0;
  if (in.leaf > 0.5) {
    const float back =
      mix (0.30, pow (max (dot (-n, l), 0.0), 1.45), resolvable);
    const float thin = exp (-2.5 * (1.0 - in.exposure));
    trans = back * thin * visibility;
    toward = saturate (dot (-to_eye / max (distance, 0.001), l));
  }

  // A broad wrap term keeps crown volumes legible while retaining a directional
  // sunward side. It is deliberately separate from the sky exposure signal.
  // Reflectance plus transmittance cannot exceed one, so the diffuse sun
  // term gives up what the leaf transmits.
  const float wrap = saturate ((dot (n, l) + 0.26) / 1.26);
  float3 color =
    base *
    (moppe_hemisphere_light (u.ambient.rgb, n) * (0.62 + 0.38 * in.exposure) +
     u.sun_diffuse.rgb * wrap * visibility * (1.0 - 0.45 * trans));
  color +=
    base * float3 (0.10, 0.16, 0.20) * (0.35 + 0.65 * in.exposure) * in.leaf;

  if (in.leaf > 0.5) {
    // Chlorophyll transmits green-yellow and almost no blue: the electric
    // backlit colour is a transmittance spectrum, not a stylistic warm-up.
    // Transmittance far exceeds reflectance on a thin leaf -- light crosses
    // half the pigment depth that a reflected ray does -- so the tint rides
    // on sqrt(base), not on the dark diffuse albedo. The forward lobe
    // shares its geometry with the sun shafts, so both fire together
    // looking sunward through partial occlusion.
    const float3 chlorophyll = float3 (0.92, 1.0, 0.24);
    const float lobe = 0.20 + 0.80 * toward * toward * toward;
    color += sqrt (base) * u.sun_diffuse.rgb * chlorophyll * trans * lobe * 4.0;
    const float3 h = normalize (l + normalize (to_eye));
    color += u.sun_specular.rgb * pow (max (dot (n, h), 0.0), 18.0) *
             visibility * 0.055;
  }

  const float3 fog_color =
    moppe_warmed_fog (u.fog_color.rgb, -to_eye / max (distance, 0.001), l);
  color = mix (color, fog_color, smoothstep (0.0, 0.92, fog));
  // The glow is view- and sun-locked, so it slides across the geometry as
  // the camera moves; extra history rejection keeps the reconstruction from
  // smearing the bright fringe. Only while the fringe resolves, though:
  // subpixel tufts are held steady by accumulated history, and rejecting
  // it there turns the distant fringe into shimmer.
  return moppe_temporal_output (
    float4 (color, 1.0),
    in.motion,
    in.leaf > 0.5 ? 0.48 + 0.30 * trans * toward * resolvable : 0.12);
}

SOURCE FILE
Path: docs/forest-lod.md
Resource: /3CV92Z/source-files/ZG9jcy9mb3Jlc3QtbG9kLm1k#content
Size: 7424 bytes, 128 lines

# Forest LOD: the continuous-assembly design

Status: implementation record of the conifer LOD system in
`moppe/shaders/metal/forest.metal` as of August 2026, and of the principles
and instruments that shaped it. The forward-looking plan is in
[forest density and aggregates](forest-density-and-aggregates.md).

## The bet

No tree mesh exists in CPU or GPU memory. Every frame, the object stage
chooses each organism's detail from its projected size and the mesh stage
grows boughs, tufts, and blades from a seed (Kuth et al. 2025, "Real-Time
GPU Tree Generation", HPG; Sheaf `#EDURTK`, notes `#6S29CJ`). The memory
saving is the headline but not the point: because nothing is retained,
detail can be exactly right for the current frame and vary continuously --
the whole temporal-stability design below is only expressible because no
baked LOD meshes exist. The costs are structural: generation is paid every
frame even when the camera is still, stability of every generation input
becomes a discipline instead of a given, and ray tracing would need per-frame
acceleration-structure rebuilds.

## Principles, each learned the hard way

1. **Transitions finish while the tree is small in the frame.** The bough
   ramp saturates by ninety projected scene pixels -- about a tenth of the
   frame's height at the game lens. Perception sets this point, not
   geometry: the rider's eye is exquisitely sensitive to elements appearing
   on a tree large enough to watch. When the ramp saturated at three
   hundred scene pixels, crowns were still assembling while filling a third
   of the screen, and the forest read as morphing. Beware the unit trap
   that hid this: LOD thresholds are computed in *scene* pixels, which at
   the default half render scale understate perceived size by 2x.

2. **Completion distance must be size-invariant.** A pixel ramp alone makes
   completion distance proportional to tree height, so saplings finish
   growing only when the walker stands beside them. A short tree's measure
   is boosted (`forest_bough_measure`, factor `clamp(22.5/h, 1, 4.5)`) so
   every tree completes near the same distance.

3. **Conservation of foliage.** A crown must never thin with distance;
   distance changes representation, never mass. Removed boughs move their
   area into survivors (`sqrt(63/count)` widening, at full strength at
   every count -- it converges to one at the full complement, so it needs
   no fade for near stability). The bundled tier's three-tuft boughs widen
   by `sqrt(13/3)` to stand in for thirteen-tuft ones. The floor of
   twenty-one boughs keeps the sparsest assembly reading as a small solid
   tree rather than a pole with stubs, and the floor complement stands at
   full growth (a permanently half-grown bough leaks mass).

4. **Fill evenly; the silhouette is sacred.** The first nine ranks sketch
   the whole silhouette, one bough per whorl bottom-to-top. Later ranks add
   one spoke per whorl per round, top whorl first, with consecutive
   arrivals in a whorl landing on opposite azimuths. Bottom-up fill read as
   the tree growing taller against the sky; consecutive spokes read as a
   lopsided branch; region-by-region completion reads as construction.

5. **Growth is imperceptible per frame.** Boughs grow in from nothing over
   a count window scaled by their size (large low boughs over many ranks,
   small high ones over few), and a per-individual threshold
   (`forest_lod_threshold`) staggers every boundary so a stand never
   crosses one on the same frame.

## Mechanisms

- Projected size comes from the centre's clamped view depth, not endpoint
  projection (endpoints collapse discontinuously when root or tip crosses
  the camera plane). The frustum bound must contain the whole organism
  (`0.55h + 1.4 crown`), or passing trees vanish while filling the screen.
- Tiers: below ~14 threshold pixels one part draws a solid crown proxy;
  the bundled band packs four three-tuft boughs per meshlet, with each
  coarse tuft a single tented quad (two triangles spanning the blade
  fan's splay -- a flat quad in the bough plane disappears edge-on); the
  hero band pays one thirteen-tuft fan bough per meshlet. Both
  representation switches align with ramp saturation.
- `forest_bough_slot` is total over any rank: bundling rounds the
  scheduled range past the sixty-three real slots, and an out-of-range
  read there once rasterized as screen-sized garbage triangles.
- The shadow pass draws only crown proxies plus trunk prisms; reception in
  the fragment shader uses a depth margin of several metres so the tree's
  own coarse proxy does not black out its crown.

## Constraints discovered

- **Metal's 16 KB mesh-output ceiling faults silently.** Two hero boughs
  per meshlet (234 vertices at 80-byte varyings) corrupted the pass with
  no error. Packing varyings to half precision *also* produced garbage
  triangles in motion -- plausibly an interpolant-layout disagreement on
  three-component half vectors -- and still frames never showed it. Hero
  coalescing and varyings packing return only under the Metal debugger,
  not by struct arithmetic.
- Xcode's GPU capture layer crashes the MetalFX temporal scaler under
  Metal 4; the generated scheme disables capture injection, and real GPU
  captures should run with `--upscaling linear` (the exact non-MetalFX
  fallback). `MOPPE_METAL_CAPTURE(_START,_FRAMES)` captures a trace from a
  chosen gameplay frame. Shader sources and line tables are embedded in
  the metallib for source-level attribution.

## Instruments, and what each can and cannot verify

- `moppe-tree-studio`: render(plane + tree). Solo specimen
  (`MOPPE_STUDIO_SOLO`), interactive dolly/orbit (W/S/A/D, P screenshots),
  deterministic dolly ladder (`MOPPE_STUDIO_DOLLY`).
- `tools/tree-lod-atlas`: the dolly cropped to constant apparent size, so
  the only thing changing between tiles is the LOD decision itself.
- In-game: `F` walks, `P` captures to `screenshots/run-<timestamp>/`.
- `MOPPE_RIDE_CAPTURE_DIR` records consecutive gameplay frames;
  `tools/ride-judge` encodes them to video and, with `GEMINI_API_KEY`,
  asks a video-capable model for a 1-5 temporal-stability rating.
- **Epistemics: still frames cannot verify temporal behaviour.** Settled
  captures kept "confirming" fixes the rider immediately refuted; the
  half-precision garbage was invisible in every still. Arithmetic in
  perceived units (fractions of frame height) plus video or a human ride
  are the only valid judges of motion. The atlas catches structural
  regressions in one glance; an FPS counter happily measures corruption.

## Performance state (August 2026, M2 Pro, windowed 2560x1600)

GPU frame ~16.5 ms at the 16.7 ms budget: scene pass ~10.3 ms (terrain +
forest + actors -- the only content-scaled cost), MetalFX temporal upscale
~4.4 ms (fixed), everything else ~1.3 ms. Toggleable effects are all under
0.7 ms. The rider sees 50-59 FPS in tree-heavy views: frames are not
skipped, presents slip to later vsync slots. Ranked levers, none yet done:
register pressure/occupancy in the forest mesh stage, half-precision
*arithmetic* inside fragment shaders (interior math only -- not
interfaces), the upscaler's fixed cost, hero-bough coalescing once the
varyings question is settled under the debugger, per-bough back-side
culling, and a Kuth-style rate-limited auto-LOD governor to hold 60 by
adaptation rather than heroics.

### 34. Assistant tool calls

#### Tool call: `write_note`

```json
{
  "block_ids": [
    "CYDXJ2",
    "USARUR",
    "EDURTK",
    "2WBRVG",
    "KDYKVC",
    "WZDZZW",
    "8TDNZZ",
    "7R6ZNJ"
  ],
  "text": "Analysis of moppe's forest shader (moppe/shaders/metal/forest.metal, with docs/forest-lod.md and docs/forest-density-and-aggregates.md): it implements the LOD-as-statistics credo (#CYDXJ2, #USARUR) end to end.\n\nMapping to the framework:\n- Continuous parameter space: fractional bough count as float ramp of projected pixels (21..63), boughs grow in from nothing over size-scaled windows; Kuth-style per-frame generation, no stored meshes (#EDURTK, #2WBRVG).\n- Dithered LOD front: per-individual hashed threshold (0.88..1.12) staggers construction boundaries across a stand — Jahrmann's hashed culling (#KDYKVC) promoted to transition timing.\n- Conservation of coverage: \"conservation of foliage\" — survivor widening sqrt(63/count) area ratio, bundle tufts widen sqrt(13/3); geometric analogue of HZD coverage-preserving mips (#WZDZZW), exact by construction, converges to 1 near.\n- Solid-angle budget: bough ramp saturates at 90 projected px (~1/10 frame height); size-invariant completion distance via forest_bough_measure boost clamp(22.5/h,1,4.5) — echoes quadratic noise fade reasoning (#8TDNZZ).\n- Geometry→material renormalization in shading: `resolvable` factor collapses per-blade backlight lobe to its ensemble mean (0.30) as tufts fall subpixel, and modulates temporal-history rejection. This is the microfacet-statistics move executed in the fragment shader.\n- Perceptual arrival order: first 9 ranks sketch silhouette (one bough/whorl bottom-up), then even densification top-first with opposed azimuths; silhouette sacred, interior negotiable.\n- Physically based leaf shading: Beer–Lambert via exposure-as-optical-depth, chlorophyll transmittance spectrum, tint on sqrt(base) (half pigment path), diffuse yields energy to transmission.\n- Missing/next: Bruneton–Neyret-style far-field canopy material (#7R6ZNJ) — density-and-aggregates doc argues closed canopy at distance is statistically uniform and should render as one surface; ranked lever list includes a Kuth-style rate-limited auto-LOD governor.\n- Methodological point for the thesis: docs record that still frames cannot verify temporal behaviour — dolly atlas at constant apparent size, video ride captures, and perceived-unit arithmetic are the valid judges of continuous-LOD quality.",
  "title": "moppe forest.metal as an implementation of LOD-as-statistics"
}
```

### 35. Tool result: write_note

NOTE SAVED #GWVMN5
IRI: https://m.sheaf.less.rest/GWVMN5

### 36. Assistant

Saved as #GWVMN5, linked to the two earlier synthesis notes. The short version of my read: the shader already practices the whole statistical credo — continuous fractional detail, dithered transition fronts, an explicit conservation law ("a crown must never thin with distance; distance changes representation, never mass"), and even the geometry-to-material renormalization, done live in the fragment shader via the `resolvable` collapse of per-blade backlight to its ensemble mean. What remains open, and what the density-and-aggregates doc already names, is the terminal object: a true far-field canopy *material* in the Bruneton–Neyret sense, which the current per-tree proxies approximate but don't yet become.

### 37. User

Can you please just talk to me? I don't even need a bunch of super dense, concise research notes. I just want to discuss and learn and think about these things.

### 38. Assistant

Yeah, fair — I got into archivist mode. Let's just talk.

Honestly, the thing I keep coming back to after reading your shader is that one comment: *"distance changes representation, never mass."* That's such a good sentence. It reads like a physical law, and in a sense it is one — it's a conservation law you imposed on the renderer. And what strikes me is that once you write that sentence down, half the design decisions stop being decisions. The survivor widening isn't a trick anymore, it's just... what the law requires. The square root falls out of asking "if this bough's area has to go somewhere, where does it go?" I find that genuinely beautiful — the way a good invariant collapses a whole space of hacks into one obvious move.

And there's something almost funny about how the industry got here. Twenty years of LOD literature is basically people fighting popping with increasingly elaborate blends, and the actual answer turns out to be: popping isn't a transition problem, it's a *bookkeeping* problem. Things pop because some quantity — coverage, brightness, mass — silently changed. If nothing measurable changes, there's nothing to see. The blend was always treating the symptom.

The other thing I'd love to just chew on with you: the `resolvable` term. Because I think you did something there that's conceptually a bit ahead of where the papers are. Everyone knows you should fade out *geometric* detail as it goes subpixel. But you faded out a *lighting term* — you let the per-blade backlight lobe exist only while a blade owns enough pixels to express it, and below that it collapses to the average. That's treating the BRDF itself as having a level of detail. Which, if you follow it, suggests something kind of wild: every shading term in a renderer has a natural distance band where it's meaningful, and outside that band it should degenerate into its own mean. Specular sparkle, leaf transmission, ambient occlusion — each one has a scale where it stops being a *feature* and becomes *texture*, then stops being texture and becomes a *constant*. Most renderers only handle that transition for normal maps, badly, and everything else just aliases.

What I'm curious about, since you've actually lived with this system: does the per-frame regeneration feel *freeing* in practice, or does it feel precarious? The docs are candid that stability becomes "a discipline instead of a given" — every input has to be smooth or the whole tree wobbles. That seems like the real philosophical trade. Baked meshes are dumb but they're *inert*; nothing about them can betray you frame to frame. Your trees are alive in a way that means they can misbehave. The bugs in your docs are great evidence of this — NaN through a bough frame rasterizing as screen-sized triangles is the kind of failure a static mesh literally cannot have.

And then there's the question I suspect matters most for the thesis framing: the density doc says density is an *unlock*, not a cost — that a closed canopy is what makes the far field statistically honest. I think there's something deep there. The statistical representation isn't just cheaper, it's only *valid* when the underlying population is dense enough to have stable statistics. A sparse stand of five trees doesn't have a meaningful "expected radiance" — it has five individuals, and any average lies about all of them. So there's a threshold — an ergodicity condition, almost — below which you must render individuals and above which you may render the ensemble. Real forests cross that threshold spatially, at stand edges. Which is maybe why forest edges are where every game forest looks worst?

What's pulling at you most right now — the far-field canopy material, the governor, or something else entirely?

### 39. User

Yeah, somehow, this whole problem in this whole area is kind of a beautiful thing about nature and reality, and also phenomenology, because the world seems to have this quality: it really is made up of individual things, individual organisms, features, limbs, leaves, trees, pebbles, sand, and so on. It also has this canopy quality where things just blend into an environment. It becomes an environment. Isn't that kind of what a world is? That seems like a quality of a world in a phenomenological sense, maybe.

### 40. Assistant

That's a really lovely thought, and I don't think it's a stretch at all — I think you've hit on why this technical problem feels *deep* rather than merely hard.

What you're describing is basically the figure/ground structure that Merleau-Ponty puts at the absolute bottom of perception. His claim is that perception never delivers either of the two "pure" cases: you never see a bare individual, complete in itself, and you never see an undifferentiated continuum. You always see a figure *against* a ground. And — this is the part that matters for your intuition — the ground isn't a deficient mode, isn't just "stuff I haven't looked at yet." The ground is doing positive work. It's what makes the figure a figure. The meadow-as-environment isn't a failed perception of ten thousand blades; it's a successful perception of a meadow.

Husserl has a word for the canopy quality: *horizon*. Every thing is given with an outer horizon of more world — indeterminate, but not nothing. Determinable, promissory. The grass at the edge of vision is given as "more grass, which I could go look at," and that *could* is part of the experience even though you never cash it in. And the world itself, for Husserl, isn't a very large object — it's the horizon of all horizons, the thing that can never become a figure because it's the ground of every figure. So when you say "it becomes an environment... isn't that kind of what a world is?" — yes, almost technically yes. Worldhood *is* the capacity to recede. A collection of objects that refused to recede, each insisting on its individuality at every distance, wouldn't be a world. It would be an inventory.

And I think that's precisely why bad game vegetation feels fake in the specific way it does. Repeated identical trees don't fail because they're low-detail — they fail because each one keeps asserting "I am an object" at distances where a real tree would have surrendered into forest. The lie isn't in the polygons; it's in the refusal to become ground. Your density doc's complaint that the current stands read as "a bunch of spiny trees, not a forest" is exactly this, stated as an engineering defect.

Here's the further step, though, and it's where your own thesis library gets interesting. The phenomenological reading alone would say: individuals are real, and the "environment" is just how they appear from far away — a fact about *us*, our finite acuity. But I don't think that's right, and neither does the ontology shelf you've collected. Barry Smith's granular partitions stuff, and especially the mountains paper (#M8BQ3S), argues that reality genuinely *supports* multiple grains — that the everyday world where mountains are things and the geoscience world where there's only a continuous elevation field are both veridical projections onto the same reality. The coarse view isn't a blur of the fine view. Think of temperature: no molecule has a temperature, yet temperature is not an illusion — it's a perfectly real property that only *exists* at the ensemble scale. Forest-radiance is like that. "Dark conifer green with this roughness and this backscatter" is a true fact about a hillside, a fact that no individual spruce possesses. So when your renderer swaps individuals for statistics, it isn't switching from truth to approximation. It's switching between two grains of truth — which is, I suspect, why doing it *honestly* (conserving mass, converging expectations) works so well perceptually. You're not fooling the eye. You're agreeing with it.

And there's a Gibsonian note in your corpus too (#2F8T3H) that fits: for ecological psychology, what animals perceive is mostly not objects at all — it's *media, surfaces, affordances*. The environment as environment: walkable ground, passable gaps, cover. A deer doesn't perceive ten thousand grass blades and neither do you; you perceive *graze*, *softness underfoot*, *concealment*. Individuality is almost a special mode of perception, reserved for the near, the relevant, the handled. Heidegger would go further and say things show themselves as individuals mostly when something makes them conspicuous — the tool that breaks, the tree you're about to hit. The rest of the time the world serves as world precisely by staying inconspicuous.

Which gives you, I think, a genuinely nice way to see what a renderer with continuous LOD actually *is*: it's an engineering phenomenology. It doesn't model the world in itself — it models the world *as given to a located, finite perceiver*, and the budget forces it to be honest about something philosophy already knew: at any moment, almost none of the world is given as individuals. Individuality lives in a small bubble that travels with the perceiver — with the rider — and everything outside the bubble is given as ensemble, as horizon, as ground. The LOD ladder is the structure of givenness, implemented. Detail *is* nearness, in the phenomenological sense of nearness — concern, not meters.

The one place I'd push on your framing, gently: it's tempting to say the world "really is" made of individuals and the blending is appearance. But sand is a nice counterexample to sit with. Is a beach ten billion individuals? Grains only become *individuals* when something individuates them — when you pick one up, when one gets in your shoe. Before that, "sand" might be the more fundamental description, and the grain the derived one. Maybe individuality and environmentality aren't a fact plus an appearance, but two poles reality can occupy depending on grain — and a *world* is whatever is rich enough to sustain both poles and the passage between them. In which case your shader's real subject isn't trees. It's the passage.

Does the sand case feel right to you, or does something in you want to insist the grains are prior?

### 41. User

I'm pretty sure that, in human experience and animal experience, the world that I experience, the environment that I experience, is actually procedurally generated, in a sense. Isn't that more or less true? I don't see the details of reality even when I look closely at a close-up.

I'm sitting at a desk now that I built out of birch plywood. On that desk is a mouse pad that I made out of granulated cork board and some acrylic lacquer, and I chamfered the edges with sandpaper a bit, etc. The plywood, I didn't really put any kind of finish on, so it has quite a lot of dirt and random discolorations. I'm pretty sure that my experience even of this cork pattern, as I'm looking at it directly right now from 10 cm, is kind of invented by my brain.

I think that's one thing that psychedelic, like LSD, makes relatively blatant: the mind and our perceptual system are incredibly, incredibly extraordinarily good at fabricating detailed textures and patterns that are never quite the same. You never look at the same river twice. You never even look at the same piece of wood twice.

### 42. Assistant

I think you're right, and moreover I think you're right in a way that has become respectable cognitive science over the last twenty years — the predictive processing people would sign off on your desk description almost verbatim. But let me try to say precisely *in what sense* it's true, because the interesting part is in the fine print.

The strong version of the claim — perception is a controlled hallucination, a generative model constrained by sensory evidence — is basically the Friston/Clark/Hohwy picture. The brain doesn't build experience *up* from the retina like a camera pipeline. It runs a generative model *downward* — predicting what the senses should be reporting — and the upward traffic is mostly *error*, the residual between prediction and input. What you experience is the model, not the data. The data only gets to veto and correct. On that picture your cork pad experience is literally synthesized: cortex proposes "granulated cork, this statistical character, this lighting," the residual errors sculpt it where the proposal is wrong, and the settled state is what it's like to see your mouse pad.

And the vision-science details back you up more specifically than you might know. Your fovea — the only part of the retina with real resolution — covers about the area of your thumbnail at arm's length. *Everything* else is astonishingly coarse. Yet visual experience seems uniformly detailed wall to wall. That surplus detail is manufactured. Even better: there's beautiful work (Rosenholtz, Freeman & Simoncelli) showing that peripheral vision doesn't represent *things* at all — it represents *summary statistics* of texture. You can build two physically different images that share local statistics, and in the periphery they are literally indistinguishable — "metamers." Which means the brain does, precisely, what your renderer does: individuals in a small attended bubble, statistics everywhere else. The visual system has an LOD scheme, and its far field is a texture-statistics representation. The canopy quality of the world isn't just *out there* in the ontology — it's the native format of most of your visual field.

Even at 10 cm, at full attention, I think your intuition holds. When you look at the cork, you don't — can't — perceive each granule as an individual simultaneously. You get the *texture*: the statistical signature, plus maybe two or three granules that attention has picked out and individuated *right now*. Look again and attention picks out different ones. The overall experience of "richly detailed cork surface" is a statement about a distribution, freshly sampled. Which is exactly why you never look at the same piece of wood twice: **the detail isn't stored, it's regenerated on each look.** Your brain doesn't keep the cork texture in memory as pixels; it keeps something like the *parameters* — enough to re-synthesize a cork-ish experience on demand, seeded a little differently each time by noise, attention, adaptation.

And notice — this is the part that delights me — that's *your forest shader*. No mesh is retained; the organism is regenerated every frame from a seed and a few constraints. The moppe bet ("because nothing is retained, detail can be exactly right for the current frame") is apparently also the brain's bet. Biology reached the same architecture, presumably for the same reason: storing the full-resolution world is impossible and pointless when you can cheaply re-derive it from compact causes plus live evidence.

There's a companion idea from O'Regan and Noë that completes the picture: the world serves as its own memory. The brain can afford to render almost nothing in detail because the detail stays out there, re-queryable at will — just saccade back. Perceptual "presence" of detail isn't detail *in the head*; it's the mastery of *access*. The world feels inexhaustibly detailed because every query gets answered — and that's exactly the phenomenology of a lazy procedural generator, isn't it? A Minecraft world genuinely has no terrain beyond the loaded chunks, yet from inside it is seamlessly endless, because generation always outruns your approach. If the generator never fails a query, the phenomenal report is "infinite pre-existing detail." Inexhaustibility is what on-demand generation feels like from the inside. Change blindness is the tell — swap details during a saccade and nobody notices, because those details were never being carried; the system trusted the world to hold them.

On LSD: your reading matches the current best theory pretty exactly. The REBUS model (Carhart-Harris and Friston) says psychedelics relax the precision of high-level priors — the model's confidence in its own predictions drops, the error signals run hotter, and the generative machinery, normally invisible because it's *converged*, becomes visible as process. Textures breathe because the texture-synthesis loop is no longer settling to a fixed point; surfaces crawl with pattern because the pattern-completion priors are being applied with too little constraint; faces bloom out of foliage because the "there are agents and faces" prior gets to overreach. None of this adds a new capacity — it *exposes* a standing one. The machinery that makes cork on acid is the machinery that makes cork sober; sober, it's just tightly slaved to the evidence. Dreams are the other exposure of the same fact: full worlds, textured, lit, spatial, with the sensory constraint at zero. The renderer runs fine with no input at all. That alone settles that the detail is generated, not received.

Now the fine print, because I don't want to hand you an idealism you didn't order. "Invented" is too strong if it means *unconstrained*. The generation is disciplined by the world at every moment — that's the whole point of the error traffic. Your cork pad's statistics are *real*, out there, caused by actual granules you actually glued; your brain's cork-texture experience is a sample from a model whose parameters were fitted to, and are continuously corrected by, that real surface. So the right slogan isn't "the world is a hallucination" but something more like: **experience is a procedurally generated rendering, parameterized by reality.** The seed and the noise are yours; the constraints are the world's. And what perception actually *transmits* faithfully isn't the micro-detail at all — it's the stable, action-relevant structure: the desk's height, its solidity, the pad's slickness under the mouse, the chamfer your thumb finds. Gibson's affordances survive intact. The texture is upholstery; regenerated each visit, never twice the same, and — here's the thing — *it doesn't matter that it's never the same*, because what needed to persist was the distribution, not the sample.

Which lands us somewhere I find genuinely satisfying, given the whole arc of this conversation: your renderer and your visual system agree on what's worth conserving. Not the instance — the statistics. Moppe never shows anyone the same forest twice, frame to frame, bough for bough; the brain never shows you the same desk twice. Both get away with it because the thing being kept faithful is the ensemble, and the ensemble is where the reality is. "You never step in the same river twice" turns out to be an engineering specification.

The question it leaves me with — and I'm honestly unsure — is about the *near field*. We've said individuality lives in a small bubble of attention and concern. But is even the bubble generated? When you hold one cork granule under your fovea and attend to it fully, is *that* finally raw contact, or just the generator at maximum constraint? I lean toward the latter — it's inference all the way in, only with the error bars squeezed toward zero. But it does make you wonder what, phenomenologically, "seeing the thing itself" was ever supposed to mean. What do you think — does full attention feel to you like meeting the granule, or like the model finally fitting snugly?

### 43. User

Now this makes me think of one thing, one idea that is one of my favorite ideas ever. It's so interesting and kind of underappreciated, I think: yes, experience is parameterized by reality and grounded in reality, and with saccades and stuff, it tracks reality.

Also, when you take LSD and you go and stare at a white wall that has a little bit of noise texture to it, or even a completely blank wall, for that matter, when you do that, you experience a visual field full of colors and beautiful patterns with symmetries and movement and stuff. It's so interesting that that visual field seems to kind of have laws, or it has its own kind of physics, or it's kind of like phenomenological material. There's some kind of conservation of momentum or something in those so-called hallucinations.

Now, it's not conservation of energy in the strict physical sense. The hallucination gets stronger and stronger when you look at it, usually. One reason it feels magical is, I think, precisely that you get the feeling that it's not limited by conservation of energy, but still, there are some kind of conservation laws happening. Those are also kind of like, I think, textures and canopies and the rustling of leaves and wind and stuff like that. I think, in part, it's because it's like what's the word, diffusion or energy sinks or something, phenomenological energy sinks, something like that.

### 44. Assistant

(no text)
