simvx.graphics.renderer.post_process

Post-processing pass: HDR tone mapping, FXAA, bloom, DoF, motion blur, film grain, vignette, chromatic aberration.

Module Contents

Classes

PostProcessPass

Renders HDR scene to offscreen target, then tone-maps to swapchain.

Functions

near_far_from_proj

Recover (near, far) from a reverse-Z projection matrix.

Data

API

simvx.graphics.renderer.post_process.__all__

[‘PostProcessPass’, ‘near_far_from_proj’]

simvx.graphics.renderer.post_process.log

‘getLogger(…)’

simvx.graphics.renderer.post_process.near_far_from_proj(proj: numpy.ndarray) → tuple[float, float]

Recover (near, far) from a reverse-Z projection matrix.

For SimVX’s perspective (perspective() in core.math): row-major, m[2,2] = n/(f-n), m[2,3] = fn/(f-n), m[3,2] = -1 (the Vulkan Y-flip only touches m[1,1]). For its orthographic, m[2,2] = 1/(f-n) and m[2,3] = f/(f-n) with a constant w row, which is what tells the two apart. Both invert as n = m23 / (m22 + 1) under perspective and f = m23 / m22 under either. Falls back to a sane 0.1/100 if the matrix is degenerate.

A camera’s near and far planes are nowhere else in the render path:

Class:

~simvx.graphics.types.Viewport carries only the two matrices, so anything that needs the planes recovers them from the projection here.

simvx.graphics.renderer.post_process.FLAG_FXAA

None

simvx.graphics.renderer.post_process.FLAG_BLOOM

None

simvx.graphics.renderer.post_process.FLAG_SSAO

None

simvx.graphics.renderer.post_process.FLAG_DOF

None

simvx.graphics.renderer.post_process.FLAG_GRAIN

None

simvx.graphics.renderer.post_process.FLAG_VIGNETTE

None

simvx.graphics.renderer.post_process.FLAG_CHROMATIC

None

simvx.graphics.renderer.post_process.FLAG_MOTION_BLUR

None

simvx.graphics.renderer.post_process.FLAG_VOLUMETRIC_FOG

None

simvx.graphics.renderer.post_process.FLAG_LUT

None

simvx.graphics.renderer.post_process.FLAG_CRT

None

simvx.graphics.renderer.post_process.FLAG_PIXELATE

None

simvx.graphics.renderer.post_process.FLAG_BLUR

None

class simvx.graphics.renderer.post_process.PostProcessPass(engine: Any)

Renders HDR scene to offscreen target, then tone-maps to swapchain.

The pass owns an HDR render target. When enabled:

  • 3D scene renders to the HDR target (pre_render phase)

  • Bloom extraction + blur (optional)

  • Tone mapping + FXAA + cinematic effects renders to swapchain (main render pass)

Initialization

property enabled: bool
property hdr_target: simvx.graphics.renderer.render_target.RenderTarget | None
property gbuffer_view: Any

Thin G-buffer normal/roughness view, or None when inactive.

property bloom_enabled: bool
property motion_blur_enabled: bool
property lut_tex_id: int
setup() → None

Initialize HDR target and tonemap pipeline.

update_ssao_descriptor(ao_view: Any) → None

Update binding 4 with the SSAO output image view.

register_lut(tex_id: int, lut_data: numpy.ndarray, *, pool: Any) → None

Upload a 3D colour-grading LUT and register it under tex_id.

lut_data is an (size, size, size, 4) uint8 array (the format produced by colour_grading.generate_*_lut), uploaded as a 3D rgba8 image: byte-for-byte the web runtime’s LUT representation. Re-registering an id replaces the previous texture. Select the active LUT with lut_tex_id.

pool is the command pool the staging copy is recorded into, passed down to :func:~.colour_grading.upload_lut_3d, whose docstring says which pool each caller owes. Engine.register_lut is the tick-time entry point and hands over the engine’s upload pool with its lock held.

update_motion_blur_matrices(view: numpy.ndarray, proj: numpy.ndarray) → None

Update the motion blur UBO with current inverse VP and previous VP.

Call once per frame before render_tonemap() with the current camera matrices. Uses temporal smoothing to prevent sudden jumps from frame time variance.

next_taa_jitter(width: float, height: float) → tuple[float, float]

Advance the Halton jitter index and return this frame’s sub-pixel offset.

Called once per frame (only when taa_enabled) before the forward draw. The renderer applies the returned offset to the forward-draw projection via :func:simvx.core.math.apply_jitter, while leaving the unjittered camera_proj intact for culling and motion-vector reprojection. The jitter is stored so :meth:taa_jitter can hand it to the resolve pass.

width / height are the render extent; the offset is in pixels.

update_taa_matrices(view: numpy.ndarray, proj: numpy.ndarray) → None

Capture the unjittered current/previous view-projection for TAA resolve.

Call once per frame (only when taa_enabled) with the clean (unjittered) camera matrices, before the forward pass jitters its own projection copy. Stores the current VP, its inverse, and rolls the previous-frame VP so the future resolve pass can reconstruct each pixel’s world position from depth and reproject it through the previous VP (camera + static-geometry motion).

Independent of motion blur so TAA functions whether or not motion blur is enabled. On the first frame prev_vp == cur_vp to avoid a velocity spike.

property taa_jitter: tuple[float, float]

This frame’s applied sub-pixel jitter (pixels); for un-jittering in resolve.

property taa_cur_vp: numpy.ndarray

Unjittered current-frame view-projection (row-major).

property taa_prev_vp: numpy.ndarray

Unjittered previous-frame view-projection (row-major).

property taa_inv_vp: numpy.ndarray

Inverse of the unjittered current VP (depth->world for resolve).

property taa_depth_view: Any

HDR target’s samplable depth view: the resolve pass’s reprojection source.

begin_hdr_pass(cmd: Any) → None

Begin the HDR render pass (call before 3D rendering).

end_hdr_pass(cmd: Any) → None

End the HDR render pass.

rebegin_hdr_pass(cmd: Any) → None

Re-begin the HDR pass with the colour+depth LOAD variant.

Used by the scene-read pass split: after end_hdr_pass and the scene colour/depth copy, this loads the opaque colour + depth (no clear) so the transparent phase blends over the scene and depth-tests against it.

render_bloom(cmd: Any) → None

Execute bloom pass (extract + blur). Call after end_hdr_pass.

render_tonemap(cmd: Any, x: int, y: int, width: int, height: int) → None

Render the tone-mapped quad into (x, y, width, height) of the swapchain.

The whole swapchain in the ordinary case. A letterboxed presentation passes its sub-rect: the fullscreen triangle’s UVs still span the whole HDR target, so the scene arrives scaled into the rect, and the scissor leaves the bars carrying the swapchain’s own clear rather than post-processed clear colour.

resize(width: int, height: int) → None

Recreate HDR target and pipeline for new dimensions.

cleanup() → None

Release all GPU resources.