scenario-godot-shaders
Godot shaders (technical artist, shading)
Target: Godot 4.7.2.stable, macOS Apple Silicon (Metal by default, Vulkan through MoltenVK for GPU timing).
Expert level here means every shader is text the agent can diff, compiled under each target renderer, drawn on a real mesh in a window, swept across its parameters and looked at on a contact sheet, with its GPU cost measured against a StandardMaterial3D baseline. Compiling is not the same as working: several 4.7.2 failures only appear when the backend draws, and several bugs produce no error at all. 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-godot-expert (channels, gd_run, the review loop, 4.7 traps).
Stance (the expert delta)
- Code first, graphs on request. Text shaders give loops, functions,
#includeand clean diffs (Godotneers, Shaderland [01:10:09]; Daniel Ilett on the gaps of VisualShader). When a human wants a graph, build the VisualShader in code (add_node,connect_nodes, an Expression node for the maths) and save it as.tres; port names are not exposed to GDScript, so output indices are hard-coded and checked by readingget_code()[added, run 2026-10-02]. - Compile headless, then draw windowed. Headless
get_shader_uniform_list()catches language errors per--rendering-methodin about 0.4 s per renderer. Stencil reads in the opaque pass and fog shaders under Compatibility only fail when a window draws them. Uninitialized locals andDEPTHwritten in one branch never fail at all, so the linter checks those [added, run 2026-10-02]. - World or object space beats mesh UVs. Dissolve, wind, water and triplanar effects should be driven from object-space or world-space coordinates, so seams and UV stretch disappear (StayAtHomeDev dissolve [00:09:59], DevPoodle triplanar, Dylearn grass). For a shared material, put the per-object values (bounds, amount) in
instance uniforms, or each mesh dissolves at the wrong height [added]. - Water that does not slide. Use two panned copies of one seamless cellular noise, multiply them, raise to 1.25, and share them between
vertex()andfragment()so the crests sit on the wave tops (Bramwell [00:11:57 to 00:20:57]). Use hybrid toon bands, a smoothstep only near each cut, so the bands do not flicker (Dylearn [00:13:42]). Shore foam comes from vertical depth, reconstructed from the depth texture with the reversed-z formula [added]. - HDR edges need three things together: a color above 1.0, the EMISSION output, and Glow in the WorldEnvironment (StayAtHomeDev [00:07:36], Daniel Ilett [00:10:22], Pixezy [00:06:55]).
- Know what moves a material to the transparent pass. Writing
ALPHAor reading the screen or depth texture moves it there. It loses shadows and sorting, and it is invisible to other screen-reading materials (docs). UseALPHA_SCISSOR_THRESHOLDfor dissolves: the material stays opaque and the shadow dissolves too. Scissor is still an alpha test: likediscard, it gives up early-Z for that mesh (docs), which costs most on mobile tile GPUs, so swap the dissolve material in only for the effect (P18). Stencil readers must be transparent, with arender_priorityabove the writer's (LowQualityCoding [00:07:35]). - Compute: on screen, stay on the main device. Texture2DRD needs the main RenderingDevice inside
RenderingServer.call_on_render_thread, and no submit or sync (The Pathfinders Codex [00:03:07]). To get results back on the CPU, use a local device, free every RID and test parity against a CPU version (DevPoodle [00:02:21], Crigz [00:11:24]). - Bake shaders from a windowed export. The shader baker runs only when the export process has a rendering device. A
--headlessexport withshader_baker/enabled=truesilently packs nothing extra [added, run 2026-10-02].