Godot 2D 精灵着色器实战:Sprite Shaders 演示项目逐效果解析

原创2026-10-02 01:21:4920 阅读
文章标签:示例工程

Godot 2D 精灵着色器实战:Sprite Shaders 演示项目逐效果解析

本文基于仓库 2d/sprite_shaders 目录下的官方演示工程撰写。该项目将多种着色器应用到同一张精灵图上,集中展示了 Godot 的 2D 精灵特效能力,涵盖描边(Outline)、发光(Glow)、模糊(Blur)、阴影(Drop/Offset Shadow)、扭曲变形(Fatty)、剪影(Silhouette)、溶解消散(Disintegrate)等典型效果。读完本文,你将掌握这些效果各自对应的 .gdshader 源码实现、关键 uniform 参数的调参含义,以及如何在 Godot 4.x(Compatibility 渲染器)中搭建一个类似的"同图多效果对照"演示场景。

项目概览:同图多效果的着色器陈列馆

仓库根目录 README.md 对项目的定位非常简明:"a sample consisting of different shaders applied to some sprites",即一个由多种着色器作用于精灵图(Sprite)组成的示例。它验证了 Godot 官方在 2D 渲染领域的两个关键技术选型:

  • 语言:全部使用 Godot Shader Language 编写(shader_type canvas_item),面向 2D 精灵的 CanvasItem 着色器;
  • 渲染器:Compatibility(GL Compatibility,即 OpenGL 兼容管线),对应 project.godot 中的配置:
    [rendering]
    renderer/rendering_method="gl_compatibility"
    renderer/rendering_method.mobile="gl_compatibility"
    
    这意味着这些着色器全部运行在兼容性渲染管线之上,不依赖 Vulkan 独有特性,可平滑迁移到移动端或老旧显卡环境。

工程内共包含 10 个精灵着色器文件,全部位于 shaders/ 目录:

着色器文件 效果 核心 uniform 参数
outline.gdshader 描边 outline_width、outline_color
aura.gdshader 光环/外发光轮廓 aura_width、aura_color
blur.gdshader 模糊 radius
fatty.gdshader 膨胀扭曲 fattyness
dropshadow.gdshader 柔边投影 radius、modulate
offsetshadow.gdshader 偏移硬阴影 offset、modulate
silouette.gdshader 剪影/单色填充 modulate
glow.gdshader 自发光光晕 radius、amount
dissintegrate.gdshader 噪点溶解 amount

说明:dissintegrate.gdshader 的拼写是项目原始命名(对应英文 "disintegrate" 的变体写法),文中引用时保持仓库原样。

官方截图如下,画面以同一张角色精灵图为底,按两行排列了 10 种不同效果的实时渲染结果,从左到右依次为原始图、描边、光环、模糊、扭曲、投影、偏移阴影、剪影、发光、溶解:

Sprite Shaders 演示截图:同一精灵在 10 种着色器下的效果对照

场景工程拆解:10 个 Sprite2D 共用一张贴图

演示主场景 sprite_shaders.tscn 的结构非常清晰,可以直接看出该工程的组织方式:同一张贴图 godotea.png,通过 10 个 Sprite2D 节点各挂一个 ShaderMaterial 实现效果对照。

从场景源码可以看到关键细节(sprite_shaders.tscn):

  • 根节点 SpriteShaders(Node2D)整体缩放 scale = Vector2(0.3, 0.3),并挂载了第一个 outline 材质作为基底;
  • 10 个子节点均为 Sprite2D,texture 统一指向 ExtResource("2") 即 godotea.png;
  • 每个节点通过 position 在二维网格中错位排列(第一行 5 个、第二行 5 个),texture_filter = 4 表示使用最近邻/像素风过滤模式,保证模糊、描边等采样效果在低分辨率下依然可辨识;
  • 场景末尾带一个 Camera2D(offset = Vector2(248, 120)),用于将 10 个精灵全部纳入视野。

材质参数在场景内以 shader_parameter/... 形式预设,例如 Outline 节点使用了黑色描边 Color(0, 0, 0, 1)、宽度 2.0(sprite_shaders.tscn),Aura 节点使用了青色发光 Color(0, 1, 0.486275, 1)(sprite_shaders.tscn)。这意味着你不必打开编辑器,也能从场景文件直接反推出每个效果的默认参数组合。

工程窗口配置同样值得注意(project.godot):

[display]
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"

canvas_items 拉伸模式让 2D 内容随窗口尺寸等比缩放,配合 expand 宽高比策略,无论窗口如何变化,演示网格都能完整显示。

描边与光环:邻域 Alpha 采样法

outline.gdshader(描边)

outline.gdshader 是典型的"边缘检测型"描边实现,核心思路是在当前像素上下左右四个方向偏移采样 Alpha 值,再比较极差:

shader_type canvas_item;

uniform float outline_width = 2.0;
uniform vec4 outline_color : source_color;

