关于 scenario-godot-shaders
面向使用 Godot 4.7.2 的技术美术和编码 Agent 的着色器技能。它通过多渲染器编译与绘制测试、参数扫描和 GPU 成本测量来验证着色器。
主要功能
- 01按渲染器检查并编译着色器
- 02在场景元素上绘制测试
- 03扫描参数并查看联系表
- 04与材质基准比较 GPU 成本
- 05构建 VisualShader 图与计算流程
适用场景
- 01验证着色器在目标渲染器上的表现
- 02检查水面、溶解或后处理效果
- 03将效果与 StandardMaterial3D 基准比较成本
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-godot-shaders安装后在 Claude Code 中输入/scenario-godot-shaders确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/gd_shaders.pyscripts/agentkit/shaders/dissolve_swap.gddissolve.gdshaderfullscreen_depth.gdshadergame_time.gdhit_flash.gdshaderportal_reader.gdshaderportal_writer.gdshadershader_tools.gdsky_toon.gdshaderwater_common.gdshaderincwater_lite.gdshaderwater_stylized.gdshaderscripts/agentkit/shaders/examples/animate_instance_uniform.gdbuild_dissolve_scene.gdbuild_flash_scene.gdbuild_fx_scenes.gdbuild_global_scene.gdbuild_visual_shader.gdbuild_water_scene.gdcompile_probe.gdcompute_texture.gdfullscreen_old_trap.gdshader.txtrender_probe.gdvs_ports.gd详细介绍
Godot 着色器(技术美术、着色)
目标:Godot 4.7.2.stable,macOS Apple Silicon(默认使用 Metal,通过 MoltenVK 使用 Vulkan 进行 GPU 计时)。
此处的专家级要求是:每个着色器都是 Agent 可比较差异的文本;在每种目标渲染器下编译,在窗口中绘制到真实网格上,对其参数进行遍历并整理成联系表,并将其 GPU 开销与 StandardMaterial3D 基准进行比较。编译成功不代表能正常工作:有些 4.7.2 故障只会在后端绘制时出现,还有些错误完全不会报错。如果这里提到的同级技能不在你可用的技能中,请让用户安装它(npx skills add scenario-labs/skills --skill <name>);若无法等待,请根据工具架构继续操作,并指出这一缺失。
必备背景: scenario-godot-expert(通道、gd_run、审查循环、4.7 陷阱)。
立场(专家差异)
- 优先写代码,按需使用图表。 文本着色器支持循环、函数、
#include,差异也清晰 (Godotneers、Shaderland [01:10:09];Daniel Ilett 谈 VisualShader 的不足)。如果需要图表, 就用代码构建 VisualShader(add_node、connect_nodes,数学运算使用 Expression 节点), 并将其保存为.tres;端口名称不会向 GDScript 暴露,因此输出索引需要硬编码,并通过读取get_code()检查 [新增,运行于 2026-10-02]。 - 先无头编译,再开窗绘制。 无头调用
get_shader_uniform_list(),可针对每个--rendering-method捕获语言错误,每个渲染器耗时约 0.4 s。透明通道中的模板缓冲区读取 和仅在 Compatibility 下使用的雾效着色器,只有窗口实际绘制时才会出错。未初始化的局部变量 以及只在一个分支中写入的DEPTH则完全不会报错,因此 Linter 会检查这些情况 [新增,运行于 2026-10-02]。 - 世界空间或对象空间优于网格 UV。 溶解、风、水和三平面效果应由对象空间或世界空间坐标
驱动,这样接缝和 UV 拉伸就会消失(StayAtHomeDev 溶解效果 [00:09:59]、DevPoodle 三平面、
Dylearn 草地)。对于共享材质,应将每个对象的值(边界、量)放入
instance uniform,否则 每个网格的溶解高度都会出错 [新增]。 - 水面不能滑动。 使用同一无缝细胞噪声的两个平移副本,将它们相乘,再提升到 1.25 次方,
并让
vertex()和fragment()共用,这样波峰就会位于波浪顶部(Bramwell [00:11:57 至 00:20:57])。使用混合卡通色带,只在每个分界附近应用 smoothstep,这样色带就不会 闪烁(Dylearn [00:13:42])。岸边泡沫由垂直深度决定,该深度通过反向 Z 公式从深度纹理重建 [新增]。 - HDR 边缘需要同时具备三项条件: 高于 1.0 的颜色、EMISSION 输出,以及 WorldEnvironment 中的 Glow(StayAtHomeDev [00:07:36]、Daniel Ilett [00:10:22]、Pixezy [00:06:55])。
- 了解哪些操作会让材质进入透明通道。 写入
ALPHA或读取屏幕或深度纹理,都会让材质进入 透明通道。它会失去阴影和排序,并且对其他读取屏幕的材质不可见(文档)。溶解效果使用ALPHA_SCISSOR_THRESHOLD:材质会保持不透明,阴影也会随之溶解。Scissor 仍然是 alpha 测试: 与discard一样,它会让该网格无法使用 early-Z(文档);这在移动端 tile GPU 上代价最大, 因此仅在需要该效果时才切换溶解材质(P18)。模板缓冲区读取器必须是透明材质,且其render_priority高于写入器(LowQualityCoding [00:07:35])。 - Compute:用于屏幕显示时,留在主设备上。 Texture2DRD 需要在
RenderingServer.call_on_render_thread内使用主 RenderingDevice,并且不要提交或同步 (The Pathfinders Codex [00:03:07])。若要将结果取回 CPU,应使用本地设备、释放每个 RID, 并与 CPU 版本进行一致性测试(DevPoodle [00:02:21]、Crigz [00:11:24])。 - 从有窗口的导出中烘焙着色器。 只有导出进程拥有渲染设备时,着色器烘焙器才会运行。使用
shader_baker/enabled=true执行--headless导出时,不会报错,但也不会额外打包任何内容 [新增,运行于 2026-10-02]。
先确定
- 目标渲染器: Forward+(桌面)、Mobile(手机)、Compatibility(网页、旧款 GPU);每种 渲染器支持的提示和阶段各不相同。默认:Forward+ 加一种命名的备用渲染器。
- 风格: 卡通、PBR 或像素艺术。确定色带数量、描边方式(模板缓冲区或反转外壳),以及是否 启用 Glow。
- 预算: 每种效果相对于 StandardMaterial3D 基准的 GPU 毫秒数。默认:在这台 Mac 上以 1080p 运行主打效果时,额外耗时低于 +0.5 ms,并在目标设备上确认。
- 由谁编辑: Agent(文本),还是也由人类美术师编辑(VisualShader
.tres、带提示的分组 uniform)。 - 共享还是独立材质: 每个实例的值决定使用 instance uniforms(最多 16 个,不含纹理)、 独立材质还是全局变量。
工作流
- 安装并检查代码规范。
gd_shaders.install(project)会将模板复制到res://shaders/,并将工具复制到addons/agentkit/shaders/。然后运行gd_shaders.lint(path, renderers, project=P)。门槛:不得有error发现项;每个warn都必须修复或说明理由。 - 针对每个渲染器编译。
gd_shaders.compile_matrix(P, root="res://shaders/")。门槛:每个目标渲染器的结果均为ok,且failed列表为空。 - 针对每个渲染器绘制一次。
gd_shaders.draw_check(P, files)会将每个着色器应用到已绘制的网格、画布项、天空、粒子系统或雾体积上。门槛:每次运行的shader_errors和engine_errors均为 0。 - 用代码构建测试场景。 编写一个任务,保存包含摄像机、灯光和明确设置的 Environment 的
.tscn:根据需求设置 AgX 和 Glow 开或关。水效果需要有穿过水位的地面,以及位于水面上方和下方的物体。溶解效果至少需要三个大小和旋转角度各不相同的网格。门槛:场景以无头模式加载时结果为ok,且已检查 project.godot 中的渲染器和 Jolt 设置。 - 扫描并查看效果。 运行
gd_shaders.param_sweep(P, scene, shots=...),使用shader、instance、global或prop类型。打开联系表。门槛:changes的表现符合预期(例如溶解单调变化、相位移动),且每张截图都呈现预期效果。 - 比较渲染器。 运行
gd_shaders.renderer_captures(P, scene)。门槛:目标渲染器均能保持预期效果;记录已知差异(相同 Environment 下,Compatibility 渲染效果更亮、饱和度更高)。 - 成本。 在 Vulkan、1080p 下运行
gd_shaders.cost_compare(P, {"standard": ..., "hq": ..., "lite": ...})。门槛:与基线相比的差值在预算范围内;移动端数据标记为“desktop proxy”。 - 交付。 添加 macOS 或目标预设,并设置
shader_baker/enabled=true。通过窗口化进程导出一次(参见 procedures.md P14)。门槛:日志显示“Baking shaders (N steps)”,且包体增大。缺失的全局 uniform 会在此处显示为错误。
其他路径:
- 供人类美术人员使用的 VisualShader(P9):使用 Expression 节点通过代码构建图,并将其保存为
.tres。使用shader_tools.gd:visual_shader读回,它会返回生成的代码、节点数和 uniform。然后像处理文本着色器一样扫描。门槛:生成的代码包含所有预期的输出赋值。 - Compute(P12、P13):以窗口化模式运行
compute_probe,进行 CPU 一致性检查。使用 Texture2DRD 任务输出屏幕内容。门槛:不一致项为 0、无泄漏的 RID,且帧会发生变化。 - 全局变量(P11):使用
register_global注册全局变量(它会写入编辑器的多行格式)。使用RenderingServer.global_shader_parameter_set设置实时值,并在自动加载脚本中保存一份副本:getter 会阻塞渲染线程(参见文档)。运行时的ProjectSettings.set_setting不会注册全局变量。 - 参数动画: Tween 或 AnimationPlayer 轨道上的
instance_shader_parameters/dissolve_amount可驱动实例 uniform;shader_parameter/<name>可驱动物料 uniform(P8)。
专家检查清单(在计划中说明这些内容)
- 交付物中的完整代码。 粘贴完整模板
(
dissolve.gdshader、water_stylized.gdshader及其 include) 和设置脚本:读者可能没有这套工具包。 - 每个颜色 uniform 和颜色采样器都使用
source_color, 法线、粗糙度或蒙版 数据则不能使用(lint规则source_color)。 - Uniform 预算: 移动端为 16,384 B,vec3 会填充到 16 B;大型数组应放入纹理(
lint规则uniform_budget)。 TIME每 3,600 s 回绕一次,并且暂停时仍会继续运行(文档):需要支持暂停的运动应由game_time.gd自动加载脚本写入的global uniform float game_time驱动(P18)。- 溶解边界。 网格移动或缩放时,世界空间方案会失效;StayAtHomeDev 使用
@tool脚本传入 AABB 最小值和最大值(pIwQ 09:59 至 12:52)。模板使用对象空间,并为每个网格设置一个instance uniform vec2 bounds, 因此在移动、缩放和共享时都能正常工作。 - 替换为该效果。
dissolve_swap.gd:swap_in(mi, mat)会为每个表面放置一个副本 (保留每个表面的反照率颜色和纹理),并返回原始材质供restore使用(P18)。 - 排序。
depth_prepass_alpha配合cull_disabled可修复透明排序问题,并保留阴影(pIwQ 00:01:00)。 LIGHT_VERTEX(4.3)可移动受光位置,例如将其吸附到网格以实现阶梯式像素艺术光照,且无需修改VERTEX(Dylearn 00:01:25);该效果可在全部三种渲染器上绘制(P18)。- 轮廓。 在高密度网格上使用 BaseMaterial3D
stencil_mode轮廓;其他情况下使用反转外壳的 next pass(Daniel Ilett 15:18)。
数值
| 数值 | 数值 | 相对于 |
|---|---|---|
| 无头编译,3 个渲染器,11 个着色器 | 1.3 秒 | M5 Max,运行于 2026-10-02 |
| 首次窗口化绘制(管线构建) | Metal 7.5 秒,Mobile 5.3 秒,Compat 1.6 秒 | 冷缓存,同一组探测 |
| 1080p 下水体 GPU p50 | 标准版 2.9 至 3.2 毫秒,HQ 3.4 至 3.5,LITE 3.0 至 3.1 | Vulkan,运行 3 次,完整帧 |
| HQ 水体(折射)相对基准的开销 | +0.35 至 0.5 毫秒 | 同一场景,StandardMaterial3D 水体 |
| Uniform 缓冲区 | 桌面端 65,536 B,移动端 16,384 B;vec3 填充至 16 B | 官方文档 |
| 每个着色器的实例 Uniform 数量 | 16,不含采样器或数组 | 文档;编译器会拒绝采样器 |
| 水体噪声 | 两个平移相乘,pow 1.25 | Bramwell |
| 溶解边缘 | 宽度 0.06,发光 x6,启用 Glow | 模板默认值 [已添加] |
| 计算着色器,1M 个浮点数,提交 + 同步 | Metal 1.2 毫秒,Vulkan 1.9 至 2.4 毫秒 | 本地 RD,窗口化 |
| 本项目的着色器烘焙器 | 709 步,打包后从 4.9 MB 增至 18.3 MB,首次烘焙 19 秒,之后为 4 至 12 秒 | 窗口化导出,Metal 在没有 Xcode 工具时仅限 SPIR-V |
TIME | 每 3,600 秒回绕,不受暂停影响 | 文档 |
| 着色器驱动的锁步同步 | 不同 GPU 厂商之间运行约 60,000 帧后出现一个像素的偏差 | Leszek Nowak [00:23:39] |
质量门槛
- 可测量:
lint没有错误;compile_matrix和draw_check按目标渲染器检查时显示零个着色器错误;扫描结果changes符合预期(第一帧为 0,溶解时单调变化,移动相位时高于 0.005);像素探针命中预期颜色;cost_compare在预算范围内;计算着色器的mismatches == 0且没有leaks;烘焙器日志显示步数。 - 视觉(打开图表):
- 水体:深度渐变,每处岸边都有泡沫环,波纹会移动,不会出现平铺滚动;水下物体会染色,干燥的岩石则不会;
- 溶解:边缘跟随噪声、发光,也会切除阴影;amount 为 1 时没有残留;每个网格独立溶解;
- 后期效果:摄像机转动时覆盖整个屏幕;
- 模板缓冲:平滑网格上的轮廓连续;
- 天空:色带和太阳圆盘的位置由
LIGHT0_DIRECTION决定。
常见错误
| 错误 | 表现 | 修复方法 |
|---|---|---|
Godot 3 名称(SCREEN_TEXTURE、hint_color、WORLD_MATRIX) | 编译错误;错误消息会指出应使用的 uniform | 使用带提示的 uniform 和 MODEL_MATRIX;lint 规则 godot3 |
全屏四边形使用 POSITION = vec4(VERTEX, 1.0) | Forward+ 和 Mobile:效果只显示在天空区域;Compatibility 会掩盖这个问题 | 使用 vec4(VERTEX.xy, 1.0, 1.0)、翻转面四边形、extra_cull_margin 16384 |
在 canvas_item 中使用 texture(TEXTURE, UV) * COLOR | 精灵颜色变暗(通道值从 54 变为 11) | COLOR 已经包含纹理乘以调制色的结果 |
全局 uniform 未列入 [shader_globals] | 运行时编译无错误,但读取的是另一个全局值;烘焙器和编辑器会报告此问题 | 保存着色器前先调用 gd_shaders.register_global |
#include 一个 .gdshader | “Unknown character #35” | 只能包含 .gdshaderinc |
| 在不透明材质中读取模板缓冲区 | 绘制时出现 “reads stencil but is not in the alpha queue”;没有任何内容被遮罩 | 写入 ALPHA(或禁用深度绘制或深度测试),提高 render_priority |
| 在方块上绘制模板轮廓 | 轮廓在角落处断裂(法线被拆分) | 使用密集的平滑网格,或反转外壳 |
多个溶解材质共享一个 bounds | 小网格会同时消失,大网格则永远无法完成 | 为每个网格使用 instance uniform vec2 bounds |
对屏幕纹理使用 textureLod,但未设置 mipmap 过滤器 | 模糊效果仍然锐利 | 使用 filter_linear_mipmap |
在 Mobile 或 Compatibility 上使用 hint_normal_roughness_texture | 在该渲染器下出现编译错误 | 仅用于 Forward+,或添加 #if CURRENT_RENDERER 分支 |
启用着色器烘焙器,使用 --headless 导出 | 打包大小不变,也没有日志 | 从窗口化进程导出 |
shader_parameter/x 上的 Tween 保持默认值 | “Type mismatch ... Nil and float”;Tween 永远不会完成 | 开始 Tween 前先调用一次 set_shader_parameter(未设置的值会读取为 null) |
| 根据捕获帧数测量运动 | 水面在帧与帧之间看起来静止 | 将 wave_speed 作为相位偏移进行扫描 |
使用 TIME 实现与游戏同步的运动 | 在暂停菜单中仍会持续滚动 | 使用来自 autoload 的 game_time 全局变量(P18) |
交接
- 接收: 渲染器、Environment 和 Compositor 需求(scenario-godot-rendering-lighting); 粒子和卡片着色器(scenario-godot-vfx);精灵效果(scenario-godot-2d);可平铺地图 (scenario-textures)。
- 交付:
.gdshader、.gdshaderinc和 VisualShader.tres文件、ShaderMaterial 设置 代码、实例和全局 uniform 名称及其范围、扫描表和成本表。 - 移交给: scenario-godot-performance-export(卡顿、CI 中的烘焙、设备计时);
scenario-godot-animation(
instance_shader_parameters/<name>上的轨道); scenario-godot-rendering-lighting(用于 CompositorEffect 的全屏效果)。
Godot 4.7 说明
- 自 4.3 起采用反向 z(Forward+ 和 Mobile);Compatibility 保留 OpenGL 深度。使用
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY(4.4+)处理 NDC 公式。 - 模板缓冲(4.5,仍处于实验阶段):着色器的
stencil_mode,以及 BaseMaterial3D 的stencil_mode预设(描边、X 光),这些预设会创建自己的next_pass。在 4.7.2 中,它们可在 Forward+ 和 Compatibility 下运行。 - 着色器烘焙器(4.5):一种导出选项;ubershader(4.4)可减少剩余卡顿。
- Texture2DRD(4.2)和
texture_get_data_async(4.4)仍在使用。 - 4.8 或更高版本的变更:此处未涵盖;请重新检查标记为实验性的模板缓冲语法。
参考资料
references/expert-notes.md(专家原则、时间戳、 4.7.2 观察结果)、procedures.md(P1 至 P18、代码、测试、 结果)、critique.md(评估标准)、gui-paths.md(编辑器路径)、sources.md(来源和时间戳)。scripts/gd_shaders.py:安装、lint、uniform 和预算、全局变量、 compile_matrix、draw_check、param_sweep、renderer_captures、cost_compare。scripts/agentkit/shaders/:shader_tools.gd、dissolve_swap.gd、game_time.gd和模板 (water HQ 和 LITE、dissolve、fullscreen_depth、portal writer 和 reader、sky_toon、hit_flash)。
评论
支持 Markdown