Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,15 +246,16 @@ mapping (`BLENDER_EEVEE` on 5.x, `BLENDER_EEVEE_NEXT` on 4.2-4.5).
</tr>
<tr>
<td width="46%" valign="middle">
<a href="examples/shader-node-group/"><img src="examples/shader-node-group/preview.webp" alt="Shader node group: a teal sphere and a magenta sphere sharing one TintedGloss node group with different Tint parameters" /></a>
<a href="examples/shader-node-group/"><img src="examples/shader-node-group/preview.webp" alt="Shader node group: five speckled stoneware mugs in oxblood, amber, celadon, teal and cobalt sharing one DippedGlaze node group with different Tint parameters" /></a>
</td>
<td valign="middle">

### [shader-node-group](examples/shader-node-group/)

One reusable `TintedGloss` group declared via `tree.interface.new_socket`, instanced in two
One reusable `DippedGlaze` group declared via `tree.interface.new_socket`, instanced in five
materials with different Tint values. Witnesses the grouping contract: shared datablock
(`users == 2`), parameters on the group **node** — two spheres, one group, two colors.
(`users == 5`), parameters on the group **node** — five mugs share one foot band, dip line
and speckle, and differ only in colour.

</td>
</tr>
Expand Down
Binary file modified docs/gallery/assets/shader-node-group-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions docs/gallery/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -420,12 +420,12 @@ <h2><a href="bmesh-gear/">bmesh-gear</a></h2>
</article>
<article class="card" data-tags="materials node-groups">
<a class="card-media" href="shader-node-group/" tabindex="-1" aria-hidden="true">
<img src="assets/shader-node-group-hero.webp" alt="Two glossy spheres, one teal and one magenta, sharing one shader group." loading="lazy" decoding="async" />
<img src="assets/shader-node-group-hero.webp" alt="Five speckled stoneware mugs in oxblood, amber, celadon, teal and cobalt on a walnut riser, each with the same bare clay foot and wavy dip line from one shared glaze group." loading="lazy" decoding="async" />
</a>
<div class="card-body">
<h2><a href="shader-node-group/">shader-node-group</a></h2>
<p class="teaches">One reusable shader group declared via tree.interface.new_socket, instanced in two materials with different Tint values — two spheres, one group, two colors.</p>
<p class="witnesses"><span class="tag">witnesses</span> Grouping contract: interface sockets appear on every instance, both materials share one group datablock (users == 2), and per-material parameters live on the group node, not inside the tree.</p>
<p class="teaches">One reusable shader group declared via tree.interface.new_socket, instanced in five materials with different Tint values — one dipped-glaze group, five stoneware mugs, five colors.</p>
<p class="witnesses"><span class="tag">witnesses</span> Grouping contract: interface sockets appear on every instance, all five materials share one group datablock (users == 5), and per-material parameters live on the group node, not inside the tree.</p>
<a class="card-link" href="shader-node-group/">View example<span class="sr-only"> shader-node-group</span> <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand Down
315 changes: 243 additions & 72 deletions docs/gallery/shader-node-group/index.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions examples/gallery.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@
{
"name": "shader-node-group",
"dir": "examples/shader-node-group",
"teaches": "One reusable shader group declared via tree.interface.new_socket, instanced in two materials with different Tint values — two spheres, one group, two colors.",
"alt": "Two glossy spheres, one teal and one magenta, sharing one shader group.",
"witnessesFix": "Grouping contract: interface sockets appear on every instance, both materials share one group datablock (users == 2), and per-material parameters live on the group node, not inside the tree.",
"teaches": "One reusable shader group declared via tree.interface.new_socket, instanced in five materials with different Tint values — one dipped-glaze group, five stoneware mugs, five colors.",
"alt": "Five speckled stoneware mugs in oxblood, amber, celadon, teal and cobalt on a walnut riser, each with the same bare clay foot and wavy dip line from one shared glaze group.",
"witnessesFix": "Grouping contract: interface sockets appear on every instance, all five materials share one group datablock (users == 5), and per-material parameters live on the group node, not inside the tree.",
"hero": "docs/gallery/assets/shader-node-group-hero.webp",
"preview": "examples/shader-node-group/preview.webp",
"tags": [
Expand Down
39 changes: 24 additions & 15 deletions examples/shader-node-group/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
# Shader Node Group

A runnable example that declares a reusable `TintedGloss` shader group through
A runnable example that declares a reusable `DippedGlaze` shader group through
`tree.interface.new_socket` — the 4.x/5.x API that replaced `tree.inputs`/`tree.outputs` —
and instances it in two materials with different parameters, following
and instances it in five mug materials with different parameters, following
[`procedural-materials-and-shaders`](../../skills/procedural-materials-and-shaders/SKILL.md)
and the [`shader-node-group`](../../snippets/shader-node-group.py) snippet.

**What it witnesses:** the grouping contract. Sockets declared on the interface appear on
every group-node instance; both materials share ONE group datablock (`users == 2`); and the
per-material Tint lives on the group **node**, not inside the group — set it inside the tree
and every material changes at once. The render is the proof: two spheres, one group, two
colors.
every group-node instance; all five materials share ONE group datablock (`users == 5`); and
the per-material Tint lives on the group **node**, not inside the group — set it inside the
tree and every mug changes at once.

**The render is the proof.** The group is a dipped stoneware glaze: bare buff clay at the
foot, a wavy dip line, iron speckle through clay and glaze alike, and a glaze that breaks
back toward clay over the rim. All of that lives inside the group, so the five mugs — a
warm-to-cool lineup of oxblood, amber, celadon, teal and cobalt — carry the same foot band,
the same dip line, the same speckle field and the same rim break. Only the instance-level
Tint differs. If Tint were baked into the group, the lineup would be one colour; if each
material built its own tree, the shared character would be a copy rather than one
datablock (the check's `users` gate).

## Staging

The still renders under the Standard view transform on the dark house
stage; it had no view transform set, so AgX washed both tints toward
pastel over a lighter floor. The key is larger and softer (the glossy
spheres had mirrored it as a hard white square), and the camera aims
at the sphere centres. Render path only.
A staggered shop-display lineup on the dark house stage: the odd mugs stand on a low
walnut riser behind the even ones, so five mugs fill the frame without shrinking to a
thin strip. Handles all turn the same way so the only thing that changes along the row is
colour. The still renders under the Standard view transform (AgX would wash the five
tints toward pastel); the key's spread is narrowed so it lights the mugs, not the back
wall, and a warm wedge sits between the riser and the wall. Render path only.

## Run

Expand All @@ -30,8 +39,8 @@ blender --background --python shader_node_group.py --
blender --background --python shader_node_group.py -- --same-tint

# Also render a still (EEVEE on a GPU host; use --engine cycles on GPU-less hosts):
blender --background --python shader_node_group.py -- --output spheres.png
blender --background --python shader_node_group.py -- --output spheres.png --engine cycles
blender --background --python shader_node_group.py -- --output mugs.png
blender --background --python shader_node_group.py -- --output mugs.png --engine cycles
```

## Exit codes
Expand All @@ -45,9 +54,9 @@ against it. `10` is the shared framing helper.
| 1 | Uncaught exception (FATAL wrapper) |
| 2 | argparse / usage |
| 3 | Interface sockets missing Tint / Roughness / Shader |
| 4 | Group datablock `users` ≠ 2 |
| 4 | Group datablock `users` ≠ number of mug materials (5) |
| 5 | Instance points at a different node tree |
| 6 | Instance Tint values identical (`--same-tint` lands here) |
| 6 | Instance Tint values not pairwise distinct (`--same-tint` lands here) |
| 7 | `--output` produced no file |
| 10 | Gallery framing violation |

Expand Down
Binary file modified examples/shader-node-group/preview.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading