Feature reference

Every feature, documented field by field.

Pixel-level cel-shading — broken down into the renders you'd ship, the ACelshadingVolume field that drives each one, and the matching r.Celshading.* CVar.

Pipeline-aware shading

Lumen GI, shadow maps, exponential height fog, volumetric fog and volumetric lights all feed into the cel-shaded output. The pass reconstructs and re-applies fog around quantization so colors stay clean. No GBuffer override, no PostProcess-Material double-fog hack.

Volume: bFogAware, FillLightStrength · CVars: r.Celshading.FogAware, r.Celshading.MasterStrength

Per-actor opt-in or Non-Stencil Full-screen

Two targeting modes. Set CustomDepth Stencil Value on a mesh component (any slot 1..255) to opt that actor in. Or flip bAffectWholeScreen to skip per-actor setup entirely and process every visible pixel.

Volume: StencilSlot, bAffectWholeScreen · CVars: r.Celshading.AffectWholeScreen, r.Celshading.CelStencilMask, r.Celshading.CelStencilRef

Zone Overrides

Each ACelshadingVolume can drive the level's atmosphere while the camera is inside its bounds — three independent channels in one actor:

  • Directional light (Sun): intensity, color, indirect bounce, volumetric scattering, source angle, rotation. Walk between zones and the values blend smoothly instead of snapping — ideal for indoor/outdoor transitions or per-slot warm/cool suns.
  • Sky Light: intensity and color. Cool a cave with one volume, warm the courtyard with the next — no Level Sequence, no PostProcess hack, no second sky light actor.
  • Exponential Height Fog: density, height falloff, inscattering color, directional color, start & cutoff distance, height. The cel pass is fog-aware on top: fog is reconstructed before quantization, so banding stays clean even with thick haze.

Every field is individually opt-in via its bOverride_* bit and follows the same SpatialFalloff as the rest of the volume. When the camera leaves every overriding volume, the level's own sun, sky and fog values come back.

Volume → Atmosphere: Sun*, SkyLight*, AtmosFog* fields (each with its own bOverride_*)

Color-aware outline + Outside halo

Adaptive Laplacian outline INSIDE the silhouette (creases, depth jumps) plus an optional OUTSIDE halo wrapping the actor. The inside outline can color-tint by the underlying surface (anime self-shadow line look). The outside halo runs as a second compute pass that samples the cel-shaded silhouette pixel directly, so it carries every internal effect — band quantization, SpecularHighlightColor blik, rim glow, fog - as a single line around the actor. Zero cost when off (OutsideOutlineThickness=0): second pass is not dispatched.

Color-aware outline tinting per surface color

Volume: bColorAwareOutline, OutlineColorAwareness, OutlineStrength, OutlineThicknessNear/Far, OutsideOutlineThickness · CVars: r.Celshading.ColorAwareOutline, r.Celshading.OutlineColorAwareness, r.Celshading.OutsideOutlineThickness

Volume style blending

Drop ACelshadingVolume actors. Bounded volumes use a smooth SpatialFalloff around their box; overlapping volumes sort by BlendPriority and blend per-field. The workflow mirrors PostProcessVolume — the same mental model your art team already uses.

Smooth spatial blending between two style zones

Volume: bUnbound, SpatialFalloff, ContributionWeight, BlendPriority

Surface response: Metallic & Emissive

The cel pass treats different surface types as different surface types. Two dedicated response paths preserve material identity through quantization:

  • Metallic: the lit side of metallic surfaces is lifted, shadows are floored so they don't crush to black, and a tinted specular highlight is clamped to the brightest band — chrome rims and sword blades read correctly next to matte cel-shaded surfaces.
  • Emissive: emissive pixels punch through cel quantization without bleeding into neighboring bands. Unlit materials are kept out of the bands automatically. On lit materials, tag glowing areas by routing 1 − EmissiveMask into the Ambient Occlusion output — the cel pass reads it as the emissive marker, so no GBuffer override or extra texture is required — or lower the brightness cutoff to catch very bright pixels automatically. A multiplier sets the glow strength, and a dark-side boost keeps emissives readable when they sit in shadow.

Both paths are optional and per-zone — you can mix matte cel surfaces, glossy cel metals and glowing cel emissives in the same volume.

Volume: bMetallicLightCompensation, MetallicShadowFloor, MetallicLitMultiplier, MetallicHighlightIntensity, SpecularHighlightColor, EmissiveAOCutoff, EmissiveBrightCutoff, EmissiveMultiplier, EmissiveDarkBoost · CVars: r.Celshading.Metallic*, r.Celshading.Emissive*

Halftone (manga screentone)

Manga-style dot overlay. A regular dot grid is drawn over the cel-shaded fill — dots grow in shadow areas and fade out in the lit zone. Configurable cell pitch, angle, threshold, softness, opacity and color. Branch on the master toggle is GPU-uniform, so the entire block costs zero when disabled.

Volume: bHalftoneEnable, HalftoneScale, HalftoneAngle, HalftoneThreshold, HalftoneSoftness, HalftoneStrength, HalftoneColor, PatternDepthReference (optional) · CVars: r.Celshading.Halftone*

Band mask

Comic-inspired ink mask along the band transitions. A screen-space pattern biases each pixel's brightness up or down by up to one band's width — pixels right at a band boundary are pushed stochastically to lit or shadow, producing classic comic-book ink in place of crisp cutoffs. Different from Halftone: this modifies the band assignment itself, not an overlay on top. Dial BandDitherStretchX from 1 (DOTS) to 5+ (STROKES), rotate via BandDitherAngle (45° = classic comic ink direction), and dial BandDitherSoftness from 1 (soft halftone) to 0 (hard pixel edges).

