Real-Time Rendering / Visibility Culling / GPU Vegetation

Vulkan Rendering & Optimization

A Vulkan real-time renderer combining glTF PBR scene rendering with compute-driven vegetation, CPU and GPU visibility culling, static batching, shadow caster filtering, mipmapped assets and GPU timestamp profiling.

VegetationCompute + indirect
Scene geometryglTF + PBR
VisibilityCPU + GPU
MeasurementGPU timestamps
Hunter's Dream scene rendered by the Vulkan renderer
Final scene / glTF PBR + HDR IBL
GPU simulated grass and flowers moving in the Hunter's Dream scene
GPU vegetation / compute + indirect
Grass workload524,288
Culling gain+78.1%
LOD gain+43.6%
Visibility paths02
01 / Frame architecture

Frame Architecture

The project is presented as a frame, not as a scene. Compute work updates and compacts vegetation. A shadow pass and main pass consume different visible sets. Timestamp queries then report where the GPU budget went.

Stage 01 / Compute

Simulation & Compaction

Every blade is updated in a compute shader, tested for visibility and compacted into the output buffer. The surviving count becomes the indirect draw command.

Click a stage to inspect the frame
compute → shadow → main → resolve → present

02 / GPU vegetation

Half a million blades. One compacted draw.

The grass system is the largest controlled workload in the renderer. A quadratic Bezier blade stores shape and physical state; compute applies gravity, recovery and wind before three culling tests remove work that cannot contribute to the frame.

GPU simulated grass reacting to wind
Live compute simulation
GPU data path

GPU-Driven Data Flow

No visible blade list returns to the CPU. The compute shader writes surviving blades into a compact buffer and atomically updates the instance count consumed by the draw.

Input blade SSBO524,288
Physics + cullingCompute
Visible blade SSBOCompacted
Draw commandIndirect
uint dst = atomicAdd(draw.instanceCount, 1);
visibleBlades[dst] = blade;

vkCmdDrawIndirect(cmd,
    drawBuffer, 0, 1, stride);
Culling benchmark524K blades / 640×480
No culling
64
Orientation
94
Frustum
71
Distance
83
Combined
114
+78.1%All culling vs. no culling
Tessellation LODDistance-controlled detail
Distance-based tessellation LOD visualized by color
5 → 3 → 2 levels
Fixed detail
399
With LOD
573
+43.6%Same image intent, less generated geometry
03 / Imported scene workload

Scene Visibility & Submission

The imported scene uses a different optimization path from the grass. Bounding volumes are tested on the CPU, compatible static primitives are merged, shadow casters are filtered independently and complete mip chains stabilize distant textures.

Runtime frustum culling validationVisibility / CPU

Scene frustum culling

At the locked capture camera, world-space AABB tests reduce submitted scene batches from 290/290 to 247/290, rejecting 43 batches while preserving the rendered image.

Shadow caster culling validationShadow / light space

Independent shadow filtering

The shadow pass uses a separate conservative receiver test, sweeping each caster up to 40 world units along the light direction. At this locked view it intentionally retains all 290 potential casters to prevent off-screen shadow popping; this image validates behavior, not a claimed frame-time gain.

Static batching runtime comparisonSubmission / CPU

Material + spatial batching

With batching disabled the scene produces 495 draw batches; material- and cell-compatible batching reduces that to 290, eliminating 205 batches (41.4%) without turning the level into one un-cullable mesh.

Mipmapped distant texture validationSampling / texture

Generated mip chains

This image shows the current mipmapped output. A still frame is not an ON/OFF proof: GPU-generated mip chains and trilinear filtering primarily reduce temporal shimmer in distant stone, foliage and texture edges while the camera moves.

04 / GPU profiler

GPU Timestamp Profiling

CPU frame time cannot explain which graphics pass is expensive. Timestamp query pools bracket the graphics frame, shadow pass and main pass so the title HUD reports actual GPU duration after query availability is confirmed.

6.50 ms GPU

Example captured frame from the runtime HUD. The visual below turns the same timestamp result into a readable frame breakdown.

Graphics
6.50 ms
Shadow
2.28 ms
Main
4.22 ms
Timestamp period converted to milliseconds / availability checked before readback / no blocking wait in the render loop
05 / Renderer systems

Implemented Rendering Systems

The final scene is only the stress test. The actual work is a set of reusable rendering systems, each solving a different bottleneck in simulation, visibility, submission, shading or measurement.

01 / Assets

glTF + PBR

Static scene import, material channels, transforms and alpha-tested foliage.

02 / Lighting

HDR IBL

Environment cubemap, irradiance and prefiltered specular response.

03 / GPU work

Compute grass

Physics, visibility compaction and indirect submission remain on the GPU.

04 / Geometry

Tessellation LOD

Bezier blades generate fewer segments as projected importance falls.

05 / Visibility

Two cullers

CPU scene AABBs and GPU blade tests target different workload shapes.

06 / Submission

Static batches

Material and spatial compatibility reduce state changes and draw overhead.

07 / Shadows

Caster filtering

A separate light-space test reduces unnecessary shadow map submissions.

08 / Profiling

GPU timestamps

Pass-level timings expose the real cost of shadow and main rendering.

Responsive Vulkan grass system with wind simulation and GPU culling

GPU GRASS SYSTEM

VULKAN · COMPUTE SHADERS · TESSELLATION

A responsive real-time grass renderer built from Bézier blades. Compute shaders simulate wind and recovery forces while orientation, frustum and distance culling plus adaptive LOD keep more than half a million blades interactive.