关于 defold-shaders-editing
用于创建和编辑 Defold GLSL 着色器文件,涵盖顶点与片段程序、可复用代码片段、现代语法,以及着色器与材质的关联。
主要功能
- 01编辑 .vp、.fp 和 .glsl 文件
- 02提供现代 GLSL 语法指引
- 03说明着色器与材质的数据绑定
- 04介绍跨平台编译限制
适用场景
- 01创建 Defold 顶点与片段着色器
- 02向项目添加可复用 GLSL 片段
- 03使着色器变量匹配材质设置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ indiesoftby/ defold-agent-config --skill defold-shaders-editing安装后在 Claude Code 中输入/defold-shaders-editing确认已加载。
详细介绍
编辑 Defold 着色器
创建和编辑 Defold 着色器文件:顶点程序(.vp)、片段程序(.fp)和 GLSL 包含代码片段(.glsl)。
适用场景
此技能涵盖 Defold 渲染管线中使用的 GLSL 着色器文件。不涵盖 .material 文件——编辑此类文件请使用 defold-proto-file-editing 技能(参见 references/material.md)。
着色器管线和 GLSL 版本
Defold 支持两种着色器管线:
- 旧版管线 — 使用兼容 OpenGL ES 2.0 的 GLSL 编写的着色器(没有
#version指令)。自 Defold 1.9.2 起已弃用。 - 现代管线 — 使用兼容 SPIR-V 的 GLSL 编写的着色器,版本为
#version 140或更高。这是当前标准。
始终使用 #version 140 编写着色器(OpenGL 3.1),并将其放在文件顶部。此指令会在构建过程中选择现代管线。如果没有找到 #version,Defold 将回退到旧版管线。
#version 140
交叉编译和平台目标
Defold 会从单个 GLSL 源文件为多个图形 API 编译着色器:
- OpenGL 3.x / 4.x(桌面端:Windows、macOS、Linux)
- OpenGL ES 2.0 / 3.0(移动端:Android、iOS)
- WebGL 1.0 / 2.0(HTML5)
- SPIR-V(Android 和桌面端上的 Vulkan)
- Metal(iOS、macOS — 通过 SPIR-V 交叉编译)
由于存在交叉编译,并非所有 GLSL 特性都能在所有平台上使用。某些函数(例如 dFdx、dFdy、fwidth)在 ES 2.0 / WebGL 1.0 目标上需要扩展,但在 ES 3.0+ / 桌面端 GL 上是内置的。使用 #extension 和预处理器保护来实现条件特性:
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
#if !defined(GL_ES) || __VERSION__ >= 300 || defined(GL_OES_standard_derivatives)
// Use derivative functions
#else
// Provide fallback
#endif
请牢记以下主要限制:
- 带可变边界的动态循环可能无法在 ES 2.0 / WebGL 1.0 上运行
- ES 2.0 对整数运算的支持有限
sampler2DArray需要 ES 3.0+ / WebGL 2.0+- 存储缓冲区和计算着色器需要 Vulkan / Metal
文件类型
顶点程序(.vp)
每个顶点运行一次。将顶点位置从模型/世界空间变换到屏幕空间。输出 gl_Position,并通过 out 变量向片段着色器传递数据。
片段程序(.fp)
每个片段(像素)运行一次。计算最终颜色。输出到用户定义的 out vec4 变量(而非 gl_FragColor,后者在 #version 140 中已弃用)。
GLSL 包含代码片段(.glsl)
可复用的 GLSL 代码,通过 #include 由 .vp 或 .fp 文件包含。不能独立运行。使用头文件保护以防止重复包含。
现代 GLSL 语法规则(#version 140)
Attribute → in
在顶点着色器中,使用 in 替代 attribute:
in highp vec4 position;
in mediump vec2 texcoord0;
片段着色器没有顶点属性。
Varying → out / in
在顶点着色器中,使用 out 替代 varying。在片段着色器中使用 in:
// vertex shader
out mediump vec2 var_texcoord0;
// fragment shader
in mediump vec2 var_texcoord0;
片段输出 → out vec4
使用已声明的 out 变量,而非已弃用的 gl_FragColor:
out vec4 out_fragColor;
void main()
{
out_fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
Uniform 块
非不透明 uniform(矩阵、向量、浮点数)必须放在 uniform 块中。按惯例,顶点着色器使用 vs_uniforms,片段着色器使用 fs_uniforms:
uniform vs_uniforms
{
highp mat4 view_proj;
};
不透明 uniform(采样器、图像)仍保持独立:
uniform mediump sampler2D texture_sampler;
可直接按名称访问 uniform 块的成员(无需块前缀):
gl_Position = view_proj * vec4(position.xyz, 1.0);
纹理采样 → texture()
使用 texture() 替代已弃用的 texture2D() / texture2DArray():
vec4 color = texture(texture_sampler, var_texcoord0.xy);
精度限定符
在 #version 140 中,显式精度(lowp、mediump、highp)为可选项——管线会自动为需要精度限定的平台设置精度。不过,你仍可使用它们来提升清晰度,或与项目中现有代码风格保持一致。请遵循相邻文件的约定。
编辑器专用代码(EDITOR 定义)
在 Defold Editor 视口中渲染着色器时,预处理器定义 EDITOR 可用。使用 #ifdef EDITOR 编写在编辑器与游戏中表现不同的代码:
#ifdef EDITOR
// Simplified rendering for editor preview
out_fragColor = texture(texture_sampler, var_texcoord0.xy);
#else
// Full rendering with effects for the game
out_fragColor = apply_effects(texture(texture_sampler, var_texcoord0.xy));
#endif
常见用例:
- 在编辑器中禁用开销较大的效果(RGSS、后期处理),以提升性能
- 为预览效果不佳的材质显示调试或回退视觉效果
- 跳过依赖仅在运行时可用的数据的功能(例如,蒙皮)
包含代码片段(#include)
着色器文件可以使用 #include,通过相对于项目或文件的路径包含 .glsl 代码片段:
// Absolute (project-relative) path
#include "/main/my-snippet.glsl"
// Relative to current file
#include "my-snippet.glsl"
#include "../shared/utils.glsl"
规则:
- 只能包含
.glsl文件 - 路径必须位于项目内(或库依赖项中)
- 绝对路径以
/开头 #include不能在语句内部使用
头文件保护
在 .glsl 代码片段中使用头文件保护,以防止重复包含:
#ifndef MY_SNIPPET_GLSL
#define MY_SNIPPET_GLSL
// ... snippet code ...
#endif // MY_SNIPPET_GLSL
与材质的关系
着色器与材质(.material)紧密耦合。材质文件定义着色器接收什么数据。编辑材质文件时,请使用 defold-proto-file-editing 技能(参考:references/material.md)。
常量(uniform)
在材质的 vertex_constants / fragment_constants 中声明的常量会成为着色器中的 uniform 变量。请将它们放在 uniform 块中:
| 材质常量类型 | 着色器 uniform 类型 | 描述 |
|---|---|---|
CONSTANT_TYPE_VIEWPROJ | mat4 | 视图 × 投影组合矩阵 |
CONSTANT_TYPE_WORLD | mat4 | 世界变换矩阵 |
CONSTANT_TYPE_VIEW | mat4 | 视图(摄像机)矩阵 |
CONSTANT_TYPE_PROJECTION | mat4 | 投影矩阵 |
CONSTANT_TYPE_WORLDVIEW | mat4 | 世界 × 视图矩阵 |
CONSTANT_TYPE_WORLDVIEWPROJ | mat4 | 世界 × 视图 × 投影矩阵 |
CONSTANT_TYPE_NORMAL | mat4 | 法线矩阵 — transpose(inverse(view * world))。生成的是视图空间法线,而非世界空间法线。 |
CONSTANT_TYPE_USER | vec4 | 自定义数据,可通过 go.set() / go.animate() 修改 |
CONSTANT_TYPE_USER_MATRIX4 | mat4 | 自定义矩阵数据,可通过 go.set() 修改 |
材质中的 name 必须与 uniform 块中的变量名一致。
采样器
在材质的 samplers 部分中声明的采样器会成为 sampler2D uniform。材质中的采样器 name 必须与着色器中的 uniform 名称一致:
uniform mediump sampler2D texture_sampler; // Matches samplers { name: "texture_sampler" ... }
对于精灵、瓦片地图、GUI 和粒子,系统会自动将第一个 sampler2D 绑定到组件的图像。
顶点属性
在材质的 attributes 部分中声明的属性(或引擎提供的默认属性)会成为顶点着色器中的 in 变量。
各组件类型的默认属性:
| 组件 | 属性 |
|---|---|
| 精灵 | position、texcoord0 |
| 瓦片地图 | position、texcoord0 |
| GUI 节点 | position、texcoord0、color |
| ParticleFX | position、texcoord0、color |
| 模型 | position、texcoord0、normal |
| 字体 | position、texcoord0、face_color、outline_color、shadow_color |
顶点空间
材质的 vertex_space 设置会影响位置数据的传入方式:
VERTEX_SPACE_WORLD(默认)——位置已预先变换到世界空间。用于 2D 组件(精灵、瓦片地图)。使用view_proj(或CONSTANT_TYPE_VIEWPROJ)直接变换到屏幕空间。VERTEX_SPACE_LOCAL——位置处于局部/对象空间。用于 3D 模型。必须在着色器中依次经过world → view → projection进行变换。
实例化
对于实例化渲染(Model 组件),请将 mtx_world 和 mtx_normal 声明为 in 属性(而非 uniform):
in mediump mat4 mtx_world;
in mediump mat4 mtx_normal;
材质必须设置 vertex_space: VERTEX_SPACE_LOCAL。这些属性会自动配置为逐实例步进函数。
法线矩阵(mtx_normal)——视空间与世界空间
内置的 CONSTANT_TYPE_NORMAL 会在 CPU 上计算 transpose(inverse(view * world))。这会生成视空间(相机空间)法线,而非世界空间法线。
适合使用视空间法线的情况:
- 所有光照计算都在视空间中进行
- 相机位置隐式设为原点
(0,0,0)(可简化镜面反射计算)
需要使用世界空间法线的情况:
- 立方体贴图反射、环境映射
- 世界空间光照、世界空间效果
要获取世界空间法线,可在顶点着色器中使用伴随矩阵技巧从 mtx_world 计算法线矩阵(比完整的 inverse() 更快——使用 3 次叉积,而非余子式展开):
// transpose(adjugate(M)) for upper-left 3x3 of mat4.
// Equivalent to transpose(inverse(M)) up to a uniform scale factor,
// which is eliminated by normalize().
mat3 adjoint(mat4 m)
{
return mat3(
cross(m[1].xyz, m[2].xyz),
cross(m[2].xyz, m[0].xyz),
cross(m[0].xyz, m[1].xyz)
);
}
在顶点着色器中的用法:
var_world_normal = normalize(adjoint(mtx_world) * normal.xyz);
| 目标 | 方法 |
|---|---|
| 视空间法线 | 使用内置的 mtx_normal(CONSTANT_TYPE_NORMAL) |
| 世界空间法线 | 在着色器中计算 normalize(adjoint(mtx_world) * normal.xyz) |
重要规则: 切勿混用坐标空间——如果光照方向位于世界空间,法线也必须位于世界空间。
典型示例
内置参考着色器
将 .deps/builtins/materials/ 用作标准着色器的参考:
sprite.vp/sprite.fp— 2D 精灵(世界空间、view_proj、tint)model.vp/model.fp— 带光照的 3D 模型(局部空间、mtx_worldview、mtx_normal、light)model_instanced.vp— 带实例化的 3D 模型(mtx_world、作为in属性的mtx_normal)gui.vp/gui.fp— GUI 节点(世界空间、color属性、预乘 Alpha)particlefx.vp/particlefx.fp— 粒子效果tile_map.vp/tile_map.fp— 瓦片地图skinning.glsl— 骨骼动画包含文件(使用#ifdef EDITOR作为回退方案)
GLSL 包含代码片段(带头文件保护和扩展)
#ifndef MY_UTILS_GLSL
#define MY_UTILS_GLSL
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
// Utility function available on platforms that support derivatives
#if !defined(GL_ES) || __VERSION__ >= 300 || defined(GL_OES_standard_derivatives)
mediump float edge_smoothing(mediump float dist)
{
return smoothstep(0.0, fwidth(dist), dist);
}
#else
mediump float edge_smoothing(mediump float dist)
{
return step(0.0, dist);
}
#endif
#endif // MY_UTILS_GLSL
工作流
创建一对新的着色器文件(.vp + .fp)
- 确定组件类型(精灵、模型、GUI 等),以了解应使用哪些属性和顶点空间。
- 查看对应的
.material文件(或规划一个文件),以了解着色器会接收哪些常量、采样器和属性。编辑材质时使用defold-proto-file-editing技能。 - 两个文件都以
#version 140开头。 - 在
.vp中:声明in属性、out易变变量、包含常量的 uniform 块,并计算gl_Position。 - 在
.fp中:声明in易变变量、用于颜色输出的out vec4、采样器和 uniform 块,并计算输出颜色。 - 确保 uniform 名称与材质中的常量名称完全一致。
- 确保采样器名称与材质中的采样器名称完全一致。
创建 GLSL 包含代码片段
- 创建
.glsl文件。 - 添加头文件保护(
#ifndef/#define/#endif)。 - 如果使用并非所有目标平台都支持的功能,则添加带预处理器保护的
#extension指令。 - 编写可复用的函数或常量。
编辑现有着色器
- 阅读当前着色器文件及其对应的
.material文件。 - 只修改所请求的部分。
- 保持现有代码风格(精度限定符、命名、空格)。
- 确保将任何新增的 uniform 也添加到材质文件中(使用
defold-proto-file-editing技能)。 - 确保将任何已删除的 uniform 也从材质文件中移除。