Skip to content

Commit 080a520

Browse files
fix: restage png-exr-alpha hero into dark studio house style
White-clamped evidence stays; staging now uses framed easels on a dark table with dim studio lights so the card is no longer the gallery odd one out. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ebee175 commit 080a520

6 files changed

Lines changed: 168 additions & 54 deletions

File tree

-2.69 KB
Loading
178 KB
Loading
16.2 KB
Loading

docs/gallery/png-exr-alpha/index.html

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -552,8 +552,12 @@ <h2>Source</h2>
552552
Columns = rising alpha; top rows = dark mid-tones that PNG clamps to
553553
white; lower rows = primaries that survive. Left panel bakes the
554554
closed-form mangling; right panel shows authored straight RGBA.
555+
556+
A dark bezel is painted into the texture so panel edges read as framed
557+
objects in the studio still (render-only — checks use fill_pattern).
555558
&quot;&quot;&quot;</span>
556559
gw, gh = <span class="n">256</span>, <span class="n">192</span>
560+
border = <span class="n">10</span>
557561
img = bpy.data.images.new(name, gw, gh, alpha=<span class="k">True</span>, float_buffer=<span class="k">True</span>)
558562
img.colorspace_settings.name = <span class="s">&quot;Non-Color&quot;</span>
559563
px = [<span class="n">0.0</span>] * (gw * gh * <span class="n">4</span>)
@@ -567,25 +571,32 @@ <h2>Source</h2>
567571
(<span class="n">0.2</span>, <span class="n">0.35</span>, <span class="n">1.0</span>), <span class="c"># blue-leaning; G/B still clamp at low a → wash</span>
568572
]
569573
alphas = [<span class="n">1</span> / <span class="n">255</span>, <span class="n">8</span> / <span class="n">255</span>, <span class="n">32</span> / <span class="n">255</span>, <span class="n">64</span> / <span class="n">255</span>, <span class="n">128</span> / <span class="n">255</span>, <span class="n">1.0</span>]
570-
col_w = gw // n_cols
571-
row_h = gh // len(row_colors)
574+
iw, ih = gw - <span class="n">2</span> * border, gh - <span class="n">2</span> * border
575+
col_w = max(<span class="n">1</span>, iw // n_cols)
576+
row_h = max(<span class="n">1</span>, ih // len(row_colors))
577+
bezel = (<span class="n">0.04</span>, <span class="n">0.042</span>, <span class="n">0.048</span>)
572578
<span class="k">for</span> y <span class="k">in</span> range(gh):
573-
ri = min(y // row_h, len(row_colors) - <span class="n">1</span>)
574-
rgb = row_colors[ri]
575579
<span class="k">for</span> x <span class="k">in</span> range(gw):
576-
ci = min(x // col_w, n_cols - <span class="n">1</span>)
580+
i = (y * gw + x) * <span class="n">4</span>
581+
<span class="k">if</span> x &lt; border <span class="k">or</span> y &lt; border <span class="k">or</span> x &gt;= gw - border <span class="k">or</span> y &gt;= gh - border:
582+
px[i : i + <span class="n">4</span>] = [bezel[<span class="n">0</span>], bezel[<span class="n">1</span>], bezel[<span class="n">2</span>], <span class="n">1.0</span>]
583+
<span class="k">continue</span>
584+
xi, yi = x - border, y - border
585+
ri = min(yi // row_h, len(row_colors) - <span class="n">1</span>)
586+
ci = min(xi // col_w, n_cols - <span class="n">1</span>)
587+
rgb = row_colors[ri]
577588
a = alphas[ci]
578589
<span class="k">if</span> mangled:
579590
r, g, b, _a = expected_float_png_rgba(*rgb, a)
580591
<span class="k">else</span>:
581592
r, g, b = rgb
582-
i = (y * gw + x) * <span class="n">4</span>
583593
px[i : i + <span class="n">4</span>] = [r, g, b, <span class="n">1.0</span>]
584594
img.pixels.foreach_set(px)
585595
<span class="k">return</span> img
586596

587597

588598
<span class="k">def</span> easel_plane(name, image, loc, rot_z_deg):
599+
<span class="s">&quot;&quot;&quot;Single-plane easel + short stand — no dual-surface z-fight.&quot;&quot;&quot;</span>
589600
me = bpy.data.meshes.new(name)
590601
bm = bmesh.new()
591602
<span class="k">try</span>:
@@ -610,19 +621,46 @@ <h2>Source</h2>
610621
tex = nodes.new(<span class="s">&quot;ShaderNodeTexImage&quot;</span>)
611622
tex.image = image
612623
tex.interpolation = <span class="s">&quot;Closest&quot;</span>
624+
links.new(tex.outputs[<span class="s">&quot;Color&quot;</span>], bsdf.inputs[<span class="s">&quot;Base Color&quot;</span>])
613625
links.new(tex.outputs[<span class="s">&quot;Color&quot;</span>], bsdf.inputs[<span class="s">&quot;Emission Color&quot;</span>])
614-
bsdf.inputs[<span class="s">&quot;Base Color&quot;</span>].default_value = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">1.0</span>)
615626
bsdf.inputs[<span class="s">&quot;Roughness&quot;</span>].default_value = <span class="n">1.0</span>
616627
specular_off(bsdf)
617-
bsdf.inputs[<span class="s">&quot;Emission Strength&quot;</span>].default_value = <span class="n">0.95</span>
628+
<span class="c"># Faint emission so clamps read under a dim stage without washing the wall.</span>
629+
bsdf.inputs[<span class="s">&quot;Emission Strength&quot;</span>].default_value = <span class="n">0.22</span>
618630
me.materials.append(mat)
619631

620-
obj = bpy.data.objects.new(name, me)
621-
obj.location = loc
622-
obj.rotation_euler = (math.radians(<span class="n">62</span>), <span class="n">0.0</span>, math.radians(rot_z_deg))
623-
obj.scale = (<span class="n">1.15</span>, <span class="n">1.15</span>, <span class="n">1.15</span>)
624-
bpy.context.collection.objects.link(obj)
625-
<span class="k">return</span> obj
632+
root = bpy.data.objects.new(name, <span class="k">None</span>)
633+
root.location = loc
634+
root.rotation_euler = (math.radians(<span class="n">62</span>), <span class="n">0.0</span>, math.radians(rot_z_deg))
635+
root.scale = (<span class="n">0.88</span>, <span class="n">0.88</span>, <span class="n">0.88</span>)
636+
bpy.context.collection.objects.link(root)
637+
638+
face = bpy.data.objects.new(name + <span class="s">&quot;Face&quot;</span>, me)
639+
bpy.context.collection.objects.link(face)
640+
face.parent = root
641+
642+
stand_me = bpy.data.meshes.new(name + <span class="s">&quot;Stand&quot;</span>)
643+
bm = bmesh.new()
644+
<span class="k">try</span>:
645+
bmesh.ops.create_cube(bm, size=<span class="n">1.0</span>)
646+
bm.to_mesh(stand_me)
647+
<span class="k">finally</span>:
648+
bm.free()
649+
smat = bpy.data.materials.new(name + <span class="s">&quot;StandMat&quot;</span>)
650+
smat.use_nodes = <span class="k">True</span>
651+
sb = smat.node_tree.nodes[<span class="s">&quot;Principled BSDF&quot;</span>]
652+
sb.inputs[<span class="s">&quot;Base Color&quot;</span>].default_value = (<span class="n">0.045</span>, <span class="n">0.048</span>, <span class="n">0.055</span>, <span class="n">1.0</span>)
653+
sb.inputs[<span class="s">&quot;Roughness&quot;</span>].default_value = <span class="n">0.45</span>
654+
<span class="k">if</span> <span class="s">&quot;Metallic&quot;</span> <span class="k">in</span> sb.inputs:
655+
sb.inputs[<span class="s">&quot;Metallic&quot;</span>].default_value = <span class="n">0.3</span>
656+
stand_me.materials.append(smat)
657+
stand = bpy.data.objects.new(name + <span class="s">&quot;Stand&quot;</span>, stand_me)
658+
<span class="c"># Fully below the card face — never intersects the evidence plane.</span>
659+
stand.scale = (<span class="n">0.06</span>, <span class="n">0.06</span>, <span class="n">0.28</span>)
660+
stand.location = (<span class="n">0.0</span>, <span class="n">0.04</span>, -<span class="n">0.88</span>)
661+
bpy.context.collection.objects.link(stand)
662+
stand.parent = root
663+
<span class="k">return</span> root
626664

627665

628666
<span class="k">def</span> render_still(path, engine):
@@ -631,8 +669,27 @@ <h2>Source</h2>
631669
png_card = make_gallery_card(<span class="s">&quot;PngMangled&quot;</span>, mangled=<span class="k">True</span>)
632670
exr_card = make_gallery_card(<span class="s">&quot;ExrClean&quot;</span>, mangled=<span class="k">False</span>)
633671

634-
easel_plane(<span class="s">&quot;PngPanel&quot;</span>, png_card, (-<span class="n">1.22</span>, <span class="n">0.0</span>, <span class="n">1.18</span>), <span class="n">10</span>)
635-
easel_plane(<span class="s">&quot;ExrPanel&quot;</span>, exr_card, (<span class="n">1.22</span>, <span class="n">0.0</span>, <span class="n">1.18</span>), -<span class="n">10</span>)
672+
<span class="c"># Mounted easels on a shared dark table — continuous base, no V-tear shadow.</span>
673+
easel_plane(<span class="s">&quot;PngPanel&quot;</span>, png_card, (-<span class="n">1.15</span>, <span class="n">0.1</span>, <span class="n">1.28</span>), <span class="n">5</span>)
674+
easel_plane(<span class="s">&quot;ExrPanel&quot;</span>, exr_card, (<span class="n">1.15</span>, <span class="n">0.1</span>, <span class="n">1.28</span>), -<span class="n">5</span>)
675+
676+
table_me = bpy.data.meshes.new(<span class="s">&quot;Table&quot;</span>)
677+
bm = bmesh.new()
678+
<span class="k">try</span>:
679+
bmesh.ops.create_cube(bm, size=<span class="n">1.0</span>)
680+
bm.to_mesh(table_me)
681+
<span class="k">finally</span>:
682+
bm.free()
683+
tmat = bpy.data.materials.new(<span class="s">&quot;TableMat&quot;</span>)
684+
tmat.use_nodes = <span class="k">True</span>
685+
tb = tmat.node_tree.nodes[<span class="s">&quot;Principled BSDF&quot;</span>]
686+
tb.inputs[<span class="s">&quot;Base Color&quot;</span>].default_value = (<span class="n">0.022</span>, <span class="n">0.024</span>, <span class="n">0.028</span>, <span class="n">1.0</span>)
687+
tb.inputs[<span class="s">&quot;Roughness&quot;</span>].default_value = <span class="n">0.6</span>
688+
table_me.materials.append(tmat)
689+
table = bpy.data.objects.new(<span class="s">&quot;Table&quot;</span>, table_me)
690+
table.scale = (<span class="n">1.5</span>, <span class="n">0.55</span>, <span class="n">0.08</span>)
691+
table.location = (<span class="n">0.0</span>, <span class="n">0.25</span>, <span class="n">0.28</span>)
692+
scene.collection.objects.link(table)
636693

637694
floor_me = bpy.data.meshes.new(<span class="s">&quot;Floor&quot;</span>)
638695
bm = bmesh.new()
@@ -644,20 +701,20 @@ <h2>Source</h2>
644701
fmat = bpy.data.materials.new(<span class="s">&quot;Studio&quot;</span>)
645702
fmat.use_nodes = <span class="k">True</span>
646703
fb = fmat.node_tree.nodes[<span class="s">&quot;Principled BSDF&quot;</span>]
647-
fb.inputs[<span class="s">&quot;Base Color&quot;</span>].default_value = (<span class="n">0.03</span>, <span class="n">0.032</span>, <span class="n">0.037</span>, <span class="n">1.0</span>)
648-
fb.inputs[<span class="s">&quot;Roughness&quot;</span>].default_value = <span class="n">0.7</span>
704+
fb.inputs[<span class="s">&quot;Base Color&quot;</span>].default_value = (<span class="n">0.02</span>, <span class="n">0.021</span>, <span class="n">0.025</span>, <span class="n">1.0</span>)
705+
fb.inputs[<span class="s">&quot;Roughness&quot;</span>].default_value = <span class="n">0.8</span>
649706
floor_me.materials.append(fmat)
650707
floor = bpy.data.objects.new(<span class="s">&quot;Floor&quot;</span>, floor_me)
651708
scene.collection.objects.link(floor)
652709
wall = bpy.data.objects.new(<span class="s">&quot;Wall&quot;</span>, floor_me.copy())
653-
wall.location = (<span class="n">0.0</span>, <span class="n">6.8</span>, <span class="n">0.0</span>)
710+
wall.location = (<span class="n">0.0</span>, <span class="n">8.8</span>, <span class="n">0.0</span>)
654711
wall.rotation_euler = (math.radians(<span class="n">90</span>), <span class="n">0.0</span>, <span class="n">0.0</span>)
655712
scene.collection.objects.link(wall)
656713

657714
world = bpy.data.worlds.new(<span class="s">&quot;World&quot;</span>)
658715
world.use_nodes = <span class="k">True</span>
659716
world.node_tree.nodes[<span class="s">&quot;Background&quot;</span>].inputs[<span class="s">&quot;Color&quot;</span>].default_value = (
660-
<span class="n">0.02</span>, <span class="n">0.021</span>, <span class="n">0.025</span>, <span class="n">1.0</span>,
717+
<span class="n">0.012</span>, <span class="n">0.013</span>, <span class="n">0.015</span>, <span class="n">1.0</span>,
661718
)
662719
scene.world = world
663720

@@ -671,19 +728,19 @@ <h2>Source</h2>
671728
ob.rotation_euler = tuple(math.radians(a) <span class="k">for</span> a <span class="k">in</span> rot)
672729
scene.collection.objects.link(ob)
673730

674-
light(<span class="s">&quot;Key&quot;</span>, (-<span class="n">3.8</span>, -<span class="n">4.8</span>, <span class="n">5.8</span>), <span class="n">360.0</span>, <span class="n">4.5</span>, (<span class="n">1.0</span>, <span class="n">0.96</span>, <span class="n">0.9</span>), (<span class="n">52</span>, <span class="n">0</span>, -<span class="n">32</span>))
675-
light(<span class="s">&quot;Fill&quot;</span>, (<span class="n">4.8</span>, -<span class="n">2.8</span>, <span class="n">1.6</span>), <span class="n">70.0</span>, <span class="n">9.0</span>, (<span class="n">0.75</span>, <span class="n">0.85</span>, <span class="n">1.0</span>), (<span class="n">72</span>, <span class="n">0</span>, <span class="n">52</span>))
676-
light(<span class="s">&quot;Rim&quot;</span>, (<span class="n">0.2</span>, <span class="n">4.0</span>, <span class="n">2.8</span>), <span class="n">220.0</span>, <span class="n">3.2</span>, (<span class="n">0.6</span>, <span class="n">0.78</span>, <span class="n">1.0</span>), (-<span class="n">62</span>, <span class="n">0</span>, <span class="n">178</span>))
677-
light(<span class="s">&quot;Wedge&quot;</span>, (<span class="n">0.5</span>, <span class="n">5.2</span>, <span class="n">2.8</span>), <span class="n">520.0</span>, <span class="n">7.0</span>, (<span class="n">1.0</span>, <span class="n">0.72</span>, <span class="n">0.42</span>), (-<span class="n">78</span>, <span class="n">0</span>, <span class="n">180</span>))
731+
light(<span class="s">&quot;Key&quot;</span>, (-<span class="n">3.8</span>, -<span class="n">4.8</span>, <span class="n">5.8</span>), <span class="n">220.0</span>, <span class="n">4.5</span>, (<span class="n">1.0</span>, <span class="n">0.96</span>, <span class="n">0.9</span>), (<span class="n">52</span>, <span class="n">0</span>, -<span class="n">32</span>))
732+
light(<span class="s">&quot;Fill&quot;</span>, (<span class="n">4.8</span>, -<span class="n">2.8</span>, <span class="n">1.6</span>), <span class="n">40.0</span>, <span class="n">9.0</span>, (<span class="n">0.75</span>, <span class="n">0.85</span>, <span class="n">1.0</span>), (<span class="n">72</span>, <span class="n">0</span>, <span class="n">52</span>))
733+
light(<span class="s">&quot;Rim&quot;</span>, (<span class="n">0.2</span>, <span class="n">4.8</span>, <span class="n">2.8</span>), <span class="n">120.0</span>, <span class="n">3.2</span>, (<span class="n">0.6</span>, <span class="n">0.78</span>, <span class="n">1.0</span>), (-<span class="n">62</span>, <span class="n">0</span>, <span class="n">178</span>))
734+
light(<span class="s">&quot;Wedge&quot;</span>, (<span class="n">0.5</span>, <span class="n">6.5</span>, <span class="n">2.5</span>), <span class="n">450.0</span>, <span class="n">7.0</span>, (<span class="n">1.0</span>, <span class="n">0.72</span>, <span class="n">0.42</span>), (-<span class="n">78</span>, <span class="n">0</span>, <span class="n">180</span>))
678735

679736
aim = bpy.data.objects.new(<span class="s">&quot;Aim&quot;</span>, <span class="k">None</span>)
680-
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">1.1</span>)
737+
aim.location = (<span class="n">0.0</span>, <span class="n">0.1</span>, <span class="n">1.15</span>)
681738
scene.collection.objects.link(aim)
682739

683740
cam_data = bpy.data.cameras.new(<span class="s">&quot;Cam&quot;</span>)
684-
cam_data.lens = <span class="n">45.0</span>
741+
cam_data.lens = <span class="n">50.0</span>
685742
cam = bpy.data.objects.new(<span class="s">&quot;Cam&quot;</span>, cam_data)
686-
cam.location = (<span class="n">0.0</span>, -<span class="n">5.4</span>, <span class="n">2.2</span>)
743+
cam.location = (<span class="n">0.0</span>, -<span class="n">6.0</span>, <span class="n">2.15</span>)
687744
con = cam.constraints.new(<span class="s">&quot;TRACK_TO&quot;</span>)
688745
con.target = aim
689746
con.track_axis = <span class="s">&quot;TRACK_NEGATIVE_Z&quot;</span>

0 commit comments

Comments
 (0)