Open Asterune
Anti-aliasing and render resolution

Anti-aliasing and render resolution

Understand multisample coverage, FXAA edge searches, specular filtering, and the renderer's temporal and spatial resolution paths.

Asterune treats rasterization coverage, shading detail, and image reconstruction as separate problems. Multisample anti-aliasing (MSAA) resolves coverage inside one frame. Temporal anti-aliasing (TAA) reconstructs an image from jittered frames and motion data. Fast approximate anti-aliasing (FXAA) searches the tone-mapped image for edges. Material shading also filters specular highlights before any of those image treatments.

These choices change the renderer's attachments and available passes. They also determine whether the world can render below the output resolution. Temporal anti-aliasing describes accumulation and history rejection in detail.

Coverage samples and scene attachments

The multisampled path allocates separate multisampled color and depth targets. Color uses Rgba16Float; depth uses Depth32Float. A single-sample high-dynamic-range (HDR) texture receives the color resolve and supplies later image processing. The depth texture retains its individual samples for passes that read depth.

With MSAA disabled, the scene color attachment is the HDR texture itself. Opaque rendering also writes surface information, motion, and reflection information into separate attachments. The MSAA shader variants omit those outputs. Their surface and reflection textures are 1 × 1 placeholders instead of full-size tracing buffers. Switching anti-aliasing therefore changes more than the final edge filter: the multisampled path avoids the desktop temporal and screen-reflection data path.

The engine chooses the sample count once per process. An explicit internal ASTERUNE_MSAA override selects 1 for a parsed value of 1, 2 for 2, and 4 for every other parsed value. Without that override, an anti-aliasing mode other than MSAA selects 1 sample. The selected quality profile requests either 2 or 4 samples when MSAA is active.

Device selection checks the requested count against the HDR, depth, and glow formats. The 2-sample path also requires the adapter-specific texture-format feature. If the request fails those checks, the engine logs the unsupported count and selects 4 samples. This is a fixed 4-sample fallback, rather than a search through progressively lower counts.

Changing quality or anti-aliasing during a session does not rebuild every scene pipeline at a different sample count. If the settings request a count different from the chosen count, apply_quality logs that the existing count remains active until the next launch. The pass dispatch uses the chosen count when deciding whether the scene is multisampled.

Resolve boundaries and tile memory

The renderer resolves color when another pass needs a single-sample scene image. Water and refraction need such an image to sample the world behind their surfaces. If neither feature needs an intermediate copy, the engine can draw more of the world before its final resolve.

For a multisampled scene, the opaque pass can also draw the sky and blended surfaces. The WorldTail decision records how much of that work it includes: water takes precedence, then refracting surfaces, then a complete world when order-independent transparency and viewmodels do not require later work. Otherwise, the opaque pass includes only the sky beyond its opaque geometry. Later passes inspect that decision instead of drawing the same content twice.

Combining work reduces render-pass boundaries. A tile GPU otherwise has to preserve and reload multisampled attachments across those boundaries. The final scene attachment resolves into HDR and discards its multisampled color storage; an intermediate attachment stores the samples for subsequent draws. Depth remains available separately.

Cutout materials use an alpha threshold and discard rejected fragments. The depth prepass applies the same coverage test as the color pass, so a hole in a texture does not leave an invisible depth occluder. Level-of-detail crossfades also use matching dither tests in their depth and color passes. These are shader coverage decisions; MSAA adds rasterization samples around the fragments that remain.

FXAA runs only when the resolved anti-aliasing setting selects Fxaa. Its input is the tone-mapped scene, before place effects, custom effects, and the screen interface. It has no history, motion vectors, or depth input.

The shader estimates brightness with the square root of a weighted RGB sum. It first compares the center with its 4 immediate neighbors. If their brightness range is less than max(0.0312, maximum_brightness × 0.125), the shader returns the original sample. That early exit leaves low-contrast regions untouched.

