Files
solvecraft/docs/render-opaque-pbr.md
T
selune 9e6e0b8df1 Render workspace: complete experimental ribbon, resources and effects integration (#5493) (#5494)
* wip(render): reconcile completed material and image pipeline source with 0.20 (#5493)

Source-only integration reference for staged review, not a merge-ready aggregate.
Donor: 0xlillith/solidcraft@aea20b0115.
Upstream: 858e5995d6.

Retain original CPU/material contracts and explicit GPU donor provenance in
docs/render-materials-lab.md (#2067, #2191, #2780, #3306). Preserve released
format migrations and current navigation/startup/shell behavior. New saved
records use pending descriptors. The physical-card descriptor follows the
maintainer's material-engineering-cards naming request, with a legacy-lab
format-29 regression. Replace raw new panel hosts with shared containers.

Repository checks, complete workspace/all-target Clippy, and the independent
physical/resource authoring checks pass for this source. The fresh full local
CI invocation has reached test compilation and is not yet complete. Do not
transfer donor native output or prior test totals to this integration.

Generated review screenshots, video, package ZIP and machine logs remain at
the immutable personal evidence pin. Uncommitted geometric displacement is
backed up separately, not silently included. No kernel/vendor change, direct
main write, release or independent contributor-branch rewrite.

Co-authored-by: Christian Svensson <blue@cmd.nu>

* test(cli): pin the Compact camera-sample admission diagnostic (#5493)

The bounded request still refuses 1024x1024x64 samples before host initialization and writes no output. Update only the stale expected diagnostic to the existing Compact budget wording; production limits and code are unchanged. Exact-tree CLI regression suite: 14 passed, 0 failed. The full repository gate has been invoked separately and is not yet complete.

* Normalize UI container violation paths across platforms

Report repository-relative paths with forward slashes so diagnostics and the existing nested-package regression assertions agree on Windows and Unix. Keep the container detection rules unchanged.

Validation: official ui-containers gate and all seven focused xtask tests passed; rustfmt and diff checks passed. Only xtask/dependencies were compiled; no application build or heavy tests.
(cherry picked from commit 44b58d8e10)

* fix(render): preserve camera contracts and correct acceptance regressions (#5493)

Preserve Design view.apply semantics with a separate strict Render camera command. Retain existing translations when English-only aliases collide; add material-axis entries and camera-specific pitch labels. Compare complete validated engineering cards and retain job handles before cancellation.

Focused and native acceptance is pinned to tree f8a423b21f. Full CI has been invoked separately and is not claimed green. Landing follows the render integration policy in #5493.

* fix(render): resolve showcase licence and upstream resource hygiene (#5493)

Record SychPL's explicit MIT OR Apache-2.0 grant, retaining source/model bytes and the Ubuntu lettering notice. Remove 80 absent attribution rows, personal fork records, dead delivery attributes and three fork/resource-only workflows. Existing xtask assets now performs bounded offline hash checks and rejects orphan render attribution; no parallel draft CI or package installation. Original authoring reproduction remains explicit. Scoped xtask tests, assets, Clippy, formatting and prepared SELENE validation pass; no application or renderer-wide result is inferred.

* fix(render): align saved bounds and progressive sample resolution (#5493)

Address review r4364211669/r4364211677/r4364211681/r4364211691/r4364211695. Persisted light/frame domains respect the existing 1e9 document bound; related LED/rectangle/direction cases use the same bound. Workstation primary resolve shares the 4096 admission cap and film counters use checked conversions. Exact candidate: 485 CPU render tests, 21 render document tests and strict scoped Clippy pass, including positive boundary round trips and 2048/4096 end-to-end checkpoints. No released format/migration edits, relaxed test limits or full-stack acceptance claim.

* wip(render): isolate optional resources and harden sample handling (#5493)

Review work in progress: opt-in encoded render library with reproducible runtime catalog exports; native-only robust dependency through the workspace; checked bloom slices; per-sample point-light diagnostics; independent area-light spatial/film keys. Resource-module binaries in both configurations, generation, Cargo production feature graphs and attribution checks pass. Full engine/render regression and combined acceptance are pending: do not merge this donor as a constituent or treat scoped resource proof as runtime acceptance.

* fix(gpu): preserve donor allocations through the scope-safety repair (#5722)

Port the native-validated #5723 scope-cleanup correction into the experimental donor, retaining its adaptive, volume, aging, photographic and bloom allocations. Same reverse-drain helper and regression cases; the donor combination still needs its own compile/test gate and is not covered retroactively by render-base results.

* claim: #1369

* Reject unknown pending concepts after format folding (#1369)

* test(render): close bounded sampling and bloom review regressions (#5493)

Validate the optional-resource/lighting/dependency donor at its exact tree. Required Vulkan GPU 127/0/3; CPU renderer 487/0; engine Render 72/0; document render 21/0; lean library 7/0; strict scoped Clippy, assets/layers/lists/containers and lean-engine WASM pass. Preserve original failed controller/typing attempts. This is the draft donor/disposition source, not a replacement for constituent render PRs or final main acceptance.

* wip(render): preserve stable aging target validation candidate

Exact staged source from prior review work on donor 3b892eab5e. This is a recovery/review checkpoint, NOT accepted delivery. An actual command-path regression currently fails because the selected executor refuses ordered surface aging. No assertion, capability or admission limit is weakened. Native/UI/full combination acceptance remains pending. Saved/runtime split adds one pending descriptor and retains original file scope; no main/render branch is changed.

* test(render): preserve numeric setup intent at the admitted coordinate bound

Follow up jyviko review on #5494 without weakening the corrected saved-coordinate contract. The positive clicked fixture uses 1e9 instead of inadmissible 2e9; explicit out-of-range cases still refuse, and the full saved setup round-trips through JSON and the native container. Exact-source Render workspace 95/0 and strict all-target UI Clippy pass. This does not incorporate the separately failing stable-aging candidate or claim full current-render acceptance.

* test(render): use the real GPU executor for saved aging

Move successful native cold-reopen Check/Start coverage from the CPU-backed protocol fixture into the production GPU executor tests. Verify capture-index changes preserve the stable target and that enabling aging changes both PNG and raw EXR output. Retain explicit, allocation-free refusal coverage for executors without aging support. Production capabilities and admission checks are unchanged.

* fix(render): persist stable identities for targeted aging (#5493)

Separate saved RenderBodyTarget identities from captured mesh ordinals. Reject legacy ambiguous targets, resolve Check/Start after visibility changes and preserve native reopening, copies and undo. Unsupported executors still refuse; real AMD Vulkan regression completes saved aging after reordered capture.

Source tree 6b84c8ad passes document 23, engine Render 75, GPU effects 5 and Render UI 95 with no failures; scoped strict Clippy, WASM and repository gates pass. Fresh CLI/native reopen and clean/aged GPU exports pass with identical physical totals and inspected actual images. Whole current-render integration is separate.

* fix(render): reconcile combined CPU/GPU contracts on current upstream

Unify captured resources/status and CPU-vs-GPU request schemas after the real two-parent integration; remove obsolete duplicate Vulkan selector and retain combined platform-filter regression. Deduplicate only identical or superseded test definitions, retaining the stronger card metadata and reserved-executor assertions. No assertions, admission limits or feature refusals are weakened.

All targets of render dependencies, GPU, engine, UI, CLI and native app pass cargo check with raytrace-cpu and bundled-render-library at tree c70d00d1. Native executable tests and full current-source acceptance remain pending. This is a private integration rehearsal for subsequent constituent PRs, not an aggregate official render/main merge.

* test(render): retain one complete host lifetime regression set

Deduplicate byte-identical DropProbe/task-lifetime tests and retain the adapted environment/texture ownership regressions with every assertion and complete memory estimate. Keep the registered-geometry admission regression once; only struct field order differed. Replace a one-element diagnostic loop with its equivalent block, preserving all wrap/width assertions.

Test-only integration repair. Formatting and diff checks pass; refreshed executable validation is required. No production behavior, limits, assertions or ignores changed.

* fix(doc): recognize the proven format-29 card descriptor alias

Normalize only the historical format-29 physical-material-cards spelling, and only when material-engineering-cards is registered. Admission and migration de-duplication use the same identity. Unknown concepts, wrong versions and incapable readers still refuse before mutation. Six controlled-history Rust regressions and actual CLI migration/no-clobber acceptance pass. No version, released migration or CI changes. Refs #5793, #1369, #5493.

* test(render): preserve grounding refusal before explicit instance moves

Update the two Render binding fixtures for upstream first-occurrence grounding. Verify grounded movement refuses without changing document/history, then explicitly unground through the public command before the intended move. Retain every target, resource, copy and persistence assertion. Refs #5493.

---------

Co-authored-by: Christian Svensson <blue@cmd.nu>
Co-authored-by: Victor Santos Oliveira <56417388+Vittuu@users.noreply.github.com>
Co-authored-by: cfaulkingham <colin.faulkingham@gmail.com>
2026-10-24 14:21:59 +00:00

8.1 KiB
Raw Blame History

Opaque product surfaces

This is the first integrated consumer slice of #1004, under #813/#620. It uses the existing Render workspace, product geometry, appearance assignment and PNG job host. There is no second renderer or external material dataset.

Apply and preserve a response

{"command":"appearance.assign","params":{"bodies":["Specimen"],"color":"#b47846","pbr":{"metallic":1,"roughness":0.3,"dielectric_ior":1.5}}}

The Appearance editor has an Opaque PBR checkbox and separate Metallic, Roughness and Dielectric IOR fields. Enabling it explicitly sets opacity to one. View the response in Render or export a product PNG; Design retains its simple color preview. Apply/OK, swatch reuse and component/face targeting use the existing assignment command. A later scalar face override replaces a body's PBR response on that face; it does not inherit the optical response implicitly.

Parameters are finite and bounded: metallic [0,1], perceptual roughness [0.045,1], relative dielectric IOR [1,3]. For PBR, unknown optical fields and nonnumeric/out-of-range opacity refuse before changing the design. PBR requires opacity exactly one. IOR controls the nonmetal fraction's reflection; it does not enable refraction. A library appearance selected by name keeps its complete preset response when pbr is omitted. An explicit color, including a custom look named like a library entry, starts with scalar shading unless a pbr block is supplied. Supplying pbr: null explicitly selects scalar shading. Assignment replaces a look; it is not a partial parameter patch.

Use name with an explicit color to copy an inert look label, including labels that happen to equal a starter.* ID. name and appearance are mutually exclusive. The Appearance editor uses name for owned looks and appearance only for an explicitly selected recipe, so reopening or reusing a copied look does not change its optical values. Explicit unavailable glass recipe requests still refuse; an inert label never enables refraction.

material.create/edit accept the same optional pbr block alongside a default appearance/color. A named PBR default also requires opacity exactly one, even when the command omits pbr; invalid combinations refuse before creating or editing the material. A density-only edit retains the entire existing look. Visual response changes do not modify density, geometry, placement or engineering axes. No grade-specific optical or mechanical values are inferred from a name or color.

Since format 21, pbr is saved inside existing body/face/component and material-default looks. Missing fields retain scalar shading. Format-20 render setups and old looks upgrade without reinterpretation; older readers refuse newly saved files instead of dropping their optical response. Undo/redo, copying and cold reopening use the existing design-owned values, with no external file dependency.

The portable catalog v2 preserves PBR looks and physical-material defaults, alongside validated engineering cards. Explicit scalar v1 export refuses any loss of those records before writing. V1 imports and v2 looks without PBR explicitly select scalar shading and clear a prior PBR response. See local material libraries. In-design swatches retain and reuse the response.

Original opaque finish presets

The existing Appearance library includes six additional opaque finishes. Its 20 scalar entries keep their names, colors, opacity and shading behavior. The new names are distinct, and the Appearance editor uses its existing categories, search, swatches and optical fields to select and edit them.

Appearance sRGB color Metallic Roughness
Steel - Satin PBR #9ea4ac 1 0.40
Aluminum - Satin PBR #ccd0d6 1 0.30
Copper - Clean PBR #cc7a50 1 0.25
ABS - Molded PBR #deded8 0 0.40
PLA - Smooth PBR #6ea0de 0 0.35
Wood - Oak Uniform PBR Preview #be925c 0 0.65

Each preset has opacity 1 and an illustrative dielectric IOR of 1.5. The values are original visual studies, not measured optical constants or engineering-grade material properties. Oak is a uniform preview without grain; PLA has no printed layers; satin metals use an isotropic response. A physical material's density and engineering axes remain independent of these visual choices.

The source values live in assets/appearances/opaque_finishes.rs. Assigning a preset saves its values inside the existing look, so a later library update does not change a saved assignment. This addition does not change the design format.

examples/material-finishes.json creates six original radius-10 mm spheres, assigns generic physical materials and applies these finishes through the same commands used by the editor:

solidcraft-cli run examples/material-finishes.json --save target/material-finishes.solidcraft
solidcraft-cli render target/material-finishes.solidcraft --out target/material-finishes.png --width 1600 --height 1000

Actual consumers and color

The installed integrated gpu-viewport and cpu-product image hosts advertise the pbr feature. Requests require it when the captured product scene contains PBR. The static legacy cpu-raster descriptor retains its previous capabilities. No host gains HDRI, refraction, progressive/final rendering or EXR support.

Look colors are decoded from sRGB8 to linear sRGB once in the shared document/scene adapter. PBR GPU vertices carry linear reflectance and optical parameters. The CPU rasterizer interpolates world position and normals with perspective correction; the GPU uses the corresponding fragment interpolation. Both use the original opaque BRDF and three fixed neutral directional lights in Z-up world coordinates. They sum BRDF × relative incident radiance × light cosine, apply per-channel Reinhard preview tone mapping, then encode exactly once. Linear and sRGB GPU attachments have distinct output transfer paths.

This fixed studio has no cast shadows, environment illumination, indirect bounces, emission, anisotropy, coating, absorption or transmission. Lighting magnitudes are relative preview values, not calibrated lux or spectral data. The single-scattering model can darken rough metals. Transparent legacy looks retain approximate sorted alpha blending, including when seen in front of an opaque PBR surface; that is not glass optics. Nondefault exposure/environment controls remain explicitly unsupported.

Scene uploads keep the 128 MiB bound, accounting for the larger PBR vertices. CPU face-map admission accounts for retained optical descriptors. Existing immutable capture, ownership, cancellation, stale-result rejection and PNG export policies remain in effect. Invalid/nonfinite geometry or zero input normals refuse.

Validation and original example

examples/render-opaque-studio.json builds four original spheres and a base, varying metallic fraction and roughness independently while keeping base color. Its values are an illustrative optical study, not engineering data for real grades.

The product offscreen test uses the actual installed callback/pipelines on Vulkan, including both linear and sRGB attachments. For four metal/roughness combinations, each comparison checks 12,454 interior pixels against CPU PNG output; observed mean channel error is below 0.2/255, with no pixels beyond the 12/255 outlier threshold. Silhouettes are excluded because CPU images use 2x supersampling and that test's GPU target uses 1x. This is a scoped parity result, not browser or universal GPU certification.

Command tests check unchanged geometry/mass, atomic refusal, undo/redo, cold reopen, physical default/face precedence, retained scalar format-20 looks upgraded to the current format and explicit v1 catalog refusal. Preset regressions also cover named PBR inheritance, pbr: null, custom looks with matching names, material create/edit refusal without changing history, density-only edits and all six scene responses. A clicked editor regression checks one transaction and reopening the same selected body. Native media and exact current-head gate results belong in the PR. wasm compilation and actual browser interaction are separate evidence.