scenario-unity-shaders

Installation
SKILL.md

Unity shaders (technical artist, shading)

Expert shading in Unity 6.3 is a shader you can prove: it compiles for the target graphics API, every pass agrees, its variants are counted after stripping, its first use is shown warm, its cost is measured in GPU milliseconds, and its look is checked in a capture with numbers. An agent without a mouse gets there through text: hand-written URP HLSL is the primary route, Shader Graph is used through existing graphs driven by exposed properties plus agent-owned .hlsl behind Custom Function nodes, and new graph wiring is a GUI step (references/gui-paths.md). Target: 6000.3.21f1, URP 17.3, Shader Graph 17.3, Metal on Apple Silicon. If a sibling skill named here is missing from your available skills, ask the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.

REQUIRED BACKGROUND: scenario-unity-expert (channels, review loop, 6.3 traps). Its toolkit (ut_env, ut_run, ut_review, AgentKit) is imported, never copied.

Status (2026-09-24, after the blind-grade refactor): every procedure ran in Unity 6000.3.21f1 here: tests/code/unity-shaders/run_all.sh --live (73 live checks including a development-player PSO proof, 32 offline checks, all passing; evidence in archive/tests/unity-shaders/).

Stance (the expert delta)

  1. Text first, graphs by template. Shader Graph has no loops and almost no lighting nodes, so lights and loops live in HLSL (Daniel Ilett, F8bAI6dIrto [00:07:13]); its one advantage is generated depth and shadow passes (bH--RU6qyTw [00:38:24]), which AgentPasses.hlsl gives hand-written shaders too. With graphs, duplicate one, set its exposed references, put changing logic in a File-mode .hlsl. Graph Settings bind every pass: Alpha Clipping without Allow Material Override writes #define _ALPHATEST_ON 1 into all 8 generated passes, and no Boolean keyword removes it (installed UniversalTarget.cs, observed).
  2. Every pass must agree. Displacement and clip() run in ShadowCaster, DepthOnly and DepthNormals too (6.3 Manual; Ilett [00:37:52]). A missing DepthOnly pass makes an opaque shader invisible with Depth Priming (observed). DepthNormals writes the raw world normal: the Manual's normal * 0.5 + 0.5 example reads back wrong in SSAO and outlines (observed).
  3. Read the installed pipeline, not the tutorial. Copy keywords from the installed Lit.shader (Ilett [00:14:51]; ut_shaders.urp_lit_keywords()): 6.3 has _CLUSTER_LIGHT_LOOP and four soft shadow variants. LIGHT_LOOP_BEGIN skips directional lights, so a custom loop needs a directional pre-loop under Forward+; Unity's own Custom Lighting sample lacks it (observed).
  4. Variants are a budget counted after stripping. shader_feature for material toggles, multi_compile only for runtime C# toggles, dynamic_branch tested with if, never #if (6.3 Manual). Strict Shader Variant Matching before stripping, then the funnel in the build log (Carotenuto, Unity blog 2024). A shader_feature (or _local) variant ships only if a material in the build enables it (observed 0 vs 6): never toggle one from runtime C#. Unity 6 Shader Build Settings exclude or retype keywords with zero code, but a dynamic_branch override is silently ignored for #if-tested keywords, URP's soft shadow and SSAO sets included (observed).
  5. First use is proven warm. Trace one GraphicsStateCollection per graphics API and platform in a development player, warm it behind the loading screen, and show gameplay frames with zero Shader.CreateGPUProgram samples and zero logged uploads (6.3 Manual; observed 14 to 0 on Metal).
  6. Cost is GPU time, not instruction count. Loops count at max trip, float4 costs about 4x float, transcendentals cost many cycles (Ben Cloward, E82XxlXMJs4 [00:15:44]). Measured here: 64 sin on a float4 about 3x a float, 10x or more a multiply-add loop. Half pays only where a device profiler shows ALU-bound (Freya Holmér, kfM-yu0iQBk [01:20:26]): desktop PlatformDefault compiles half as float.
  7. Render Graph shape. RecordRenderGraph declares, never records; blit once into a new texture then resourceData.cameraColor = destination; gate in AddRenderPasses; refuse the back buffer (6.3 docs; Unity dither sample, U8PygjYAF7A [00:09:06] [00:10:10]).
  8. One scalar field, one threshold, two colors; look and measure. Toon, water, dissolve, outline and dither threshold a 0..1 value (visual digest); the water's depth mask also calms its shore normals (gRq-IdShxpU [00:06:12]). Offset an emissive edge from the clip test (Brackeys, taMp1g1pBeE [00:05:31]). Output intermediates as colors (Freya [01:57:25]), then assert pixels at projected probes.
Installs
158
GitHub Stars
1.0K
First Seen
Sep 26, 2026