5.7 KiB
Clear coat for CPU and GPU images
The Appearance editor’s Clear coat section uses the same saved opaque coat for CPU ray tracing and GPU path tracing. Select a whole body, enable the coat, edit Weight, Roughness and IOR, then choose Save coat to shot. Edits are previewed through a stable-target request overlay; painting the editor never saves them. Discard coat edit restores the saved state. Replacing different saved numbers requires the explicit Replace control. Removing a saved coat has a confirmation and one Undo step.
A saved setup carries coats: [{target, coat: {weight, roughness, ior}}] beside
its existing surface and optical bindings. render.coat.bind takes the exact
render.scene body ordinal/name and expected_revision, then stores the existing
stable definition/feature/component/occurrence identity. Check and Start resolve
that identity again. A hidden or replaced body is refused; a later body with the
same name or ordinal never inherits the coat. render.coat.unbind removes one
exact saved target without requiring a renderer. Empty lists are omitted, so old
setups retain their serialized shape.
Both methods support the existing 64-target override bound, weight 0..1, roughness 0.045..1 and relative IOR 1..3. All saved numbers are checked even at zero weight. Saved coats require an opaque target and cannot share a Glass target, including at weight zero. CPU selected reflection and anisotropy overrides also cannot share a coated target. These unsupported combinations refuse without clearing authored values. CPU direct shading and its explicit progressive Path transport consume the same coat; raster remains schematic and refuses bindings.
Saved maps may share an opaque coat target. They alter the substrate and shading normal while retaining the coat parameters. Existing aging changes the substrate and reduces coat weight by its physical layer mask; it keeps coat roughness/IOR and never rewrites the saved coat. The guided denoiser operates on complete image updates while the raw EXR remains unfiltered. Coat Save/Discard/Remove and saved Undo/Redo preserve independent local map drafts. A conflicting external edit keeps a pending local coat edit visible until it is discarded or reviewed again.
Existing GPU runtime override API
GPU progressive image jobs accept an independent dielectric GGX coat over an opaque metallic/roughness substrate. A dark, moderately rough plastic base with a smooth coat can show narrow softbox reflections while retaining a dark body. Chrome uses the existing metallic surface; it does not need a coat.
{"command":"render.start","params":{"progressive":true,"raytrace":{"clear_coats":[{"body_index":0,"body_name":"Housing","weight":1,"roughness":0.12,"ior":1.5}]}}}
render.preflight accepts the same override. Index and exact name must both match
the captured render.scene body. At most 64 distinct bodies can be overridden.
Weight is finite in [0,1], perceptual roughness in [0.045,1], and relative IOR in
[1,3]. Unknown fields and repeated indices are errors. Weight zero is a no-op.
Coats cannot be combined with runtime transmission on the same body. Overrides
apply to the body and its faces in the job snapshot, never to the saved document,
engineering materials or source appearance. They require GPU progressive rendering.
The existing raw API retains its zero-weight no-op behavior; the saved binding
route above additionally requires an opaque target even at zero weight.
The implementation adds single-scattering GGX/Smith coat reflection with its own
Schlick Fresnel term. For cosine c, define the conservative directional bound
B(c) = Fcoat(c / sqrt(2 * (1 + sqrt(1 - c*c)))). The base BRDF is multiplied by
(1-weight*B(N.V))*(1-weight*B(N.L)). This is deliberately stronger attenuation
than macro-normal Fresnel, which can leak energy with rough coats over shiny bases.
For any reflected upper-hemisphere direction, the view/half-vector cosine is at
least the argument used in B. Schlick decreases with cosine, and a unit-Fresnel
single-scattering Smith GGX lobe reflects at most one. Thus the coat contributes
at most weight*B(N.V); the attenuated unit-energy base contributes at most
1-weight*B(N.V). The symmetric product retains reciprocity. This conservative
allocation can darken grazing views and omits internal inter-layer scattering and
refracted substrate directions; it is not a full layered transport solution.
Importance-sampled f64 furnace quadrature covers shiny bases, rough coats, partial
weights, IORs and grazing directions, alongside analytic and reciprocity checks.
Continuation selects the coat GGX lobe with probability weight/2, otherwise the
existing equal cosine/GGX base mixture. Evaluation uses the complete layered BRDF;
the PDF is the same combined mixture for continuation and direct-light MIS. GGX
samples below the surface are rejected without renormalizing the PDF. Zero weight
preserves the old BRDF and random-number consumption exactly. GPU BRDF/PDF tests
compare the production shader to the independent f64 numerical contract, including
roughness 0.045 and grazing directions.
The per-triangle resolved material occupies 64 bytes, including a 16-byte coat record. Storage-limit checks and peak-memory estimates include this additional record.
The model was designed from the public descriptions of layered materials in PBRT and Filament's clear-coat model. These motivate independent interfaces and substrate attenuation; this implementation is original and deliberately simpler than a multilayer random walk. No reference implementation source was copied.