Skip to content

V3: 3D projections, metalness, rim light, wireframe, real glass and new-layer labels - #200

Merged
git-chad merged 159 commits into
git-chad/shader-lab-v3-planfrom
git-chad/v3-model-and-labels
Oct 1, 2026
Merged

git-chad merged 159 commits into
git-chad/shader-lab-v3-planfrom
git-chad/v3-model-and-labels

Conversation

@git-chad

@git-chad git-chad commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #199.

3D layer

  • Projection (Camera): Perspective, Orthographic, Isometric (35.26°) and Dimetric (30°). Isometric and Dimetric lock the angle; Orbit turns them in 90° steps. Parallel projections use a 2400mm lens (under 1% perspective), so the gizmo, depth map and shadows work unchanged.
  • Metalness (Material) for replacement materials; picking a preset sets its natural value.
  • Rim light (Light) behind the model, off by default, with its own color.
  • Wireframe (Material).
  • Glass rebuilt: transmission refracted the empty model canvas, so it looked hollow. It now refracts the layers below through backdropNode, with a color split, roughness frost, clearcoat and fresnel.
  • Material switches redraw once the shader compiles, instead of on the next edit.
  • Saved projects render as before: new controls default off, and metalness is migrated from the preset on load.

Editor

  • The add-layer picker lists new and reworked V3 layers first, with a "new" label; new sources get an orange dot.
  • Model panel: help text moved into an info icon, the studio caption removed, and Swap and Environment on single rows.

Checks

  • Typecheck and lint pass.
  • tests/composition/model-layer-ui.mjs passes.
  • The projections, metalness, rim light, wireframe and glass were checked visually on WebGPU in Chrome Canary with a real GLB.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
shader-lab Ready Ready Preview Oct 1, 2026 3:13pm UTC

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Adds 3D model rendering features and UI labels.

The PR appears safe to merge, with keyboard access to model help and avoidable glass rendering work worth improving.

Summary

The 3D model layer adds projection, material, lighting, wireframe, and glass controls. The editor also moves new V3 layers to the front of the picker and marks them, while tightening the model panel layout.

  • Adds Perspective, Orthographic, Isometric, and Dimetric camera views.
  • Adds metalness, wireframe, rim lighting, and glass that refracts layers below the model.
  • Highlights new and reworked layers in the picker and condenses model controls.

Reviews (1) · Last reviewed commit: "Add 3D camera projections, metalness, ri..."

Comment thread src/components/editor/model-controls.tsx Outdated
Comment thread src/renderer/model-materials.ts
…roup failures

Media load callbacks now ignore replaced or removed passes and pending loads are cleared on disposal. Strict-mode pass failures are reported once at the originating pass instead of at every group boundary.
Publishing now drops every descendant of a hidden group and clears the
parentId of any retained layer (the audio source) whose parent was removed,
so the published scene passes hierarchy validation on reopen.
Cap the neighbor row and column search to two cells each side so the
worst-case cellAt count per fragment is 25 instead of unbounded in the
packed minimum-size, maximum-stroke configuration. Mirrored in the
runtime package.
…e on buttons

Clear the active paint layer when the layer stops being paintable (selection,
mode, visibility or lock change) so Paint never resumes without Edit Paint.
Only preventDefault on Space for non-interactive targets so focused buttons
and links still activate.
…opic edges

- Keep SVG interior opacity through the distance field and color texture
- Preserve alpha when extracting and replacing SVG file colors
- Skip the implicit-fill swatch when a parent group sets fill
- Retry a failed SVG load on the next sync
- Keep the export wait entry until the current SVG request finishes
- Scale sampled SVG distance per axis for stretched shapes
The ramp was a JSON text param, so it could not be animated. It is now one
timeline property, Ramp, with a new "gradient" value type: the diamond next
to "Dark to light" keys the whole ramp, Auto-Key edits at the playhead create
or update keyframes, and without Auto-Key an edit only previews until keyed,
like every other animated param.

Between two keyframes the ramps are aligned stop by stop. With equal counts
the sorted stops pair up and glide in position and color. With different
counts the shorter ramp gains stops at the positions of the unmatched ones
(a monotone matching that minimises position travel), colored by sampling
the shorter ramp itself, so it looks unchanged and nothing pops at either
keyframe; nested presets crossfade exactly. Colors blend in sRGB like the
ramp and color tracks already do, per-keyframe easing applies, and the
alignment is cached per keyframe pair. The same code runs in the editor and
the runtime timeline, so preview, PNG/video export and shader export agree.

The pass rewrites its LUT in place and only when the evaluated ramp string
changes, so static scenes and projects without ramp tracks behave as before.
- Interpolate inserted ramp stops between the adjacent matched stops so overlapping stops keep the correct side of the color change (app and runtime copy)
- Pass onInteractionStart/End through GradientRamp to its ColorPicker so a color drag is one interaction
The roadmap left three Annotations items open: connected dots (reference 21),
placement guided by image regions, and rotating individual elements.

