Unity shaders (technical artist, shading)
SkillMediaHelps your agent write and debug Unity shaders like water, toon, dissolve, outlines, and render effects.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Unity shaders (technical artist, shading) skill
About this skill
Use when a Unity 6.3 URP task involves shaders: hand-written HLSL, Shader Graph water, toon, dissolve or outline, Custom Function nodes, a renderer feature or full-screen effect on Render Graph, compute shaders, shader variants, keywords, stripping, Shader Build Settings, PSO warm-up, or shader cost
What this skill tells your AI
The instructions your AI receives, as published by scenario-labs/skills in skills/game-engines/unity/scenario-unity-shaders/SKILL.md and read by ahel’s review.
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.
Establish first
- Platforms and graphics APIs: Metal, Vulkan, DX12, GLES 3.x, WebGL 2 or WebGPU. Compute runs on Metal, Vulkan, DX11/12 and GLES 3.1+ (4 buffers guaranteed on GLES 3.1), not on GLES 3.0 or WebGL 2; the Web gets it through WebGPU (experimental in 6.3). The GPU Resident Drawer and STP exclude GLES entirely.
- Rendering path per renderer (Forward, Forward+, Deferred+): decides the light-loop code; Shader Graph custom lighting works in Forward and Forward+ only.
- URP assets per quality level: Depth and Opaque Texture, soft shadows, SSAO. The template's
Mobile_RPAssethas Depth Texture off: depth-faded water needs it on or a baked shore mask. - Style and authors: toon or PBR; artists editing graphs (keep a graph, agent owns the
.hlsl) or code only. - Per-object values: material instance,
MeshRenderer/SkinnedMeshRenderer.SetShaderUserValue(6.3, SRP Batcher friendly), orMaterialPropertyBlock(breaks the SRP Batcher). - Budgets: GPU ms for the effect, variants per shader, overdraw, first-use hitches.
Defaults when silent: URP 17.3 Forward+, PC and mobile tiers from the template, HLSL, shaders under
Assets/AgentShaders/.
Workflow
- Install and inventory.
ut_shaders.install(P);RendererFeatureJobs.ListFeatures(every quality level, asset, renderer, feature);lint_shader_file,shadergraph_summary(targets, properties, Graph Settings findings),lint_project_keywords. GATE: pipeline facts in the plan; lint errors listed. - Author. Start from
scripts/Shaders/:AgentLit,AgentToon,AgentWater,AgentDissolve,AgentEdgeOutline,AgentGrayscale,AgentCompute, sharedAgentPasses.hlsl(AGENT_CLIP/AGENT_DISPLACE) andAgentNoise.hlsl. Graph route:ShaderGraphJobs.ImportPackageSample,DuplicateGraph,shadergraph_set_target_settings,GeneratedCode,AgentCustomLighting.hlsl. GATE: lint zero errors. - Compile for the target.
ShaderJobs.ValidateShaders(ShaderUtil.GetShaderMessagesplusCompileVariantper pass for Metal). GATE: zero errors, the four opaque passes present. - Bench and look.
SetupMaterials,ShaderLab.BuildScene,ShaderLab.Shots(many shots per boot: material, renderer, feature and URP changes; probes projected to pixels). GATE:ut_reviewflags clean, contact sheet opened, probe numbers (shadow darker than open ground, dissolve shadow gone at 1, extra lights change the lit side). - Full-screen effects.
RendererFeatureJobs.AddFeaturewithAgentFullscreenFeatureor the zero-codeFullScreenPassRendererFeature. GATE: on/off captures differ as intended; no back-buffer error. - Variants.
VariantJobs.StrippingSettings(strict matching on),BuildShaderBundlefor the shipping materials,parse_variant_funnel,variant_budget_check, strip rules orKeywordOverrides(Shader Build Settings),PostProcessingAudit(every VolumeProfile under Assets/ keeps its post variants). GATE: shipped count per shader under budget; logger total equals Unity's "After scriptable stripping"; overrides cleared or committed on purpose. - First-use stutter.
PsoJobs.BuildPsoScene,ut_run.build(development=True),ut_shaders.run_pso_playertrace, cold, warm;PsoJobs.InspectCollection; shipStreamingAssets/PSO/<platform>_<api>.graphicsstateread byAgentPsoProbe. GATE:pso_gateclean (cold shows uploads, warm shows none by marker and by log). - Cost.
GpuCostJobs.MeasureFullscreenCostA/B, interleaved, async compilation off (precision_modelfor half tests). GATE: faster in ms beyond noise; device numbers from a development player (scenario-unity-performance). - Deliver. Shaders, materials, feature assets,
.graphicsstateper API, and the evidence packet (Handoffs).
Numbers
| Value | Relative to | Source |
|---|---|---|
| 7,688 full; 4,046 per stage; 62 after settings; 13 shipped | AgentKit/Lit in a macOS AssetBundle build with the template's URP assets | observed |
| 13 to 11 | AgentLit shipped variants after excluding the soft shadow set in Shader Build Settings | observed |
| 433 to 577 | UberPost variants when one unused Bloom HQ + dirt profile sits under Assets/ | observed |
| 14 to 0; 1.8 to 2.4 ms | first-use GPU program uploads in gameplay, cold vs warm; warm-up of 26 to 27 PSOs in one frame, macOS player | observed |
| 0.007 to 0.033 / 0.29 to 0.42 / 0.89 to 1.66 ms | one 1080p full-screen draw: 64 multiply-adds / 64 sin / 64 float4 sin, M5 Max; compare within one run | observed |
| 0.92 to 1.00 / 0.77 to 0.89 | half4 over float4 sin time, PlatformDefault / Unified precision model, M5 Max (noise about 10 %) | observed |
| more than 128 keywords, 4 reserved | per shader: compile slows | 6.3 Manual |
| 2000 / 2450 / 3000 | Geometry / AlphaTest / Transparent queues | Cyanilux |
| 64x1x1, 8x8x1 | compute thread groups; ceil-divided dispatch plus bounds check | 6.3 Manual, [added] |
| (1.2, 0.35, 0.03) vs (4, 1.1, 0.15) | HDR dissolve edge that stays orange vs washes to cream under Neutral tonemapping | observed |
| 0.2126, 0.7152, 0.0722 | Rec. 709 luminance on linear color | 6.3 Manual |
Quality gates
- Measurable: lint and
lint_project_keywordszero errors;ValidateShaderszero errors for the target API; SRP Batcher compatible (read after a render); probes (shadow luma under 0.75x open ground; dissolve shadow restored at 1; extra lights change the lit side in Forward and Forward+; water normals calmer at the shore than deep); feature on/off difference; shipped variants under budget and stable; no stray VolumeProfile;pso_gateclean per API; GPU ms A/B; compute GPU equals CPU (max error below 1e-5). - Visual: contact sheet opened; toon bands sharp; water hue shift, calm shore, foam, motion; dissolve edge keeps its hue; outline on silhouettes only; no magenta.
Common mistakes
| Mistake | What it looks like | Fix |
|---|---|---|
| no DepthOnly pass | object vanishes with Depth Priming Forced | AgentDepthOnlyVertex/Fragment |
| clip only in the forward pass | dissolved object still casts a full shadow | AGENT_CLIP shared by all passes |
DepthNormals returns n * 0.5 + 0.5 | wrong SSAO and outlines | return the raw normal |
LIGHT_LOOP_BEGIN without a directional pre-loop | a second directional light lights nothing under Forward+ | loop URP_FP_DIRECTIONAL_LIGHTS_COUNT first |
_FORWARD_PLUS, SHADOWS_SCREEN, ComputeScreenPos (deprecated since URP 11) | compile warning, dead shadow branch | _CLUSTER_LIGHT_LOOP, GetMainLight(shadowCoord), GetNormalizedScreenSpaceUV(positionCS) |
shader_feature_local toggled from runtime C# (dissolve "on hit") | variant missing in the player: pink or closest match | material swap, or multi_compile_local; lint_project_keywords |
| a Boolean keyword to skip Shader Graph Alpha Clipping | whole object alpha-tested in every pass, all the time | Allow Material Override (_ALPHATEST_ON keyword) or a second opaque material |
dynamic_branch Type Override on an #if-tested keyword | variant count unchanged | test with if(), declare without a stage suffix |
if (KW) on a _fragment-suffixed keyword | "a known keyword but not declared in this pass" | declare it for all stages |
| stray VolumeProfile assets (samples, tests) | post variants for effects no scene uses | move them out of Assets/; PostProcessingAudit |
| one PSO collection for every API, or none proven | first-use hitches on device | per-API trace, cold vs warm runs |
| Depth Texture off on the URP asset | water turns to solid foam; hidden while SSAO still requests depth | enable on every URP asset that renders it; test with SSAO off |
| feature missing from one renderer | "the effect does nothing" on one quality level | ListFeatures across quality levels |
| After Rendering injection | nothing drawn, back-buffer error | After Rendering Post Processing |
| HDR edge color 4 and up | emissive edge washes to cream | 1 to 1.5, Bloom carries the glow |
| judging cost by instruction count, or half on desktop | "optimized" shader no faster | MeasureFullscreenCost; half on the device |
properties outside UnityPerMaterial | SRP Batcher off, values bleed | one CBUFFER in HLSLINCLUDE |
Handoffs
- Receives from scenario-unity-rendering-lighting: URP assets per quality level, rendering path, Depth/Opaque Texture, post stack, tonemapping, the Default Volume Profile. From scenario-unity-vfx: particle shader needs (blend mode, soft particles, flipbooks); scenario-unity-vfx owns VFX Graph, the Particle System and effect budgets. From scenario-unity-world-building: water and terrain surfaces. From scenario-unity-architecture: gameplay code that drives materials.
- Delivers to scenario-unity-vfx and scenario-unity-world-building: shaders, materials with documented references, feature assets. To scenario-unity-performance: GPU ms tables, variant funnels,
.graphicsstatefiles with the cold/warm proof for device captures. To scenario-unity-pipeline-automation: the variant budget and PSO gates for CI. To scenario-unity-mobile and scenario-unity-web: API limits (compute from GLES 3.1, none on GLES 3.0 or WebGL 2), half precision to profile on device, mobile Depth Texture cost, alpha-test cost on tile GPUs. - Packet: paths,
ValidateShadersJSON, contact sheet, probes, funnel, PSO runs, cost table, Verified and Assumed.
Unity 6.3 notes
- Render Graph only: Compatibility Mode removed (
URP_COMPATIBILITY_MODEonly to port, gone in 6.4);AddBlitPassreturns a builder (returnBuilder: true); After Rendering always follows the final blit (6.2+). _CLUSTER_LIGHT_LOOPreplaced_FORWARD_PLUS(6.1); soft shadows have_LOW/_MEDIUM/_HIGHvariants; fog is declareddynamic_branchin URP'sFog.hlsl.- Shader Build Settings:
EditorGraphicsSettings.Get/SetShaderBuildSettings(the active set; a build profile can override).GraphicsStateCollectionis experimental; tracing needs a development player; on MetalCreateGraphicsGraphicsPipelineImpldoes not exist. Log Shader Compilation printsUploaded shader variant to the GPU driver: .... - URP re-adds every override to the Default Volume Profile before a build, so most post-processing variant families always ship.
SetShaderUserValue(uint)exists onMeshRendererandSkinnedMeshRenderer, notRenderer; the shader readsunity_RendererUserValue.- Shader Graph 17.3: templates, custom lighting via Unlit plus Keep Lighting Variants; reflected HLSL nodes need 17.5, Preprocessor Directives arrive in 6.6. WebGPU is supported from 6.6; Built-in RP is deprecated from 6.5.
References
references/procedures.md: P1 to P19, copyable code with live test and result.references/expert-notes.md: principles by expert, observed findings, disagreements.references/critique.md: the rubric before calling work done.references/gui-paths.md: the same procedures in the GUI.references/sources.md: sources, credentials, timestamps, revision history.scripts/ut_shaders.py: install, lints, variant and PSO helpers, Shader Graph reader and editor, pixel probes.scripts/AgentKit/Shaders/:ShaderJobs,ShaderLab,RendererFeatureJobs,VariantJobs,PsoJobs,ComputeJobs,GpuCostJobs,ShaderGraphJobs.scripts/Runtime/:AgentFullscreenFeature.cs,AgentPsoProbe.cs(runtime assembly);scripts/Shaders/(templates).
Signals
- GitHub stars
- 681
- Forks
- 82
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
scenario-unity-shaders- Source
- github.com/scenario-labs/skills