Full-screen post effects & scene depth
Render the scene to an offscreen target, then run a full-screen post-process shader that samples the rendered scene colour and scene depth (plus the frame clock and your params) and writes the final image. This is how you do colour grading, depth-based fog / underwater, outlines, and any screen-space effect (engine ADR 0074 / PLM-240 + PLM-237).
It is opt-in: when at least one post effect is added, the engine renders the scene offscreen and runs your post pass into the swapchain; with no effects added, the normal path is unchanged.
Companion guides: Frame clock & wind · Custom-material pack shaders.
// apps/<app>/shaders/src/mygrade.slang#include "plume3d_post.slang" // NOT plume3d.slang — this is the POST ABI[shader("fragment")]float4 fragment(PostVSOutput input) : SV_Target { float4 c = plume3d_sceneColor(input.UV); float d = plume3d_sceneDepth(input.UV); // 0 (near) .. 1 (far) float4 tint = plume3d_postParam(0); // params[0] from Graphics.addPostEffect return float4(c.rgb * tint.rgb, 1.0);}// main.wren — draw the scene as usual, then add the post effect each frame in draw()Graphics.loadShader("shaders/mygrade") // once, in init()// ... Graphics.drawMesh(...) your scene ...Graphics.addPostEffect("shaders/mygrade", [1.0, 0.9, 0.8, 1.0]) // params (up to 16 floats)The post ABI (plume3d_post.slang)
Section titled “The post ABI (plume3d_post.slang)”A post shader #includes plume3d_post.slang and provides only [shader("fragment")] — the
full-screen vertex is in the include, and PostVSOutput carries a UV.
| accessor | meaning |
|---|---|
float4 plume3d_sceneColor(uv) | the rendered scene colour |
float plume3d_sceneDepth(uv) | scene depth [0,1] (near → far); read by texel .Load(), so it is 1:1 exact |
float plume3d_time() | the frame clock (for animated grades — shared with the frame block) |
float4 plume3d_postParam(i) | your params: i = 0 is params[0..3], i = 1 is params[4..7], … |
Do not #include "plume3d.slang" in a post shader — that is the mesh ABI (a different set-0
layout). Full binding contract: Graphics → Post-process effects.
1. Write the post shader
Section titled “1. Write the post shader”apps/<app>/shaders/src/<name>.slang, #include "plume3d_post.slang", provide [shader("fragment")].
Sample plume3d_sceneColor / plume3d_sceneDepth, and use plume3d_postParam(i) for tunables.
2. Load it
Section titled “2. Load it”Graphics.loadShader("shaders/<name>") // once, in init()3. Add it each frame
Section titled “3. Add it each frame”// in draw() — the post list is per-frame, like draw callsGraphics.addPostEffect("shaders/<name>", [ /* up to 16 floats */ ])Graphics.clearPostEffects() stops the effect (drops back to the direct path).
4. Draw your scene normally
Section titled “4. Draw your scene normally”Your meshes render offscreen; the post pass composites them to the screen.
Verify it works
Section titled “Verify it works”plume3d apps/post_depth_demo # a post effect visualizes the scene depth, then screenshotsThe reference (post-depth example): quads drawn at different depths read back
as distinct grays (near dark, far light) and the cleared background is white (depth 1.0) —
proving the scene rendered offscreen and the post pass read its depth.
Rules & limits (v1)
Section titled “Rules & limits (v1)”- One post effect is applied in v1 (a ping-pong chain of multiple effects is a follow-up).
- The offscreen colour is LDR (
B8G8R8A8_SRGB), so existing mesh shaders render into it unchanged. True HDR (R16F) + a tonemap / bloom pass is a follow-up. - Post effects run for the single-camera path only, and are skipped when fog-of-war, split-screen, or the RTT composite path is active (they own the frame).
- Read depth with
plume3d_sceneDepth(texel.Load), not a.Sample— sampling the depth image through aSamplerStatemis-compiles on MoltenVK. The accessor already does the right thing. - A post shader loaded via
Graphics.loadShaderalso attempts a (harmless, logged) mesh-pipeline build — ignore theCould not create graphics pipelineline for a post shader; the post pass works.
Related
Section titled “Related”- API: Graphics → Post-process effects (A#5 / A#2).
- Example: Post Depth (
apps/post_depth_demo). - Companion guides: Frame clock & wind · Custom-material packs.