Band Mask: dots vs 45° comic strokes

Volume: bBandDither, BandDitherScale, BandDitherAngle, BandDitherStrength, BandDitherStretchX, BandDitherSoftness, PatternDepthReference (optional) · CVars: r.Celshading.BandDither*

Pattern Depth Reference (sprite-anchor): by default, Halftone + Band Mask cells are screen-fixed (constant pixel size at any distance). Assign an Actor to the volume's PatternDepthReference and the pattern behaves like a billboard sprite locked to that actor's pivot — the cell grid tracks its projected screen position AND zooms outward from its centre as you approach. Strobe-free across the whole stencil slot.

Rim light

Directional screen-space rim. Configurable cone width, hardness, sharpness, and Fresnel weighting. Optional sun-anchored mode — the rim follows the level's directional light direction projected to view space. Optional outline-mix gates rim by the toon outline edge mask for "rim along the line-art" looks.

Back-anchored rim light with Fresnel modulation

Volume: RimLightIntensity, RimLightColor, RimLightAngle, RimLightSpread, RimLightHardness, bRimLightFollowSun, RimLightOutlineMix

Rim band dither

The rim's fade-out, redrawn as classic comic-ink halftone. A solid crisp contour hugs the silhouette exactly like before — but its transition into transparency is replaced with size-modulated halftone dots that shrink geometrically as the rim falls off. This is true halftone, not a faded overlay: every pixel is either fully rim or fully empty, and it's the dot radius that scales with the fade — never the opacity. The look is the manga / comic "ink-pen rim" that printed comics use to terminate light edges without going to a soft gradient.

Two parameters, three regimes. RimDitherSolidEdge (0..1) controls how wide the un-dithered solid contour is, measured in geometric silhouette-distance (1 − NdotV) so the result is independent of RimLightHardness and Fresnel exponent — set 0 for full halftone gradient across the whole rim, set 1 to keep the rim entirely solid (effectively disabling the halftone for that volume). RimDitherSoftness (0..1) controls both the dot edge sharpness and the solid↔dithered boundary width with one knob: 0 gives razor-crisp ink circles with an instant solid edge (true comic ink), 1 gives a soft halftone falloff with a wide blend. Independent from BandDitherSoftness so the body and the rim can have different edge styles in the same volume.

Works at RimLightSharpness = 0. The fade-out signal is sampled before the optional sharp step() cutoff, so it stays graduated even when the visible rim is binary. This is the key sanity check that earlier "dither + sharpness" tries fail: smooth gradient rims and stepped rims both transition cleanly. Reuses the screen-space cell grid from bBandDither (pattern, scale, angle, stretch, sprite anchor are all shared with body dither, so the body's ink and the rim's ink animate together) — zero extra texture loads, no second pattern to tune.

TAA-stable. Dot radius is driven by 1 − NdotV (a per-pixel geometric value) and cell distance (deterministic from integer pixel coords). No discrete decision branches, no Side selection — no source of frame-to-frame jitter that could cause strobing under temporal AA.

Rim Band Dither: solid silhouette contour with halftone fade-out into transparency

Volume: RimDitherStrength, RimDitherSolidEdge, RimDitherSoftness (Rim Light group; requires bBandDither on) · CVars: r.Celshading.RimDither*

Algorithm in one line: FadeProgress = saturate((1 − NdotV) / (1 − SolidEdge)) drives a circular dot of radius = MaxRadius × FadeProgress; pixels with CellDist < radius get the full rim contribution, pixels outside the dot get none. The solid contour is just the saturation plateau where FadeProgress = 1 covers the whole cell. No opacity modulation, no quantizer dependency, no hidden state — what you see is a screen-space dot mask whose radius traces the rim's geometric fade.

Five included presets

Five UCelshadingPreset DataAssets ship in the plugin. Each captures a complete look: band count, shadow curve, outline, rim, color tinting. Apply directly via the volume's Active Preset field, or fork and re-save your own.

Predictable GPU cost

One compute dispatch per active stencil slot (any of 1..255 — pay only for what you populate). Empty slots cost zero, and inside a pass the pixels that don't carry the slot's stencil value exit early. Cost scales with resolution and the number of active slots — and it has its own row in the GPU profiler, so you see exactly what you spent on your target hardware.

stat gpu → Celshading; ProfileGPU / Insights → Celshading.Cel[N], Celshading.Halo[N].

BUILT TO SHIP

Production tool. Owned source. Direct support from the developer.

Engineered against the post-opaque RDG path, profiled per-pass on consumer GPUs, hardened across UE point releases, and shipped with the full C++ and USF source so your team can audit, profile and extend it on its own terms.

  • No engine fork. Drops onto stock Unreal — your team's engine repo stays the engine repo. No 100 GB custom-build to clone, re-fork on every UE point release or mirror in CI/CD.
  • Auditable shader path. Compute pass and USF source ship as readable code. No obfuscation, no encrypted blobs — review it, modify it, profile it on your hardware.
  • Direct developer contact. Bug reports get answered by the person who wrote the renderer. Fixes ship in the next patch, not the next quarter.
  • Engineered for upgrades. Engine version bumps stay routine — no project-wide event, no fork rebase, no marketplace plugins drifting out of sync.

Production-grade cel-shading that runs on stock Unreal — pay the plugin licence once and keep your engine upgrades routine.

Get the plugin

Get it on Fab.

Unreal Engine 5 · full C++ and shader source. Questions or studio & multi-seat licensing: aaanextgenfactory@gmail.com.