diff --git a/README.md b/README.md index 344ac76..b2bb747 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ non-destructive path.
-Bmesh gear: a machined steel 14-tooth gear at a three-quarter angle on a dark studio floor +Bmesh gear: a machined brass 14-tooth gear with concentric turning marks catching the light, tilted at a three-quarter angle on a dark studio floor @@ -251,7 +251,7 @@ semver on LTS builds (`"4.5.11 LTS"`), and that a Mesh reference dies at
-Depsgraph-evaluated export: a matte graphite base cube beside its glossy green subdivision-surface evaluated form on a dark studio floor +Depsgraph-evaluated export: a teal base cube beside the smooth teal sphere of its subdivision-surface evaluated form - one shared material, two silhouettes - on a dark studio floor with a warm light pool behind diff --git a/docs/gallery/assets/bmesh-gear-hero.webp b/docs/gallery/assets/bmesh-gear-hero.webp index db4cf33..da96193 100644 Binary files a/docs/gallery/assets/bmesh-gear-hero.webp and b/docs/gallery/assets/bmesh-gear-hero.webp differ diff --git a/docs/gallery/assets/depsgraph-export-hero.webp b/docs/gallery/assets/depsgraph-export-hero.webp index ae7af27..5c61029 100644 Binary files a/docs/gallery/assets/depsgraph-export-hero.webp and b/docs/gallery/assets/depsgraph-export-hero.webp differ diff --git a/docs/gallery/assets/gn-sdf-remesh-hero.webp b/docs/gallery/assets/gn-sdf-remesh-hero.webp index 558cc03..056fbde 100644 Binary files a/docs/gallery/assets/gn-sdf-remesh-hero.webp and b/docs/gallery/assets/gn-sdf-remesh-hero.webp differ diff --git a/docs/gallery/bmesh-gear/index.html b/docs/gallery/bmesh-gear/index.html index 066d61e..25983f7 100644 --- a/docs/gallery/bmesh-gear/index.html +++ b/docs/gallery/bmesh-gear/index.html @@ -295,15 +295,31 @@

Source

scene = bpy.context.scene for poly in obj.data.polygons: poly.use_smooth = False # crisp machined facets - mat = bpy.data.materials.new("Steel") + # machined brass: a flat metal face lit by area lights renders as one + # featureless gradient, so the finish carries the design — concentric + # turning marks (a RINGS wave driving roughness) break the face into + # rings that each catch the key at a different angle + mat = bpy.data.materials.new("MachinedBrass") mat.use_nodes = True - bsdf = mat.node_tree.nodes["Principled BSDF"] - bsdf.inputs["Base Color"].default_value = (0.75, 0.77, 0.8, 1.0) + nt = mat.node_tree + bsdf = nt.nodes["Principled BSDF"] + bsdf.inputs["Base Color"].default_value = (0.72, 0.44, 0.16, 1.0) bsdf.inputs["Metallic"].default_value = 1.0 - bsdf.inputs["Roughness"].default_value = 0.45 + rings = nt.nodes.new("ShaderNodeTexWave") + rings.wave_type = 'RINGS' + rings.rings_direction = 'SPHERICAL' # concentric from center, not X-axis bands + rings.inputs["Scale"].default_value = 16.0 + rings.inputs["Distortion"].default_value = 0.5 + rough = nt.nodes.new("ShaderNodeMapRange") + rough.inputs["From Min"].default_value = 0.0 + rough.inputs["From Max"].default_value = 1.0 + rough.inputs["To Min"].default_value = 0.14 + rough.inputs["To Max"].default_value = 0.36 + nt.links.new(rings.outputs["Fac"], rough.inputs["Value"]) + nt.links.new(rough.outputs["Result"], bsdf.inputs["Roughness"]) obj.data.materials.append(mat) obj.location = (0.0, 0.0, 0.85) - obj.rotation_euler = (math.radians(38), 0.0, math.radians(22)) + obj.rotation_euler = (math.radians(46), 0.0, math.radians(22)) floor_me = bpy.data.meshes.new("Floor") bm = bmesh.new() @@ -341,9 +357,11 @@

Source