For an edge candidate, the shader reads the diagonal neighbors and compares horizontal and vertical brightness curvature. It selects an edge orientation and the steeper perpendicular gradient. From the half-pixel position between the two sides, it searches along the edge for a sufficient brightness change. The search has a 12-iteration budget, with larger strides in its later iterations.

The distance to the nearer endpoint produces an offset across the edge. A separate subpixel term compares the center with a weighted neighborhood average and uses a quality factor of 0.75. The larger of the valid edge offset and the subpixel offset selects the final filtered sample. Alpha comes from the center sample.

This filter operates on visible contrast. A texture edge and a polygon edge can both trigger it. Because it filters a single image, it does not accumulate the changing sample positions that TAA uses to stabilize subpixel detail.

Specular filtering before image reconstruction

The world shader widens highlights when normal variation exceeds the detail a pixel can represent. A narrow highlight can flicker even when the polygon's silhouette has enough coverage samples. Filtering the material's roughness addresses that source of aliasing before the image reaches MSAA, TAA, or FXAA.

The shader combines screen derivatives of the shading normal with variance from the normal map. Normal-map mip filtering preserves the length of the averaged normal rather than renormalizing it. A shortened average indicates that the original normals point in different directions. The shader converts that shortfall into variance, with a dead zone of 0.01 to suppress small quantization differences.

For perceptual roughness r, the filter forms alpha = r² and adds a kernel to alpha². The derivative contribution is min(0.3 × (|dpdx(n)|² + |dpdy(n)|²), 0.2); normal-map variance contributes at most 0.5. Taking the fourth root returns perceptual roughness, clamped between the original value and 1. The operation can widen a highlight but cannot make the material smoother than its authored roughness.

World resolution and display resolution

Render scale changes the world viewport inside output-sized textures. The engine rounds the scaled width and height to whole pixels, with a minimum of 1 pixel. The display rectangle retains its output dimensions. Keeping both rectangles lets reconstruction map an internal world pixel to its intended display position without shrinking the interface.

The engine permits reduced world resolution when temporal accumulation is active or the scene is multisampled. With FXAA or anti-aliasing off in a single-sample scene, it forces render scale to 1. A low preset's configured scale therefore does not by itself establish the effective scale.

TAA reconstructs directly into display-sized history. A multisampled scene first resolves its coverage, then uses a spatial upscale when scale is below 0.999. The spatial shader applies a separable Lanczos-2 kernel: 4 weights on each axis combine 16 samples. It clamps the result to the minimum and maximum of the nearest 2 × 2 samples, limiting ringing around high-contrast edges. Its taps stay inside the world rectangle, preventing unrelated pixels outside that rectangle from entering the filter.

The resulting HDR image becomes the input to the lens chain. Tone mapping applies contrast-adaptive sharpening with an amount of 0.8 after a temporal resolve or spatial upscale. FXAA alone does not enable that sharpening.

Dynamic resolution feedback

Dynamic resolution adjusts scale from distinct GPU timing samples, between 0.5 and the configured scale ceiling. Its default budget is 14 ms, with a positive internal override available to the renderer.

When GPU time exceeds the budget, the target scale is scale × sqrt(budget / gpu_time). A single update reduces scale by at most 0.05. When GPU time is below 85% of the budget, an update adds 0.02. Inside that headroom band, scale remains unchanged. The different thresholds and step sizes keep a small timing fluctuation from immediately reversing the previous change.

The controller does not invent a timing result when none is available. It retains its dynamic scale until it receives a different sample. Disabling dynamic resolution sets scale directly to the configured ceiling. The same controller feeds temporal reconstruction and the MSAA spatial path.

Headset views

A headset defaults to MSAA. Selecting TAA also resolves to MSAA because the temporal renderer owns one accumulation history, which cannot represent both eyes. Each eye still needs its own view-dependent rasterization and coverage resolve. Work shared across the stereo frame is described in Frame pipeline.