Placement → Regions reuses the 128×72 field Edges already renders and reads
back only when the image changes. A second edge material writes the Sobel
magnitude unchanged plus averaged tone and two chroma axes; the CPU groups
cells into regions with warm-started k-means, a majority filter and connected
components, drops thin slivers, and keeps region ids across frames by overlap,
so marks glide with video instead of blinking. Boxes frame compact regions,
dots, crosses and labels sit on outlines picked by seeded anchors, rings sit
on centers, and every per-region mark uses its own seeded stream so regions
appearing or disappearing do not reshuffle the others.

Connected Dots draws small constellations: a minimum spanning tree over a few
seeded nodes plus occasional second-neighbour links, respecting every
placement and drawn last so the element budget drops it first. Rotation
Jitter turns rings, crosses and boxes by a hashed angle, and Align to Edges
turns them along edge tangents or each region's long axis; text, rulers and
metadata stay upright.

Every new control starts off and draws from separate random streams, so saved
projects keep their exact layout; a golden digest of legacy layouts computed
from the previous code guards that. Plain Edges keeps its original material
and field.
…sts in the harness

The Annotations region study seeks through aura.mp4 72 times. Chrome aborts in-flight media range requests on every seek, which the harness counted as a failed request. Only media requests aborted by the element itself are ignored now; every other failed request still fails the run.
… edge angles, spread sparse marks

- Annotations pass redraws its output once the edge/region readback completes so still texture sources no longer keep seeded marks
- Edge tangents are converted from the 128x72 grid to the composition aspect before rotating marks
- eachRegion spreads limited marks evenly across regions instead of filling the first IDs
- Mirrored into packages/shader-lab-react
Lumen Print's grain is static and tied to the print look; the user asked for grain as its own layer so photos and video can get real film grain that moves. Grain is one cheap pass: three rotated lattices of a small shared noise texture (bilinear with smoothstep weights) give isotropic, clumpy grain skewed toward dark silver specks instead of symmetric TV static, sized in document pixels so every export resolution matches. Chroma adds per-layer color grain with zero luma, so it never changes brightness. Response weights the midtones like film, and Soft Light and Overlay scale the pixel uniformly so grain keeps its hue, while Add behaves like sensor noise. Speed steps to a new pattern 24 times a second at 1 instead of sliding, derived only from the clock time so preview and export agree; at Speed 0 the layer is static and joins the reusable static prefix. Five styles follow references 31, 22, 06/12 and 07 plus a digital sensor look.
The Grain check renders three illustrative video frames through a video layer. SwiftShader can't import decoded video frames, so those renders are black there and their paired validation errors don't always balance. They are only artifacts, so they now render on hardware adapters only.
- GrainPass advances its frame from the timeline time so preview and export agree
- Scale the chroma offset so no channel clips below zero and shifts brightness
- Render 30 timing frames per style instead of 150 in the grain composition test
The roadmap left snapping the dots to the image's edges open, and the
user asked for it: reference 21 reads as a portrait because its dots
and links follow the jaw, eyes and hair. Edge Snap moves each cell's
site toward the strongest edge inside its cell, so contours come out
as rows of dots and links, and Mesh triangles line up with silhouettes.

A small parallel pass scores a 4x4 set of candidates per cell (luma and
alpha gradient plus a dark-ridge term, so thin dark lines pull points
onto the line instead of its flanks, above a noise floor so grain does
not move sites). The site pre-pass then pulls the jittered site toward
the proximity-weighted centroid of the strong candidates, scaled by how
localized the edge is, so smooth gradients do not collapse the jitter
into a grid. The main shader is untouched for Graph, Blobs and Plexus.

Mesh now reads its points from the same pre-pass instead of recomputing
36 sites per pixel, and the mesh and dot shaders are built separately
so neither carries the other's registers. At Edge Snap 0 every existing
style renders pixel-identical to before (checked at 1080p and on two
other inputs); Mesh is about twice as fast and Edge Snap costs well
under 0.2 ms at 1080p. Contour Graph and Facets are new styles that use
it; existing styles keep it at 0.
Control descriptions moved from captions under each label to an info
icon that shows them on hover, and the explanations under each effect's
Style picker went into the same icon.

Controls in the new V3 layers now follow the order you work in: mode
first, then geometry, then output and finishing. Controls that do
nothing in the current mode are hidden, and a control whose parent
toggle is hidden now hides too, so Plotter's Cross Angle no longer
shows outside Hatch mode.

Annotations had two preset pickers; the one in Text now fills Words and
Metadata, and its palette and brush tools sit under Color and Placement.
Connected Dots' palette sits under Color. Paint brushes use a
full-width Edit button with a trash button beside it.
…ha edges

