raea/post_process
What's done to the frame after it's drawn: tone mapping, bloom, depth of field, motion blur, grading and outlines.
These are Bevy's post-processing passes, placed beside the camera: <ToneMapping>, <Bloom>, <DepthOfField>,
<MotionBlur>, <AntiAliasing>, lens distortion and fisheye, plus raea's <Grade> for colour grading, <Outline>
and <Look>, which sets a whole finish at once. <FramePass> and <Compute> run your own shaders on the frame.
import { Camera3d } from "raea/camera";
import { Bloom, ToneMapping } from "raea/post_process";
import { Transform } from "raea/transform";
const Glow = () => (
<>
<Camera3d transform={Transform.fromXyz(-2, 2.5, 5).lookingAt([0, 0, 0])} />
<ToneMapping mode="tony" />
<Bloom intensity={1} threshold={0} />
</>
);import { AntiAliasing, Bloom, Compute, DepthOfField } from "raea/post_process"raea/post_process exports 12 components, 14 types. Bevy's counterpart: bevy_post_process, bevy_anti_alias. Source: sdk/raea/src/post_process.ts.
Components
AntiAliasing
How the 3D's edges are smoothed. Put it inside the Canvas3D, once; the first frame's holds for the piece.
Bevy: bevy_anti_alias::smaa::Smaa, bevy_anti_alias::fxaa::Fxaa.
Takes AntiAliasingProps's props.
Bloom
Glow around bright parts (emissive materials, strong highlights). Put it inside the Canvas3D.
Bevy: bevy_post_process::bloom::settings::Bloom.
Takes BloomProps's props.
Compute
A compute over the frame, in the 3D beside the camera; several run in the order written, before the cameras draw.
Rust: raea_sys_compute::Computes.
Takes ComputeProps's props.
DepthOfField
A lens's depth of field: what's nearer or farther than the camera's focus blurs. Put it inside the Canvas3D;
pull focus by animating the camera's focus. Inside a Camera3d it is Bevy's component on the camera, its
focal_distance the camera's focus.
Bevy: bevy_post_process::dof::DepthOfField.
Takes DepthOfFieldProps's props.
Fisheye
A fisheye lens on the camera: a pass over the 3D after tone mapping (the page over it stays straight). Every value
animates per frame; a lens's vignette and colour fringes are
Takes FisheyeProps's props.
FramePass
A full-screen pass over the scene camera's frame, beside the camera; several run in the order written. With a
frame
Rust: raea_sys_framepass::FramePasses.
Takes FramePassProps's props.
Grade
The camera's colour grade (lift, gamma, gain and saturation per range of light, before tone mapping), a lens vignette and colour fringes: one grade over every scene, so a film looks of a piece. Put it inside the Canvas3D.
Bevy: bevy_render::view::ColorGrading, bevy_post_process::effect_stack::chromatic_aberration::ChromaticAberration, bevy_post_process::effect_stack::vignette::Vignette.
Takes GradeProps's props.
LensDistortion
Barrel or pincushion distortion (Brown-Conrady k1, k2) on the camera, a pass after tone mapping as Fisheye's. Put it inside the Canvas3D.
Bevy: bevy_post_process::LensDistortion.
Takes LensDistortionProps's props.
Look
The whole frame drawn as a model's look draws its model: every part of the scene in the one grain, its bloom, fog and blur inside it. Put it inside the Canvas3D. mode="paint" or "cartoon" draws every standard material as paint (engine/render paint.rs):
Bevy: bevy_pbr::mesh_material::MeshMaterial3d<bevy_pbr::extended_material::ExtendedMaterial<bevy_pbr::pbr_material::StandardMaterial, raea_render::engine::paint::Paint>>.
Takes LookProps's props.
MotionBlur
Motion blur from each part's and the camera's motion over the frame before (Bevy's MotionBlur): with it, each frame is drawn after the one before it, so a frame's blur is the same in a render, a seek and play. A frame without it has none. Put it inside the Canvas3D.
Bevy: bevy_post_process::motion_blur::MotionBlur.
Takes MotionBlurProps's props.
Outline
Ink lines where the scene's depth steps or its surfaces turn, drawn before bloom and tone mapping; under a
Takes OutlineProps's props.
ToneMapping
The camera's tone mapping and exposure. Put it inside the Canvas3D.
Bevy: bevy_render::view::Tonemapping, bevy_camera::camera::Exposure.
Takes ToneMappingProps's props.
Types
AntiAliasingProps
| Field | Type | What it is |
|---|---|---|
mode (required) | "msaa" | "smaa" | "fxaa" | "none" | "msaa" (4x multisampling, the default), "smaa" (morphological, high preset: the default with AmbientOcclusion, which can't multisample), "fxaa" (cheapest, softest) or "none". Each draws a frame from that frame alone, so a render equals its play (temporal anti-aliasing isn't offered: it needs the frames before) |
BloomProps
| Field | Type | What it is |
|---|---|---|
intensity | number | 1 is a natural glow, 2 strong; 0 none |
threshold | number | only what's brighter than this glows (0 = everything a little; 1 = only emissive parts and highlights) |
skip | string[] | composition passes (a part's pass) left out of it: they neither glow nor take the glow |
passes | Record<string, number> | each named pass's share of it, 0 (as skip) to 1 (all of it, as "scene" has) |
ComputeProps
A compute shader (raea-sys-compute). Its source has one @compute entry point and reads time() (seconds),
u_NAME() for each uniform, each buffer as NAME: array<f32> (read and written) and each image as
NAME: texture_storage_2d<rgba8unorm, write>.
| Field | Type | What it is |
|---|---|---|
file | string | a shader file compiled as written, staticFile("shaders/x.wesl"), in place of wesl: its @group(0) storage textures and uniforms its own, as a Bevy example's render-graph node binds them |
entry | string | null | a file's entry point this frame (null or left out: nothing dispatched), as a node picks its pipeline |
bind | Record<string, string> | a file's storage textures this frame, each an image's key ({ input: "a", output: "b" }), as a node picks its bind group (a ping-pong swaps them frame by frame) |
wesl | string | the source in WESL (a superset of WGSL) |
wgsl | string | the same in plain WGSL |
workgroups (required) | number[] | how many workgroups a frame dispatches: [x], [x, y] or [x, y, z] |
uniforms | Record<string, number | number[] | string | Record<string, number | number[] | string |… | up to 16, as a surface shader's; a file's struct field may be { start: NAME } or { end: NAME }: the range of the mesh buffer NAME binds in its slab, in its array's elements (compute_mesh's range.start * 8) |
buffers | Record<string, { size: number } | number[] | StorageValue | MeshSlab> | up to 4, the compute's own, kept from frame to frame: { size } (that many f32s, zeroed) or its first contents. A file's: each var<storage> it declares, its first contents in its type's layout or a mesh's slab |
images | Record<string, string | { src: string; sampler?: SamplerSpec }> | up to 4 "render:NAME" (or "render:NAME:WxH") images it writes, which any shader's texture reads by that name (a file's may be { src, sampler }: the sampler it is drawn with) |
defs | ShaderDefs | shader defs, as a surface shader's |
DepthOfFieldProps
| Field | Type | What it is |
|---|---|---|
aperture | number | the lens's f-number: lower blurs more (1.4 dreamy, 2.8 cinematic, 8 nearly sharp); 2.8 by default |
mode | "bokeh" | "gaussian" | "bokeh" (round, as a real lens; the default) or "gaussian" (soft, cheaper) |
maxBlur | number | the largest blur, in pixels of the frame (64 by default) |
focal_distance | number | Bevy's fields, as Bevy's DepthOfField component on a Camera3d (inside it), Bevy's defaults: the distance in focus (10; the camera's focus), the f-number (1), the largest blur in pixels (64), "Bokeh" or "Gaussian" |
aperture_f_stops | number | the f-number (1): lower is a shallower focus |
max_circle_of_confusion_diameter | number | the largest blur, in px (64) |
FisheyeProps
| Field | Type | What it is |
|---|---|---|
strength | number | 0..1 (1 by default): 0 is the camera's own picture, pixel for pixel; ramp it in and out on beats |
mode | "equidistant" | "stereographic" | "equidistant" (the default) or "stereographic" (gentler at the edges) |
fov | number | the vertical field of view, in degrees, the camera widens to at strength 1 (its own by default): 100-150 for a real fisheye; the picture is still remapped corner to corner, so nothing goes black |
zoom | number | above 1 crops in, below 1 shows past the picture's edge (see edges) |
edges | "clamp" | "black" | what shows where the lens looks past the picture: its edge pixels ("clamp", the default) or "black" |
animate | Animate3d | declared animation of its numbers (strength, fov, k1, k2, zoom) |
FramePassProps
A shader over the whole frame. Its source defines
fn shade(in: FullscreenVertexOutput) -> vec4
| Field | Type | What it is |
|---|---|---|
file | string | a shader file compiled as written, staticFile("shaders/x.wesl"), in place of wesl: its @group(0) bindings its own, as custom_post_processing's pipeline binds them: each texture_2dframe names) or an image of textures, each sampler Bevy's default (nearest) or samplers' one, each varuniforms (which may change frame by frame) |
frame | string | a file's texture that reads the frame as drawn so far |
entry | string | a file's fragment entry point ("fragment" by default) |
samplers | Record<string, SamplerSpec> | a file's samplers by name |
wesl | string | the source in WESL (a superset of WGSL) |
wgsl | string | the same in plain WGSL |
uniforms | Record<string, number | number[] | string | StorageValue> | up to 16, as a surface shader's (a file's: each var |
textures | Record<string, string | Record<string, unknown>> | up to 4, as a surface shader's, each read with its own sampler |
defs | ShaderDefs | shader defs, as a surface shader's |
at | "before-tonemap" | "after-tonemap" | "after-tonemap" (display colours; the default) or "before-tonemap" (the HDR frame, after bloom) |
prepass | boolean | { depth?: boolean; normal?: boolean } | the camera's depth and normal prepasses, for depth(uv) and normal(uv) (turns its multisampling off) |
GradeProps
| Field | Type | What it is |
|---|---|---|
temperature | number | warmer (+) or cooler (-): a shift of the CIE x chromaticity, about -0.3 to 0.3 |
tint | number | magenta (+) or green (-) |
saturation | number | the whole frame's saturation after tone mapping (1 keeps it) |
shadows | GradeSection | the grade on the shadows: saturation, contrast, gamma, gain, lift |
midtones | GradeSection | the grade on the midtones |
highlights | GradeSection | the grade on the highlights |
vignette | number | { intensity?: number; radius?: number; smoothness?: number; color?: string } | the corners darkened: 0 none, 1 black; or {intensity, radius (0.75), smoothness (5), color} |
aberration | number | a lens's colour fringes at edges, as a fraction of the frame (0.005 subtle, 0.02 strong) |
skip | string[] | composition passes (a part's pass) the grade leaves as drawn: no fringes or vignette on them, and on an untonemapped camera (a paint Look's) no colour grading either |
passes | Record<string, number> | each named pass's share of the grade, 0 (as skip) to 1 |
GradeSection
| Field | Type | What it is |
|---|---|---|
saturation | number | 1 keeps the colour; 0 grey; above 1 richer |
contrast | number | 1 keeps it; above 1 harder |
gamma | number | 1 keeps it; above 1 darker mids |
gain | number | a multiplier: 1 keeps it |
lift | number | added: 0 keeps it, positive lifts the blacks |
LensCommon
| Field | Type | What it is |
|---|---|---|
edges | "clamp" | "black" | what shows where the lens looks past the picture: its edge pixels ("clamp", the default) or "black" |
animate | Animate3d | declared animation of its numbers (strength, fov, k1, k2, zoom) |
LensDistortionProps
| Field | Type | What it is |
|---|---|---|
k1 | number | Brown-Conrady: r' = r (1 + k1 r^2 + k2 r^4), r a share of the half-diagonal. Above 0 barrel, below 0 pincushion |
k2 | number | the r^4 term of the distortion |
zoom | number | by default just enough for a barrel's corners to stay inside the picture (1 for pincushion) |
edges | "clamp" | "black" | what shows where the lens looks past the picture: its edge pixels ("clamp", the default) or "black" |
animate | Animate3d | declared animation of its numbers (strength, fov, k1, k2, zoom) |
LookProps
MotionBlurProps
| Field | Type | What it is |
|---|---|---|
shutterAngle | number | the fraction of a frame the shutter is open (0.5 by default, a 180-degree shutter); above 1 smears further |
samples | number | samples each way per pixel (4 by default) |
animate | Animate3d | declared animation of its numbers |
OutlineProps
| Field | Type | What it is |
|---|---|---|
color | string | the line's colour, a hex colour (black by default) |
depth | number | a silhouette: how far behind the nearer surface (a fraction of its distance) a neighbour must be (0.04) |
normal | number | a crease: how far the surface must turn between neighbours, 1 - cos of the angle (0.3, about 45 degrees; 0 for none) |
width | number | the line's width in pixels (1 by default; a cartoon's line is about 6) |
far | number | how far from the camera lines reach, in metres: they fade out over its last third, so a far field meeting the sky draws no line along the horizon (no limit by default) |
skip | string[] | composition passes (a part's pass) drawn with no line: none on their pixels and no crease against them (a scene object's silhouette in front of one still draws, on the object) |
ToneMappingProps
| Field | Type | What it is |
|---|---|---|
mode | "tony" | "aces" | "agx" | "neutral" | "reinhard" | "none" | how bright light maps to the screen: "tony" (Bevy's default), "aces" (filmic, contrasty), "agx", "neutral", "reinhard", "none" |
exposure | number | stops brighter (+) or darker (-) than the default exposure |