PT / GI Packs
pt_surface.hlsl: shade a pack in the path tracer and the offline GI renderer. PackEvaluate, PtPackIn / PtPackOut, textures, the denoiser and limits.
What a PT / GI pack is
A surface pack (surface.hlsl) shades the raster and ray-traced camera views with PackShade. The path tracer (--render pt, the PT video renderer) and the offline GI renderer (GI stills and videos) trace every ray inside one compute shader, so PackShade cannot run there. A pack can opt in with a second file, pt_surface.hlsl, that describes the material at a hit. The renderer keeps the light transport: sun shadows, indirect light, the irradiance cache and the denoiser.
This needs MMDX12 1.5.0 or newer. A pack without the file uses the default shading in PT and GI, and older versions ignore the file.
Files
my_pack/
pack.json manifest (unchanged)
surface.hlsl raster / real-time RT (PackShade)
pt_surface.hlsl path tracer / offline GI (PackEvaluate)
shared.hlsli optional: maths both files include
Both files can #include "shared.hlsli" from the pack folder, so the look is written once. The online gallery pack nimble_toon does exactly this.
PackEvaluate
PtPackOut PackEvaluate(PtPackIn i);
PtPackIn
| Field | Meaning |
|---|---|
pos, normal, V, uv | world position, shading normal, direction to the camera, texture coordinates |
L, sunVis | direction to the sun, sun visibility 0..1 from the traced shadow ray (0 on diffuse bounces) |
baseColor | linear texture colour times the material colour, no lighting |
materialClass | PACK_BODY / SKIN / FACE / EYE / HAIR / WEAPON from your classes rules |
params[16] | your sliders, in pack.json order |
headPos, headScale, headRight, headUp, headForward, headValid | the head bone frame, as in PackSurface |
PtPackOut (assign every field; pack_check warns when one is never assigned)
| Field | Meaning |
|---|---|
albedo | linear surface colour; what diffuse bounces and the denoiser see |
shadowTint | linear multiplier for the shaded side |
shadowBias | shifts the terminator in N.L units (a smoothed face normal: dot(Nsmooth, L) - dot(normal, L)) |
terminator | (lo, hi) N.L edges of the terminator smoothstep; (0, 0) = the engine default |
specular | additive linear radiance (rim, gloss, matcap); multiplied by sunVis |
flatFace | use the engine’s flat-face handling (no indirect gradient on this surface) |
How the renderer uses it
- Camera rays and mirror / glass chains:
lerp(albedo * shadowTint, albedo, term * sunVis) * sunIntensity + specular * sunVis, whereterm = smoothstep(lo, hi, N.L + shadowBias). - Diffuse bounces (light gathered by other surfaces): only
albedois used. Toon terms never run on gather rays. - Do not add an ambient term: the path tracers trace the real indirect light.
- A half-Lambert ramp
threshold +- softnessmaps toterminator = (2 * (threshold - softness) - 1, 2 * (threshold + softness) - 1).
Textures
The textures declared in pack.json are available (explicit LOD only, no implicit derivatives):
float4 PtPackSampleTex(uint i, float2 uv); // level 0
float4 PtPackSampleTexLevel(uint i, float2 uv, float lod);
uint PtPackTexCount();
float2 PtPackTexSize(uint i);
An out-of-range index or a missing file gives white, as in PackSampleTex. sRGB textures sample as linear values.
Limits
- Characters only; stages and props are never shaded by packs.
- With several different PT packs in one scene the first is used (a warning is logged).
- The offline GI renderer draws the app’s default outlines (
PackEdgeapplies to raster and RT). - The denoiser treats the pack’s direct light like the default toon light. A hard
step()highlight kept crisp edges in a still test; fast motion was not checked. - Avoid unbounded loops in
PackEvaluate: a GPU hang closes the app. - Check a pack with
pack_check <dir> --compile: it compiles the raster, offline GI and path tracer variants.