Disable edge snap when the edge target would exceed 8192 pixels, such as
Ultra exports at minimum spacing.
Score edges with alpha-weighted darkness so light cutouts over transparent
black snap to the opaque side.
The editor chrome was dense: hairline sliders, uppercase section labels
and 10-11px mono text in menus. The new skin makes the controls feel
physical: 30px rounded slider tracks with an inset handle, pill
toggles, raised dropdowns and buttons, sections as rounded cards inside
the glass panels, 24px panel corners, sentence-case headers and larger
sans type. Orange #FF4D00 only marks the slider being dragged and
keyboard focus.

A light theme ships next to dark, switched from the top bar and kept in
localStorage. Light flips the Tailwind white and black colors, so
hardcoded white/xx utilities re-ink without touching each component.

The skin lives in one unlayered stylesheet keyed on html[data-theme],
which only the editor route sets, so community pages keep their look.
Components only gain data-ds hooks.
The top bar, the timeline bar and the restore and draft notices were
fully rounded pills. Their radius is now the icon button radius plus
the bar padding, so the corners follow the buttons inside them; the
undo and zoom trays use the icon radius plus their own padding and
border. Both come from new tokens exposed as Tailwind classes:
rounded-toolbar, rounded-group, p-bar and p-bar-group.

Every size in the skin stylesheet is now a variable: type scale, radii,
control, track, handle and toggle sizes, and paddings. Buttons inside a
toolbar match the icon button height.
The community index, scene and profile pages were dark-only, with tiny
uppercase labels, bordered chips and flat grids. They now follow the
editor's skin and theme choice:

- a floating community toolbar (Shader Lab, Scenes, Effects, theme,
  Open Shader Lab) replaces the scattered back links
- the hero is a framed rounded canvas with the most-liked scene as a
  glass pill
- effect filters sit in a button tray, scene cards are raised cards
  with concentric thumbnails, and tags are pills
- the scene viewer, its details and the profile header are rounded
  cards, and profile stats are tiles

Text over scene imagery uses an on-media token scope so it stays light
in the light theme. The toolbar reads the path inside Suspense so the
partially prerendered routes stay cacheable. The effects pages get the
same cards and toolbar clearance.
Community: the Most liked banner and the profile header use large
cropped stat numbers; scene cards get bigger radii, grouped labels and
matched insets; the effect page is one banner with the preview beside
the copy. Effect copy is now just the catalog line; the template
sentence appended to every page is gone, and the catalog descriptions
are rewritten in plain language. Discontinued layers (Basic Blur,
Directional Blur, Fluted Glass, Progressive Blur) are no longer listed
and their effect pages redirect to Blur.

Thumbnails: new effect thumbnails under /examples/effects, compressed
to WebP (lossless for the pixel-pattern effects). The new path keeps
cached copies of the old images from being served. Bloom gets one.

Editor: a blank project in light mode has a background matching the
workspace and no artboard shadow; switching theme on an empty default
project swaps it, but only after autosave restore so it cannot skip
the session. The empty state has an Add layer button. Layer rows use a
raised selected state with concentric radii. The drag status box and
the drag-handle tooltips are gone.

3D gizmo: rings scale with the model, fade their far halves, thicken
on hover and hide the rest while dragging. Hit areas follow the
strokes, so the view ring no longer swallows clicks on the axis rings.
…color restore

- Mark the drop hint and boot loader as ds-on-media so they stay light on the dark canvas in the light theme
- Add the theme toggle to the mobile actions panel
- Restore the shared theme-color meta content when ThemeMount unmounts
…lass

The 3D layer gains a Projection control (perspective, orthographic,
isometric and dimetric), Metalness for replacement materials, a rim
light behind the model and a wireframe toggle. The parallel projections
use a long telephoto lens, so the gizmo, depth map and shadows keep
working unchanged. Projects saved before this keep their look: new
controls default off, and presets get their natural metalness on load.

Glass used transmission, which refracts the screen behind the object;
the model renders alone, so it refracted nothing and looked hollow. It
now refracts a copy of the layers below through backdropNode, with a
slight color split, roughness frost, clearcoat and fresnel. Material
switches now redraw once the shader compiles instead of waiting for
another edit. Wireframe skips the async precompile, which never
resolves for wireframe materials.

The add-layer picker sorts new and reworked layers first with a "new"
label, and new sources get an orange dot. The model panel moves its
help into an info icon, drops the studio caption and puts Swap and
Environment on single rows.
@git-chad
git-chad changed the base branch from git-chad/v3-light-theme to git-chad/shader-lab-v3-plan October 1, 2026 15:15
@git-chad
git-chad merged commit 06049c2 into git-chad/shader-lab-v3-plan Oct 1, 2026
5 checks passed

This branch was successfully deployed

1 active deployment
Preview — 06049c24 Deployed Oct 1, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant