Granite: rectangular (lath) crystals and refactor check

Branch jiadeng/tbricks-semantic, commits 15809be33 (refactor) and 47cd161de (lath crystals). Renders: Cycles, 32 samples, head-on camera, 1024 px unless noted. Click any image for full size.

Very dark lath granite preset (2026-10-12, commit 918b05aeb)

dark lath preset
First candidate (dark1) | dark4 | dark5 = granite_dark_lath_preset | red lath preset for comparison. Mean display lightness in parentheses. Bottom: 2x zoom.

granite_lath_rand finished (2026-10-12, commit 7189fd739)

lath generator seeds
granite_lath_rand seeds 1-8 (full and 2x zoom).
families before
Each palette family at the weakest pale setting, before (white and cream barely separate).
families after
After.

Full sweep sheets (each row: min full | min 2x zoom | max full | max 2x zoom): geometry axes, color axes.

Red lath preset tuning, round 2: matrix, flecks, white patches (2026-10-12)

Pink fixed at the preset's. Variants in lath_tune_materials.py; the preset itself is unchanged.

round 2 x variants
Photo | current preset | x1 coarser, lighter, warmer matrix | x2 + larger, softer, dark-gray flecks | x3 + granular warm light grains, fewer white rectangles | x4 granular light grains only, no white rectangles.
round 2 y variants
Photo | current preset | x2 | y1: x2 with low matrix color jitter | y2: y1 + moderate granular light grains.
share of pixels by sRGB lightness band (median color in parentheses where useful)
              L<0.2    0.2-0.35   0.35-0.55   0.55-0.75   >0.75
photo crop     4.4%     12.3%      26.0%       46.9%       10.4% (.87,.81,.76)
photo tile     9.7%     14.4%      26.9%       39.8%        9.3%
preset        12.2%      7.9%      23.7%       48.9%        7.2% (.79,.78,.76)
x2             9.7%      5.2%      21.8%       55.7%        7.6%
x3             6.3%      3.9%      18.9%       60.1%       10.9%
x4             5.3%      3.3%      17.5%       60.9%       12.9%
y1             5.4%      7.7%      31.0%       48.6%        7.3%
y2             4.7%      6.8%      28.6%       51.6%        8.2%

Pink as one continuous range, not two colors (2026-10-08, commit 4c130e880)

pink range
Photo | r1: two pinks (previous commit) | u1: one pink with a wide tail (shows khaki crystals) | w1: one pink with a narrow continuous range (now the preset). Rows: full, 2x zoom, 4x zoom.

r1 read as two distinct pinks. One sample per crystal shows why: the symmetric hue pair made a separate redder group, and the 15% of crystals pulled to gray made a separate dull group. The photo has one peak with a gradual range. Now each crystal mixes a main pink with a slightly more orange, duller pink by a skewed random weight (most crystals near the main pink), and the gray group is off.

per-crystal hue, % of crystals per 0.005 bin from 0.055 to 0.090:
  photo   1   8  23  27  10   7   2
  r1     15  13  27  22   5   0   0     (plus 17% at 0.045-0.055: the redder group)
  w1      0   0  42  48  10   0   0
pink interiors     median sRGB            saturation 10/50/90   hue 10/50/90
photo              (0.729, 0.604, 0.518)  0.20 / 0.29 / 0.37    0.059 / 0.067 / 0.080
w1 = preset        (0.729, 0.616, 0.529)  0.20 / 0.27 / 0.38    0.068 / 0.072 / 0.078

Remaining pink differences: hue slightly warmer and a bit narrower than the photo; the brightest crystals reach value 0.80 vs 0.84.

Pink crystals matched to the photo (2026-10-08, commit 102f2e0f5)

pink matching
Photo | previous preset | q1: median color matched | r1: + per-crystal hue spread and wider brightness (now the preset). Rows: full, 2x zoom, 4x zoom.

Measured over the interiors of pink crystals (pixels whose whole 5x5 neighborhood is pink) across the whole red tile:

                 median sRGB            hue 10/50/90 pct      saturation median   value 10/50/90 pct
photo            (0.729, 0.604, 0.518)  0.059 / 0.067 / 0.080   0.29                0.62 / 0.73 / 0.84
previous preset  (0.714, 0.604, 0.537)  0.062 / 0.064 / 0.068   0.25                0.62 / 0.71 / 0.78
q1               (0.729, 0.604, 0.518)  0.067 / 0.070 / 0.072   0.30                0.64 / 0.73 / 0.80
r1 = new preset  (0.725, 0.600, 0.514)  0.056 / 0.069 / 0.077   0.29                0.62 / 0.73 / 0.80

Red lath preset tuning, round 1 (2026-10-08)

tuning round 1
Photo | current preset | v1 matrix lighter and warmer | v2 + warm light-gray white crystals | v3 + larger, fewer, less black flecks | v4 + granular warm light-gray grains in the matrix. Each variant adds one change to the previous one. Bottom row: 2x zoom.

Share of pixels by sRGB lightness band, with each band's median color:

           L<0.2               | 0.2-0.35            | 0.35-0.55           | 0.55-0.75           | >0.75
photo      4.4% (.16,.15,.13)  | 12.3% (.31,.29,.26) | 26.0% (.51,.47,.42) | 46.9% (.73,.63,.56) | 10.4% (.87,.81,.76)
preset    12.2% (.11,.11,.10)  |  7.8% (.30,.28,.25) | 21.4% (.53,.47,.42) | 51.8% (.71,.64,.58) |  6.7% (.79,.78,.76)
v1         8.0% (.12,.11,.10)  |  6.7% (.32,.28,.24) | 23.7% (.55,.48,.42) | 54.9% (.71,.63,.58) |  6.7% (.79,.78,.76)
v2         8.0%                |  6.7%               | 23.6%               | 53.9%               |  7.8% (.80,.78,.76)
v3         7.4%                |  6.5%               | 23.5%               | 54.6%               |  8.0% (.80,.78,.76)
v4         6.5% (.13,.12,.10)  |  5.9% (.32,.28,.24) | 22.5% (.55,.48,.43) | 59.3% (.71,.63,.58) |  5.8% (.80,.78,.76)

What changed and what didn't:

Generators: before vs after this session's changes (seeds 1-4)

Head-on camera, 1024 px, 32 samples, rendered on ionic. "Before" is commit e3d58a1 (before the refactor) for salt-and-pepper and porphyritic, and 47cd161de (first lath version, aligned crystals) for the lath generator. "After" is f811eb0ae (current).

granite_salt_pepper_rand and granite_porphyritic_rand are unchanged: every seed matches within 1/255 (max difference 1, no pixel above 2/255).

salt and pepper before after
granite_salt_pepper_rand: before | after | difference x8 (all black = identical)
porphyritic before after
granite_porphyritic_rand: before | after | difference x8

granite_lath_rand changed on purpose: crystals are now randomly oriented, and dropping the angle draws shifted the random stream, so the same seed gets a different palette (e.g. seed 3 went from cream to red).

lath generator before after
granite_lath_rand: before (aligned) | after (random orientation) | difference x8

0. Update: crystals randomly oriented (no dominant direction)

In the prototype every crystal pointed within ±0.35 rad of three fixed layer angles (0.4-1.3 rad), and each layer's Voronoi frame was stretched along its angle, so all crystals leaned the same way. Now elongation is applied in each crystal's own frame and each crystal gets its own uniformly random rotation. This is the default, and the preset and generator use it. Crystal size and coverage are unchanged.

orientation before after
Photo | preset before (aligned, as r25_b) | preset after (random orientation). Bottom: 2x zoom.
generator seeds random orientation
Generator seeds 1-4 with random orientation. Dropping the angle draws shifted the random stream, so these seeds have different palettes from section 2 (dark gray, white, red, white). Still open: when both crystal families are light (seeds 2 and 4), the rectangles blend together.

Sections 1-2 below show the earlier aligned version.

1. Red lath tile: photo vs prototype r25_b vs ported preset

photo, prototype, preset
Columns: photo crop | prototype r25_b | granite_red_lath_preset. Top: full view; bottom: 2x zoom.

The preset reproduces r25_b. Its color under the prototype's flat surface matches within 1/255, after two deliberate changes: white crystals take brightness from the same random channel as the pink ones, and the preset uses the honed layered surface (about 1.7/255 darker on average).

Still different from the photo (not tuned yet):

photo crop
Photo crop (350,650)-(950,1250)
prototype r25_b
Prototype r25_b
red lath preset
granite_red_lath_preset (416 nodes)

2. Generator: granite_lath_rand, seeds 1-4

generator seeds
Photo, then seeds 1-4 (gold 16.5 mm, gray 10.9 mm, cream 10.5 mm, gold 12.5 mm). Top: full; bottom: 2x zoom.

Pink and red together are 18% of the shared palette, so none of these four draws is pink. In seeds 2 and 3 the main and pale crystals have similar brightness, so the rectangles blur together. Every seed shares the single-direction alignment. The ranges are centered on the preset, not calibrated by sweeps.

seed 1
Seed 1
seed 2
Seed 2
seed 3
Seed 3
seed 4
Seed 4

3. Refactor check: 16 materials before vs after

refactor grid
Each material: before | after | difference x8. Default demo camera, 512 px.

Realized nodes went from 287-288 to 136-253 per material. The largest per-pixel difference is 12/255, never more than 0.07% of pixels differ by more than 2/255, and every pixel off by more than 4/255 is isolated.

Reference photo

full reference photo
scripts/granite_dev/reference_red_lath_tile.jpg. The red tile is in the middle; the gray tile around it is open item 3.