|
| 1 | +# Shader Graph |
| 2 | + |
| 3 | +Build a material out of nodes instead of sliders. A shader graph replaces what a |
| 4 | +material looks like — its colour, glow, roughness, surface detail, transparency, even |
| 5 | +where its vertices sit — and everything you author replicates to your peers and saves |
| 6 | +with the scene. |
| 7 | + |
| 8 | +For the node-by-node reference, see [Shader Nodes](shader-nodes.md). |
| 9 | + |
| 10 | +## Opening it |
| 11 | + |
| 12 | +The Shader editor is a tab in the bottom dock, beside the Node editor, Explorer, UV |
| 13 | +editor and Animation: |
| 14 | + |
| 15 | +- click **+** in the dock tab strip and choose **Shader editor**, or |
| 16 | +- right-click an object and pick **Edit shader**, or |
| 17 | +- open the object's properties and press **Open in Shader editor** in the Material |
| 18 | + section. |
| 19 | + |
| 20 | +## Scope: this object, or the whole scene |
| 21 | + |
| 22 | +There is no scope control to get wrong — **the selection is the control**: |
| 23 | + |
| 24 | +| Selection | What you are editing | |
| 25 | +|---|---| |
| 26 | +| one object | that object's own material | |
| 27 | +| nothing selected | the **scene default**, which drives every object with no graph of its own | |
| 28 | + |
| 29 | +The header always says which. An object with its own graph ignores the scene default, |
| 30 | +so the order is *own graph → scene default → the object's real material*. |
| 31 | + |
| 32 | +## The Surface node |
| 33 | + |
| 34 | +Every graph ends at one **Surface** node. Each of its inputs replaces one part of the |
| 35 | +material, and **anything you leave unconnected keeps the material's own value** — so a |
| 36 | +graph that only wires `roughness` changes nothing else. |
| 37 | + |
| 38 | +| Input | What it replaces | |
| 39 | +|---|---| |
| 40 | +| albedo | the base colour (multiplied into it, like a texture would be) | |
| 41 | +| emissive | glow, added after lighting | |
| 42 | +| roughness | how rough or polished the surface is | |
| 43 | +| metalness | how metallic it is | |
| 44 | +| normal | surface detail — the direction the surface *appears* to face | |
| 45 | +| opacity | transparency; wiring it switches the material to blending | |
| 46 | +| ao | shades the indirect (ambient) light only | |
| 47 | +| position | moves the vertices themselves — see below | |
| 48 | + |
| 49 | +### Vertex displacement |
| 50 | + |
| 51 | +`position` is different from the others: it runs while the mesh's vertices are being |
| 52 | +placed, not while its pixels are being shaded. Wire a vector into it and every vertex |
| 53 | +moves by that amount, so **Noise → position** ripples a surface and **Vector 3 → |
| 54 | +position** shifts the whole object. |
| 55 | + |
| 56 | +Because it happens at a different point in the pipeline, three things follow: |
| 57 | + |
| 58 | +- **Normal** gives you the object-space normal there, which is what you displace *along* |
| 59 | + (multiply Noise by Normal to push a surface outwards rather than sideways). |
| 60 | +- **View direction**, **Fresnel** and **Normal map** cannot be used — there is no camera |
| 61 | + vector and no screen-space information while vertices are being placed. Wiring one in |
| 62 | + is refused with a message rather than producing a broken shader. |
| 63 | +- **UV** gives you the mesh's raw texture coordinates. |
| 64 | + |
| 65 | +Two honest limitations, both shared with three.js's own displacement: |
| 66 | + |
| 67 | +- lighting is **not** recalculated for the new shape, so a heavily displaced surface can |
| 68 | + look flatter than it is; |
| 69 | +- the **shadow** is cast by the undisplaced mesh. |
| 70 | + |
| 71 | +## Textures |
| 72 | + |
| 73 | +The **Texture** node samples an image from your [Explorer](explorer.md) library. Assign |
| 74 | +one by clicking the swatch on the node and picking a file, or by dragging an Explorer |
| 75 | +image card onto it. Hovering the swatch shows a bigger preview with the image's |
| 76 | +dimensions and size. |
| 77 | + |
| 78 | +What the graph stores is the image's **content hash**, not the image. That means the |
| 79 | +picture travels to your peers **once** and is then reused — it is not re-sent every time |
| 80 | +you nudge a slider. A peer who does not have the image yet asks for it and shows the |
| 81 | +object untextured (not black) until it arrives. |
| 82 | + |
| 83 | +Textures **tile** by default, which is what makes [Tiling & offset](shader-nodes.md#uv) |
| 84 | +and [Panner](shader-nodes.md#uv) work. |
| 85 | + |
| 86 | +## Animated shaders stay in sync |
| 87 | + |
| 88 | +Anything driven by the **Time** node — or by **Panner**, which uses the same shared clock — |
| 89 | +reads the time from a clock all peers agree on. Nobody sends "the shader is now at 3.2 seconds": each |
| 90 | +peer works it out and arrives at the same answer. That is why a scrolling texture or a |
| 91 | +pulsing glow looks identical on every screen with no network traffic at all. |
| 92 | + |
| 93 | +## Editing while it runs |
| 94 | + |
| 95 | +Numbers, colours and vectors on a node are **live uniforms**: dragging one changes the |
| 96 | +picture immediately without recompiling anything. Adding or rewiring nodes recompiles, |
| 97 | +which takes well under a millisecond. |
| 98 | + |
| 99 | +If a graph is broken mid-edit, the object **keeps the last material that worked** and the |
| 100 | +error appears in a strip at the top of the tab, naming the node and the problem. |
| 101 | + |
| 102 | +## Driving a shader from a behaviour graph |
| 103 | + |
| 104 | +The [Set Shader Uniform](nodes/setuniform.md) node in the [Node editor](node-system.md) |
| 105 | +writes one of a graph's numbers, so a proximity trigger, a timer or a counter can drive a |
| 106 | +shader parameter. Select the node in the Shader editor and its info pane lists the uniform |
| 107 | +names to paste in. |
| 108 | + |
| 109 | +## Saving, sharing and undo |
| 110 | + |
| 111 | +- The **graph** is what replicates and what gets saved — never the compiled result — so |
| 112 | + the same graph produces the same pixels everywhere. |
| 113 | +- Graphs are stored in `.tpscene` files and sessions, and in the autosave. |
| 114 | +- A **glTF export** cannot carry a node material: the object is exported with the |
| 115 | + material it had before the graph, and a toast tells you what was dropped. |
| 116 | +- Every change is undoable, and a slider drag is one undo step rather than dozens. |
| 117 | + |
| 118 | +## When an object cannot take a graph |
| 119 | + |
| 120 | +Objects with **more than one material slot** are declined with an explanation rather than |
| 121 | +half-supported. Convert or split the mesh first if you need per-slot shaders. |
| 122 | + |
| 123 | +## The Material section while a shader is active |
| 124 | + |
| 125 | +Once a graph drives an object, its properties panel says so and hides the ordinary |
| 126 | +colour, material-type and texture rows — they would be editing a result that the next |
| 127 | +recompile discards. You get **Open in Shader editor** and **Detach** instead. Cast and |
| 128 | +receive shadow stay editable, because those are properties of the object rather than of |
| 129 | +the material. |
| 130 | + |
| 131 | +Detach removes the object's own graph. If the look came from the **scene default** there |
| 132 | +is no per-object graph to remove, and the panel says so — edit or remove the scene graph |
| 133 | +instead. |
| 134 | + |
| 135 | +## Compile backends |
| 136 | + |
| 137 | +Graphs compile locally on every peer. The built-in compiler patches three.js's own |
| 138 | +shader, so lighting, shadows and fog keep working and adding a light to the scene affects |
| 139 | +shader-driven objects like everything else. |
| 140 | + |
| 141 | +A module can register another compiler (a different lighting model, or a heavier one it |
| 142 | +ships itself). A graph remembers which backend it was authored for; a peer without that |
| 143 | +module compiles it with the built-in instead of failing, and gets the intended result as |
| 144 | +soon as the module is present. |
0 commit comments