Skip to main content

forge-grass-rendering

Add instanced grass rendering with segmented blades, wind animation, LOD density rings, and terrain LOD with geomorphing to an SDL GPU project

跳到安装

来源信息

仓库
Nebulavenus/forge-gpu
最近来源活动
2026年3月29日 00:35
检测到的 SKILL.md 语言
英语
星标
38
分支
7

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
forge-grass-rendering
description
Add instanced grass rendering with segmented blades, wind animation, LOD density rings, and terrain LOD with geomorphing to an SDL GPU project
Add grass rendering to an SDL GPU project using `forge_scene.h`. Implements instanced segmented grass blades with wind animation, density-based LOD rings, and tile-based terrain LOD with vertex shader geomorphing. ## When to use - Adding vegetation to a 3D scene - Implementing instanced rendering with per-instance attributes - Adding LOD systems for both terrain and foliage - Split-screen or multi-viewport rendering ## Key API calls - `forge_shapes_plane()` — generate tile meshes at different resolutions - `forge_scene_upload_buffer_deferred()` — per-frame instance buffer upload - `forge_scene_create_shader()` — create custom shaders alongside forge_scene - `SDL_SetGPUViewport()` / `SDL_SetGPUScissor()` — split-screen viewports - `SDL_DrawGPUIndexedPrimitives()` — instanced draw with per-instance buffer - `forge_noise_fbm2d()` — procedural heightmap generation - `forge_hash_wang()` / `forge_hash_to_float()` — deterministic placement variation ## Correct order 1. Generate procedural heightmap via fBm noise 2. Upload heightmap as R32_FLOAT GPU texture 3. Create tile meshes at multiple LOD resolutions (both triangle and line IBs) 4. Create grass blade meshes at multiple segment counts 5. Create pipelines: terrain fill/line/shadow + grass fill/line/shadow 6. Per frame: a. Select terrain tile LODs based on camera distance b. Place grass instances with distance/frustum/slope culling c. Upload instance buffers via deferred upload d. Shadow pass: draw terrain tiles + near grass into shadow map e. Main pass: set left viewport → draw terrain + grass filled; set right viewport → draw terrain + grass wireframe f. Reset viewport for UI ## Grass blade geometry Each blade is a vertical segmented strip with `2*(N+1)` vertices and `2*N` triangles. The `height_t` attribute (0 at base, 1 at tip) controls wind weighting and tapering. ```c for (int i = 0; i <= segments; i++) { float t = (float)i / (float)segments; float half_w = 0.5f * (1.0f - t * GRASS_BLADE_TIP_TAPER); verts[2*i+0] = (GrassVertex){ {-half_w, t, 0}, t }; verts[2*i+1] = (GrassVertex){ { half_w, t, 0}, t }; } ``` ## Per-instance data ```c typedef struct { float position[3]; /* world-space base position */ float rotation; /* Y-axis rotation (radians) */ float scale[2]; /* width, height */ float color[3]; /* RGB tint */ float _pad; /* alignment to 40 bytes */ } GrassInstance; ``` ## Wind animation (vertex shader) ```hlsl float wind_weight = height_t * height_t; /* quadratic: tip bends most */ float phase = time * wind_speed + world.x * 0.7 + world.z * 0.3; float wind_offset = sin(phase) * wind_strength * wind_weight; ``` ## Terrain geomorphing (vertex shader) ```hlsl float2 snapped_uv = round(global_uv / coarse_cell_size) * coarse_cell_size; float2 morphed_uv = lerp(global_uv, snapped_uv, morph_factor); float morphed_x = lerp(world_x, snapped_x, morph_factor); float h = height_tex.SampleLevel(smp, morphed_uv, 0).r; ``` ## Common mistakes - **Forgetting half-aspect projection** — split-screen viewports are half the screen width, so the projection matrix must use `half_w / height` aspect - **Using TRIANGLELIST pipeline for wireframe** — the right viewport needs a LINELIST pipeline with a separate line index buffer (each triangle edge as two indices) - **Grass shadow without wind** — the shadow vertex shader must apply the same wind animation as the main shader, or shadows will not match blade positions - **Missing morph factor** — without geomorphing, terrain tile LOD switches cause visible popping; morph_factor must ramp 0→1 in the last 20% of each LOD distance band - **Forgetting to reset viewport for UI** — after right-viewport draws, reset viewport and scissor to full screen before forge_scene_end_main_pass ## Reference See [Lesson 50 — Grass Rendering](../../../lessons/gpu/50-grass-rendering/) for the full implementation.
在 GitHub 查看