# metals live on reflections: soft warm key, restrained cool fill, and the # warm wedge raking the back wall (docs/VISUAL-STYLE.md) - light("Key", (-3.5, -4.5, 5.5), 700.0, 6.0, (1.0, 0.96, 0.9), (48, 0, -35)) - light("Fill", (5.0, -3.5, 2.5), 160.0, 9.0, (0.75, 0.85, 1.0), (65, 0, 50)) + light("Key", (-3.5, -4.5, 5.5), 550.0, 4.5, (1.0, 0.96, 0.9), (48, 0, -35)) + light("Fill", (5.0, -3.5, 2.5), 130.0, 9.0, (0.75, 0.85, 1.0), (65, 0, 50)) light("Wedge", (2.5, 5.5, 4.0), 380.0, 6.0, (1.0, 0.76, 0.5), (-68, 0, 190)) + # a small hot glint: brass needs one crisp specular streak to read machined + light("Glint", (1.8, -5.2, 6.2), 900.0, 0.9, (1.0, 0.9, 0.7), (40, 0, 18)) cam_data = bpy.data.cameras.new("Cam") cam_data.lens = 55.0 diff --git a/docs/gallery/contact-sheets/bmesh-gear-contact-sheet.webp b/docs/gallery/contact-sheets/bmesh-gear-contact-sheet.webp new file mode 100644 index 0000000..a1d5854 Binary files /dev/null and b/docs/gallery/contact-sheets/bmesh-gear-contact-sheet.webp differ diff --git a/docs/gallery/contact-sheets/depsgraph-export-contact-sheet.webp b/docs/gallery/contact-sheets/depsgraph-export-contact-sheet.webp new file mode 100644 index 0000000..cb7cbf0 Binary files /dev/null and b/docs/gallery/contact-sheets/depsgraph-export-contact-sheet.webp differ diff --git a/docs/gallery/contact-sheets/gn-sdf-remesh-contact-sheet.webp b/docs/gallery/contact-sheets/gn-sdf-remesh-contact-sheet.webp new file mode 100644 index 0000000..5358da6 Binary files /dev/null and b/docs/gallery/contact-sheets/gn-sdf-remesh-contact-sheet.webp differ diff --git a/docs/gallery/depsgraph-export/index.html b/docs/gallery/depsgraph-export/index.html index 86522ba..2f89930 100644 --- a/docs/gallery/depsgraph-export/index.html +++ b/docs/gallery/depsgraph-export/index.html @@ -287,11 +287,18 @@

Source

"""Base cube beside its evaluated form — the two counts the check compares.""" scene = bpy.context.scene + # one hero material on BOTH objects: the two silhouettes share a color so + # the only visible difference is the thing the check measures — base-mesh + # geometry versus the depsgraph-evaluated subsurf + hero = principled("Teal", (0.012, 0.30, 0.34, 1.0), 0.0, 0.2) + # left: the base mesh, modifier-free (a plain copy of the datablock) base_obj = bpy.data.objects.new("Base", obj.data.copy()) base_obj.location = (-1.7, 0.0, 1.0) - base_obj.data.materials.append( - principled("Graphite", (0.09, 0.10, 0.12, 1.0), 0.0, 0.55)) + # turned off camera-parallel so the key rakes the front face into a + # gradient instead of one flat tone + base_obj.rotation_euler = (0.0, 0.0, math.radians(32)) + base_obj.data.materials.append(hero) bpy.context.collection.objects.link(base_obj) # right: the SUBSURF object — what the depsgraph evaluates and the OBJ ships. @@ -302,8 +309,7 @@

Source

bottom = min(v.co.z for v in em.vertices) obj.evaluated_get(dg).to_mesh_clear() obj.location = (1.8, 0.0, -bottom) - obj.data.materials.append( - principled("EvalGreen", (0.03, 0.32, 0.10, 1.0), 0.0, 0.15)) + obj.data.materials.append(hero) for poly in obj.data.polygons: poly.use_smooth = True @@ -345,10 +351,10 @@

Source

light("Wedge", (2.5, 5.5, 4.0), 380.0, 6.0, (1.0, 0.76, 0.5), (-68, 0, 190)) cam_data = bpy.data.cameras.new("Cam") - cam_data.lens = 46.0 + cam_data.lens = 50.0 cam = bpy.data.objects.new("Cam", cam_data) - cam.location = (0.0, -8.2, 2.7) - cam.rotation_euler = (math.radians(78), 0.0, 0.0) + cam.location = (0.0, -7.4, 3.0) + cam.rotation_euler = (math.radians(76), 0.0, 0.0) scene.collection.objects.link(cam) scene.camera = cam diff --git a/docs/gallery/gn-sdf-remesh/index.html b/docs/gallery/gn-sdf-remesh/index.html index e6e3cac..7f853ec 100644 --- a/docs/gallery/gn-sdf-remesh/index.html +++ b/docs/gallery/gn-sdf-remesh/index.html @@ -252,8 +252,8 @@