void fragment() {
    vec4 col = texture(TEXTURE, UV);
    vec2 ps = TEXTURE_PIXEL_SIZE;
    float a;
    float maxa = col.a;
    float mina = col.a;

    a = texture(TEXTURE, UV + vec2(0.0, -outline_width) * ps).a;
    maxa = max(a, maxa);
    mina = min(a, mina);
    // ……其余三个方向同理……
    COLOR = mix(col, outline_color, maxa - mina);
}
  • TEXTURE_PIXEL_SIZE 是 Godot CanvasItem 着色器内置变量,表示单个纹素(texel)在 UV 空间中的尺寸,用它乘以偏移量即可把"像素数"转换为 UV 偏移,这正是 outline_width = 2.0 表示"向外偏移 2 个像素"的原因;
  • 当采样点位于精灵边缘时,四邻域 Alpha 的 maxa - mina 差值会明显变大,mix 就把颜色从原图渐变到 outline_color;在精灵内部则差值为 0,保持原色;
  • 效果是沿形状轮廓生成一圈统一颜色的描边,适合角色选中态、卡片边框等 UI/游戏内反馈。

aura.gdshader(光环)

aura.gdshader 与描边同构,但有两个关键差异:

  1. 使用 render_mode blend_premul_alpha(预乘 Alpha 混合),源码注释也明确指出"This shader only works properly with premultiplied alpha blend mode";
  2. 在输出时先做预乘 col.rgb *= col.a,再把光环颜色叠加在外部轮廓上(aura.gdshader):
col.rgb *= col.a;
COLOR = col;
COLOR.rgb += aura_color.rgb * (maxa - mina);

效果是精灵外部出现一圈柔和的单色光晕轮廓,常用于技能范围提示、选中高亮等场景。

模糊与投影:多方向平均采样

blur.gdshader(模糊)

blur.gdshader 是最简单的盒式模糊(Box Blur):将当前像素与上下左右四个偏移采样求和后除以 5(即取 5 点平均):

uniform float radius = 4.0;

void fragment() {
    vec4 col = texture(TEXTURE, UV);
    vec2 ps = TEXTURE_PIXEL_SIZE;
    col += texture(TEXTURE, UV + vec2(0.0, -radius) * ps);
    col += texture(TEXTURE, UV + vec2(0.0, radius) * ps);
    col += texture(TEXTURE, UV + vec2(-radius, 0.0) * ps);
    col += texture(TEXTURE, UV + vec2(radius, 0.0) * ps);
    col /= 5.0;
    COLOR = col;
}
  • radius 控制采样距离,值越大模糊范围越宽;由于只有十字方向 4 个采样点,模糊呈菱形扩散,演示中 radius = 4.0 已能产生清晰的柔化观感;
  • 若需要更强效果,可将采样扩展为 3×3 邻域并配合多次迭代。

dropshadow.gdshader 与 offsetshadow.gdshader(两类阴影)

两个阴影着色器代表了两种常见思路:

dropshadow(柔边投影)在 dropshadow.gdshader 中,先对偏移邻域的 8 个方向取平均得到模糊后的影子,再乘上 modulate 颜色,最后按原图 Alpha 与精灵混合:

uniform float radius = 5.0;
uniform vec4 modulate : source_color;

vec4 shadow = texture(TEXTURE, UV + vec2(-radius, -radius) * ps);
// ……共 8 个方向……
shadow /= 8.0;
shadow *= modulate;
vec4 col = texture(TEXTURE, UV);
COLOR = mix(shadow, col, col.a);

**offsetshadow(偏移硬阴影)**在 offsetshadow.gdshader 中则不做模糊,直接把阴影在 offset 方向平移固定像素距离,颜色取 modulate 的 RGB、透明度取自采样处 Alpha:

uniform vec2 offset = vec2(8.0, 8.0);

vec4 shadow = vec4(modulate.rgb, texture(TEXTURE, UV - offset * ps).a * modulate.a);
vec4 col = texture(TEXTURE, UV);
COLOR = mix(shadow, col, col.a);

从场景配置看,两者默认都使用纯黑阴影(modulate = Color(0, 0, 0, 1)),区别在于 Dropshadow 的 radius = 5.0 产生柔和扩散的投影,OffsetShadow 的 offset = (8, 8) 产生边缘清晰的错位硬阴影——前者适合"悬浮感"、后者适合"纸片感"。

扭曲、剪影与发光:单次采样的巧思

fatty.gdshader(膨胀扭曲)

fatty.gdshader 通过径向重映射 UV 实现"胖化"效果:以贴图中心为原点,把每个像素按距离中心的长短做非线性拉伸:

uniform float fattyness = 2.0;

