mcpField
D
defold-shaders-editing

defold-shaders-editing

github.com/indiesoftby开发10449 次安装27 周前更新
代码仓库

关于 defold-shaders-editing

用于创建和编辑 Defold GLSL 着色器文件,涵盖顶点与片段程序、可复用代码片段、现代语法,以及着色器与材质的关联。

主要功能

  1. 01编辑 .vp、.fp 和 .glsl 文件
  2. 02提供现代 GLSL 语法指引
  3. 03说明着色器与材质的数据绑定
  4. 04介绍跨平台编译限制

适用场景

  1. 01创建 Defold 顶点与片段着色器
  2. 02向项目添加可复用 GLSL 片段
  3. 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 支持两种着色器管线:

  1. 旧版管线 — 使用兼容 OpenGL ES 2.0 的 GLSL 编写的着色器(没有 #version 指令)。自 Defold 1.9.2 起已弃用。
  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_VIEWPROJmat4视图 × 投影组合矩阵
CONSTANT_TYPE_WORLDmat4世界变换矩阵
CONSTANT_TYPE_VIEWmat4视图(摄像机)矩阵
CONSTANT_TYPE_PROJECTIONmat4投影矩阵
CONSTANT_TYPE_WORLDVIEWmat4世界 × 视图矩阵
CONSTANT_TYPE_WORLDVIEWPROJmat4世界 × 视图 × 投影矩阵
CONSTANT_TYPE_NORMALmat4法线矩阵 — transpose(inverse(view * world))。生成的是视图空间法线,而非世界空间法线。
CONSTANT_TYPE_USERvec4自定义数据,可通过 go.set() / go.animate() 修改
CONSTANT_TYPE_USER_MATRIX4mat4自定义矩阵数据,可通过 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
ParticleFXposition、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)
  1. 确定组件类型(精灵、模型、GUI 等),以了解应使用哪些属性和顶点空间。
  2. 查看对应的 .material 文件(或规划一个文件),以了解着色器会接收哪些常量、采样器和属性。编辑材质时使用 defold-proto-file-editing 技能。
  3. 两个文件都以 #version 140 开头。
  4. 在 .vp 中:声明 in 属性、out 易变变量、包含常量的 uniform 块,并计算 gl_Position。
  5. 在 .fp 中:声明 in 易变变量、用于颜色输出的 out vec4、采样器和 uniform 块,并计算输出颜色。
  6. 确保 uniform 名称与材质中的常量名称完全一致。
  7. 确保采样器名称与材质中的采样器名称完全一致。
创建 GLSL 包含代码片段
  1. 创建 .glsl 文件。
  2. 添加头文件保护(#ifndef / #define / #endif)。
  3. 如果使用并非所有目标平台都支持的功能,则添加带预处理器保护的 #extension 指令。
  4. 编写可复用的函数或常量。
编辑现有着色器
  1. 阅读当前着色器文件及其对应的 .material 文件。
  2. 只修改所请求的部分。
  3. 保持现有代码风格(精度限定符、命名、空格)。
  4. 确保将任何新增的 uniform 也添加到材质文件中(使用 defold-proto-file-editing 技能)。
  5. 确保将任何已删除的 uniform 也从材质文件中移除。

评论

支持 Markdown