Source

p.use_smooth = True mat = bpy.data.materials.new("Ceramic"); mat.use_nodes = True b = mat.node_tree.nodes.get('Principled BSDF') - b.inputs['Base Color'].default_value = (0.42, 0.028, 0.045, 1) # crimson ceramic - b.inputs['Roughness'].default_value = 0.22 + b.inputs['Base Color'].default_value = (0.45, 0.025, 0.05, 1) # crimson ceramic + b.inputs['Roughness'].default_value = 0.16 # glossy: the SDF facets read by their glints obj.data.materials.append(mat) return obj @@ -275,12 +275,15 @@

Source

wall.rotation_euler = (1.5708, 0, 0); bpy.context.collection.objects.link(wall) w = bpy.data.worlds.new("W"); w.use_nodes = True w.node_tree.nodes["Background"].inputs[0].default_value = (0.02, 0.021, 0.025, 1); sc.world = w - aim = bpy.data.objects.new("Aim", None); aim.location = (0, 0, 0.55); bpy.context.collection.objects.link(aim) - cam = bpy.data.objects.new("cam", bpy.data.cameras.new("cam")); cam.location = (0, -6.5, 2.2) + aim = bpy.data.objects.new("Aim", None); aim.location = (0, 0, 0.5); bpy.context.collection.objects.link(aim) + # off-axis and low: the remesh facets are the API evidence, and they only + # read when a raking key skims them from the side + cam = bpy.data.objects.new("cam", bpy.data.cameras.new("cam")); cam.location = (2.6, -5.2, 1.5) bpy.context.collection.objects.link(cam); sc.camera = cam c = cam.constraints.new('TRACK_TO'); c.target = aim; c.track_axis = 'TRACK_NEGATIVE_Z'; c.up_axis = 'UP_Y' - # shaped warm key, faint cool fill (docs/VISUAL-STYLE.md) - for nm, loc, en, sz, col in [("K", (-4, -5, 7), 650, 4.5, (1.0, 0.96, 0.9)), + # low raking warm key so every facet catches a distinct glint, faint cool + # fill (docs/VISUAL-STYLE.md) + for nm, loc, en, sz, col in [("K", (-5, -3.5, 2.8), 480, 3.0, (1.0, 0.96, 0.9)), ("F2", (5, -4, 2), 110, 7.0, (0.75, 0.85, 1.0))]: ld = bpy.data.lights.new(nm, 'AREA'); ld.energy = en; ld.size = sz; ld.color = col lo = bpy.data.objects.new(nm, ld); lo.location = loc; bpy.context.collection.objects.link(lo) diff --git a/examples/bmesh-gear/bmesh_gear.py b/examples/bmesh-gear/bmesh_gear.py index 982a398..3c17888 100644 --- a/examples/bmesh-gear/bmesh_gear.py +++ b/examples/bmesh-gear/bmesh_gear.py @@ -93,15 +93,31 @@ def render_still(obj, path, engine): scene = bpy.context.scene for poly in obj.data.polygons: poly.use_smooth = False # crisp machined facets - mat = bpy.data.materials.new("Steel") + # machined brass: a flat metal face lit by area lights renders as one + # featureless gradient, so the finish carries the design — concentric + # turning marks (a RINGS wave driving roughness) break the face into + # rings that each catch the key at a different angle + mat = bpy.data.materials.new("MachinedBrass") mat.use_nodes = True - bsdf = mat.node_tree.nodes["Principled BSDF"] - bsdf.inputs["Base Color"].default_value = (0.75, 0.77, 0.8, 1.0) + nt = mat.node_tree + bsdf = nt.nodes["Principled BSDF"] + bsdf.inputs["Base Color"].default_value = (0.72, 0.44, 0.16, 1.0) bsdf.inputs["Metallic"].default_value = 1.0 - bsdf.inputs["Roughness"].default_value = 0.45 + rings = nt.nodes.new("ShaderNodeTexWave") + rings.wave_type = 'RINGS' + rings.rings_direction = 'SPHERICAL' # concentric from center, not X-axis bands + rings.inputs["Scale"].default_value = 16.0 + rings.inputs["Distortion"].default_value = 0.5 + rough = nt.nodes.new("ShaderNodeMapRange") + rough.inputs["From Min"].default_value = 0.0 + rough.inputs["From Max"].default_value = 1.0 + rough.inputs["To Min"].default_value = 0.14 + rough.inputs["To Max"].default_value = 0.36 + nt.links.new(rings.outputs["Fac"], rough.inputs["Value"]) + nt.links.new(rough.outputs["Result"], bsdf.inputs["Roughness"]) obj.data.materials.append(mat) obj.location = (0.0, 0.0, 0.85) - obj.rotation_euler = (math.radians(38), 0.0, math.radians(22)) + obj.rotation_euler = (math.radians(46), 0.0, math.radians(22)) floor_me = bpy.data.meshes.new("Floor") bm = bmesh.new() @@ -139,9 +155,11 @@ def light(name, loc, energy, size, col, rot): # metals live on reflections: soft warm key, restrained cool fill, and the # warm wedge raking the back wall (docs/VISUAL-STYLE.md) - light("Key", (-3.5, -4.5, 5.5), 700.0, 6.0, (1.0, 0.96, 0.9), (48, 0, -35)) - light("Fill", (5.0, -3.5, 2.5), 160.0, 9.0, (0.75, 0.85, 1.0), (65, 0, 50)) + light("Key", (-3.5, -4.5, 5.5), 550.0, 4.5, (1.0, 0.96, 0.9), (48, 0, -35)) + light("Fill", (5.0, -3.5, 2.5), 130.0, 9.0, (0.75, 0.85, 1.0), (65, 0, 50)) light("Wedge", (2.5, 5.5, 4.0), 380.0, 6.0, (1.0, 0.76, 0.5), (-68, 0, 190)) + # a small hot glint: brass needs one crisp specular streak to read machined + light("Glint", (1.8, -5.2, 6.2), 900.0, 0.9, (1.0, 0.9, 0.7), (40, 0, 18)) cam_data = bpy.data.cameras.new("Cam") cam_data.lens = 55.0 diff --git a/examples/bmesh-gear/preview.webp b/examples/bmesh-gear/preview.webp index 72b0eb8..147d448 100644 Binary files a/examples/bmesh-gear/preview.webp and b/examples/bmesh-gear/preview.webp differ diff --git a/examples/depsgraph-export/depsgraph_export.py b/examples/depsgraph-export/depsgraph_export.py index a6ac5ec..5eaa836 100644 --- a/examples/depsgraph-export/depsgraph_export.py +++ b/examples/depsgraph-export/depsgraph_export.py @@ -82,11 +82,18 @@ def render_still(obj, path, engine): """Base cube beside its evaluated form — the two counts the check compares.""" scene = bpy.context.scene + # one hero material on BOTH objects: the two silhouettes share a color so + # the only visible difference is the thing the check measures — base-mesh + # geometry versus the depsgraph-evaluated subsurf + hero = principled("Teal", (0.012, 0.30, 0.34, 1.0), 0.0, 0.2) + # left: the base mesh, modifier-free (a plain copy of the datablock) base_obj = bpy.data.objects.new("Base", obj.data.copy()) base_obj.location = (-1.7, 0.0, 1.0) - base_obj.data.materials.append( - principled("Graphite", (0.09, 0.10, 0.12, 1.0), 0.0, 0.55)) + # turned off camera-parallel so the key rakes the front face into a + # gradient instead of one flat tone + base_obj.rotation_euler = (0.0, 0.0, math.radians(32)) + base_obj.data.materials.append(hero) bpy.context.collection.objects.link(base_obj) # right: the SUBSURF object — what the depsgraph evaluates and the OBJ ships. @@ -97,8 +104,7 @@ def render_still(obj, path, engine): bottom = min(v.co.z for v in em.vertices) obj.evaluated_get(dg).to_mesh_clear() obj.location = (1.8, 0.0, -bottom) - obj.data.materials.append( - principled("EvalGreen", (0.03, 0.32, 0.10, 1.0), 0.0, 0.15)) + obj.data.materials.append(hero) for poly in obj.data.polygons: poly.use_smooth = True @@ -140,10 +146,10 @@ def light(name, loc, energy, size, col, rot): light("Wedge", (2.5, 5.5, 4.0), 380.0, 6.0, (1.0, 0.76, 0.5), (-68, 0, 190)) cam_data = bpy.data.cameras.new("Cam") - cam_data.lens = 46.0 + cam_data.lens = 50.0 cam = bpy.data.objects.new("Cam", cam_data) - cam.location = (0.0, -8.2, 2.7) - cam.rotation_euler = (math.radians(78), 0.0, 0.0) + cam.location = (0.0, -7.4, 3.0) + cam.rotation_euler = (math.radians(76), 0.0, 0.0) scene.collection.objects.link(cam) scene.camera = cam diff --git a/examples/depsgraph-export/preview.webp b/examples/depsgraph-export/preview.webp index 496294a..7f62723 100644 Binary files a/examples/depsgraph-export/preview.webp and b/examples/depsgraph-export/preview.webp differ diff --git a/examples/gn-sdf-remesh/gn_sdf_remesh.py b/examples/gn-sdf-remesh/gn_sdf_remesh.py index b315bf4..6ab360a 100644 --- a/examples/gn-sdf-remesh/gn_sdf_remesh.py +++ b/examples/gn-sdf-remesh/gn_sdf_remesh.py @@ -49,8 +49,8 @@ def build(): p.use_smooth = True mat = bpy.data.materials.new("Ceramic"); mat.use_nodes = True b = mat.node_tree.nodes.get('Principled BSDF') - b.inputs['Base Color'].default_value = (0.42, 0.028, 0.045, 1) # crimson ceramic - b.inputs['Roughness'].default_value = 0.22 + b.inputs['Base Color'].default_value = (0.45, 0.025, 0.05, 1) # crimson ceramic + b.inputs['Roughness'].default_value = 0.16 # glossy: the SDF facets read by their glints obj.data.materials.append(mat) return obj @@ -72,12 +72,15 @@ def render_still(obj, path, engine): wall.rotation_euler = (1.5708, 0, 0); bpy.context.collection.objects.link(wall) w = bpy.data.worlds.new("W"); w.use_nodes = True w.node_tree.nodes["Background"].inputs[0].default_value = (0.02, 0.021, 0.025, 1); sc.world = w - aim = bpy.data.objects.new("Aim", None); aim.location = (0, 0, 0.55); bpy.context.collection.objects.link(aim) - cam = bpy.data.objects.new("cam", bpy.data.cameras.new("cam")); cam.location = (0, -6.5, 2.2) + aim = bpy.data.objects.new("Aim", None); aim.location = (0, 0, 0.5); bpy.context.collection.objects.link(aim) + # off-axis and low: the remesh facets are the API evidence, and they only + # read when a raking key skims them from the side + cam = bpy.data.objects.new("cam", bpy.data.cameras.new("cam")); cam.location = (2.6, -5.2, 1.5) bpy.context.collection.objects.link(cam); sc.camera = cam c = cam.constraints.new('TRACK_TO'); c.target = aim; c.track_axis = 'TRACK_NEGATIVE_Z'; c.up_axis = 'UP_Y' - # shaped warm key, faint cool fill (docs/VISUAL-STYLE.md) - for nm, loc, en, sz, col in [("K", (-4, -5, 7), 650, 4.5, (1.0, 0.96, 0.9)), + # low raking warm key so every facet catches a distinct glint, faint cool + # fill (docs/VISUAL-STYLE.md) + for nm, loc, en, sz, col in [("K", (-5, -3.5, 2.8), 480, 3.0, (1.0, 0.96, 0.9)), ("F2", (5, -4, 2), 110, 7.0, (0.75, 0.85, 1.0))]: ld = bpy.data.lights.new(nm, 'AREA'); ld.energy = en; ld.size = sz; ld.color = col lo = bpy.data.objects.new(nm, ld); lo.location = loc; bpy.context.collection.objects.link(lo) diff --git a/examples/gn-sdf-remesh/preview.webp b/examples/gn-sdf-remesh/preview.webp index aec33a9..f0289ec 100644 Binary files a/examples/gn-sdf-remesh/preview.webp and b/examples/gn-sdf-remesh/preview.webp differ