void fragment() {
    vec2 ruv = UV - vec2(0.5, 0.5);
    vec2 dir = normalize(ruv);
    float len = length(ruv);
    len = pow(len * 2.0, fattyness) * 0.5;
    ruv = len * dir;
    vec4 col = texture(TEXTURE, ruv + vec2(0.5, 0.5));
    COLOR = col;
}
  • pow(len * 2.0, fattyness) * 0.5 对半径做幂运算,fattyness > 1 时边缘像素向中心收缩、中部像素被放大,产生"鼓胀"变形;
  • 场景中该效果使用了 fattyness = 3.0(sprite_shaders.tscn),变形更夸张。这类 UV 重映射思路还可扩展到波浪、放大镜、液体晃动等效果。

silouette.gdshader(剪影)

silouette.gdshader 是全部着色器中最短的一个,只用一行输出就完成"单色剪影":

uniform vec4 modulate : source_color;

void fragment() {
    COLOR = vec4(modulate.rgb, texture(TEXTURE, UV).a * modulate.a);
}

它丢弃原图全部颜色信息,仅保留 Alpha 通道,填充为 modulate 指定的纯色。场景中使用了洋红色 Color(0.858824, 0.317647, 0.701961, 1),适用于敌人被击中闪白/闪红、禁用态灰化等反馈。

glow.gdshader(自发光光晕)

glow.gdshader 采用双半径邻域采样 + 预乘 Alpha 组合:先在 radius 距离上采样 8 个方向、再在 radius * 2.0 距离上采样 8 个方向,共 17 个采样点平均后乘以 amount 作为光晕,最后与原图预乘颜色叠加:

uniform float radius = 5.0;
uniform float amount = 0.25;

glow /= 17.0;
glow *= amount;
col.rgb *= col.a;
COLOR = glow + col;
  • amount 控制光晕强度(演示默认 0.25),radius 控制光晕扩散范围;双半径采样让光晕在近处更密、远处更稀,观感比单半径更柔和;
  • 与 aura(轮廓型光环)不同,glow 是"整体向外晕开"的弥散光效,适合魔法武器、能量核心等自发光物体。

dissintegrate.gdshader(噪点溶解)

dissintegrate.gdshader 使用一个经典的伪随机哈希函数生成逐像素噪声,再按噪声阈值裁切 Alpha,实现"溶解消散":

uniform float amount = 20.0;

void fragment() {
    vec2 uv = UV * 0.05;
    float a = fract(sin(dot(UV, vec2(12.9898, 78.233))) * 438.5453);
    vec4 col = texture(TEXTURE, UV);
    col.a *= pow(a, amount);
    COLOR = col;
}
  • fract(sin(dot(UV, vec2(12.9898, 78.233))) * 438.5453) 是广为流传的 GLSL 伪随机写法,对每个 UV 位置生成 [0,1) 的确定性噪声;
  • pow(a, amount) 对噪声做幂运算:amount 越大,低噪声区域占比越高、整体越"碎";场景默认 amount = 4.0(sprite_shaders.tscn),同时该节点的 region_rect = Rect2(141, 115, 39, 6) 裁切了一小片区域,用于演示溶解动画的局部特写。若配合时间变量 TIME 动态调小 amount,即可得到从 1 到 0 的完整消散动画。

通用机制小结:TEXTURE_PIXEL_SIZE 与混合模式

纵观全部 9 个着色器,可以发现三个贯穿始终的通用机制:

  1. 邻域采样是特效基础:描边/光环依赖四邻域 Alpha 极差,模糊/阴影/发光依赖多方向平均采样,全部通过 TEXTURE + UV偏移 × TEXTURE_PIXEL_SIZE 完成像素级采样;
  2. render_mode 决定混合语义:工程中出现了 blend_mix(常规混合)与 blend_premul_alpha(预乘混合,aura 与 glow 使用),预乘模式能避免半透明边缘出现黑边/光晕伪影,是发光类效果的首选;
  3. source_color 注解:uniform vec4 xxx : source_color 表示该颜色在编辑器中按 sRGB 颜色空间处理,保证调色所见即所得。

运行与扩展建议

运行方式很简单:使用 Godot 4.7 或更高版本(工程 project.godot 声明的 config/features 为 "4.7")导入 2d/sprite_shaders 目录,主场景自动指向 sprite_shaders.tscn,直接按 F5 运行即可看到 10 种效果的同屏对照。若要在自己的项目中复用:

  • 将 shaders/ 下某个 .gdshader 文件拖入你的 Sprite2D 的 Material 槽位(或创建 ShaderMaterial 引用);
  • 参照 sprite_shaders.tscn 中的 shader_parameter/... 写法在编辑器中实时调参;
  • 注意:aura、glow 等预乘 Alpha 着色器必须搭配 blend_premul_alpha 才能获得正确效果;所有效果均基于 Compatibility 渲染器验证,若切换到 Forward+ 或移动端管线,建议先跑一遍确认采样行为一致。
登录后查看全文
godot-demo-projects