scenario-unity-shaders
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)
- 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.hlslgives 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 1into all 8 generated passes, and no Boolean keyword removes it (installedUniversalTarget.cs, observed). - 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'snormal * 0.5 + 0.5example reads back wrong in SSAO and outlines (observed). - 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_LOOPand four soft shadow variants.LIGHT_LOOP_BEGINskips directional lights, so a custom loop needs a directional pre-loop under Forward+; Unity's own Custom Lighting sample lacks it (observed). - Variants are a budget counted after stripping.
shader_featurefor material toggles,multi_compileonly for runtime C# toggles,dynamic_branchtested withif, never#if(6.3 Manual). Strict Shader Variant Matching before stripping, then the funnel in the build log (Carotenuto, Unity blog 2024). Ashader_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 adynamic_branchoverride is silently ignored for#if-tested keywords, URP's soft shadow and SSAO sets included (observed). - First use is proven warm. Trace one
GraphicsStateCollectionper graphics API and platform in a development player, warm it behind the loading screen, and show gameplay frames with zeroShader.CreateGPUProgramsamples and zero logged uploads (6.3 Manual; observed 14 to 0 on Metal). - 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
sinon 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 compileshalfasfloat. - Render Graph shape.
RecordRenderGraphdeclares, never records; blit once into a new texture thenresourceData.cameraColor = destination; gate inAddRenderPasses; refuse the back buffer (6.3 docs; Unity dither sample, U8PygjYAF7A [00:09:06] [00:10:10]). - 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.