Author a Custom Material
Write your own material by dropping small snippets of WGSL into the engine's shading pipeline - a wind-sway vertex, a tinted surface colour, a perturbed normal, or a cutout mask - and assign it to an object.
Awaken materials are hook-based. Rather than writing a whole shader, you fill in up to four well-defined hook slots and the engine composes them into its standard lit shader (composeShader). Each slot is a short piece of WGSL that runs at a specific point in the pipeline, and you expose tunable values as params the hooks read as params.<name>. This tutorial builds one custom material from a fresh asset and assigns it, using the Shader Editor.
Coming soon
This tutorial is being written. The step outline and hook reference below are final, so you can author a material today. Finished prose, screenshots, and a copy-paste sample are on the way.
What you will build
A custom material - for example a gently swaying, tinted surface - authored from WGSL hooks with a couple of Inspector-editable params, then assigned to an object in your scene.
The four hook slots
The Shader Editor exposes exactly four slots, in pipeline order. Leave a slot blank and its marker is simply stripped - you pay for only what you use.
The table below is in execution order - the order the GPU runs them, and the order the editor lists them in.
| Slot | Runs at | Fill it to… |
|---|---|---|
| Vertex Position | vertex stage, after the model transform (before the clip transform) | modify world - the world-space vertex position (a vec4<f32>) - e.g. wind sway |
| Discard | first statement of the fragment stage | early-exit a fragment: if (…) { discard; } using in.uv / in.worldPos / params.* |
| Surface Normal | fragment stage, before shading | perturb N - the mutable world-space normal - detail, waviness |
| Surface Color | fragment stage, before lighting | set or adjust albedo (a mutable vec3<f32>, rgb) and outAlpha - tints, gradients, transparency |
Discard runs before the albedo texture is sampled, so texel is not in scope there - it can't do texture alpha-testing. It doesn't need to: the template already discards at texel.a < 0.5 for every material. Use this slot for cutouts the engine can't guess - UV patterns, world-height clipping, param-driven dissolves.
Params can be float, vec2, vec3, vec4, color, bool, or texture, and every hook reads them as params.<name>. A few engine values are in scope too: globals.time (seconds, handy for animating sway) and globals.anim (0 while editing, 1 during Play - multiply your time by it to freeze animation in the editor), plus - in the fragment hooks - the interpolated varyings on in (in.worldPos, in.uv, in.color).
Hooks or a node graph
The Shader Editor has two create buttons. + New Shader authors the hook slots this tutorial uses; + New Shader Graph opens a node canvas that compiles to those same hooks and params. On a graph, the Tidy button in the Node Graph header auto-arranges the nodes left→right with the Surface node on the right (undoable), and the Fit button beside it frames the whole graph. This tutorial fills the hook slots directly.
Before you start
- Complete Make Your First Game so you have a scene with an object to assign to.
- Read Materials (how materials fit the render pipeline) and the Shader Editor reference.
Steps
- Create the material. Open the Shader Editor and click + New Shader. Rename it something memorable inline.
- Add params. Add the values your hooks will read - for a sway material, a
float swaySpeed, afloat swayAmount, and acolor tint. Each becomes an editable row; hooks reference them asparams.swaySpeed,params.tint, and so on. - Fill the hook slots. Write WGSL into the slots you need. For example, offset
worldin Vertex Position for sway (world.x = world.x + sin(globals.time * params.swaySpeed + world.z) * params.swayAmount;) and tint in Surface Color withalbedo = albedo * params.tint;. Edits mirror live - the Viewport re-renders as you type. You don't have to guess whether the WGSL is valid: the editor compiles it for real as you type and shows any error as a squiggle on the offending line, with a✓ compiles/✗ N errorsstatus beside the material name. If you do render a broken hook, the object falls back to the default shader rather than breaking the frame. - Assign it. Select an object, and in the Inspector set its material to your new asset (or drag the material onto the object). See Materials for how the assignment is stored.
- Tune. Adjust the params and watch the surface update. A param marked per-object can also be overridden on each entity from its MeshMaterial component in the Inspector - so one material can dress many objects with different colours or speeds, without duplicating it. Resetting an override clears it, so that object goes back to following the material's own default.
- Play and ship. The composed shader runs identically in Play and in the exported game - see Render Pipeline Overview and Export game.html.
📸 Screenshot - save as img/tutorials-custom-shader.png
The Shader Editor: the four hook slots (one filled with a WGSL snippet), the param table below them, and the Viewport re-rendering the assigned object live.
Where to go next
- Apply your material to imported surfaces from Import a Unity Scene.
- Learn the full shading model behind the hooks in PBR & Lighting.
See also
- Shader Editor - the panel, slots, and param table used here
- Materials - how materials attach to objects and render
- Render Pipeline Overview - where hooks run in a frame
- PBR & Lighting - the lit shader your hooks feed into