强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Godot 4 GDScript 教程 / 着色器(Shader)深入

着色器(Shader)深入

概述

Godot 使用自有着色语言(基于 GLSL ES 3.0),通过 Shader 资源和 ShaderMaterial 应用到节点上。不同类型的着色器适用于不同的渲染场景。

着色器类型 适用节点 说明
canvas_item 2D 节点 Sprite2D、Control 等
spatial 3D 节点 MeshInstance3D 等
sky Sky 天空渲染
fog FogVolume 体积雾
particles GPUParticles 粒子着色器
compute 计算着色器

Godot 着色语言基础

基本结构

shader_type spatial;  // 声明着色器类型

// Uniform 变量(从代码传入)
uniform float speed = 1.0;
uniform vec4 color : source_color = vec4(1.0);
uniform sampler2D main_texture : source_color;

// Varying 变量(顶点→片段传递)
varying vec2 world_pos;

// 顶点着色器
void vertex() {
    world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xy;
    VERTEX.y += sin(TIME * speed + VERTEX.x) * 0.2;
}

// 片段着色器
void fragment() {
    vec4 tex = texture(main_texture, UV);
    ALBEDO = tex.rgb * color.rgb;
    ALPHA = tex.a;
}

// 光照着色器(可选)
void light() {
    DIFFUSE_LIGHT += clamp(dot(NORMAL, LIGHT), 0.0, 1.0) * ATTENUATION * ALBEDO;
}

Uniform 与 Varying

Uniform 声明

// 基本类型
uniform float my_float = 1.0;
uniform int my_int = 0;
uniform vec2 my_vec2 = vec2(0.0, 0.0);
uniform vec3 my_vec3 = vec3(0.0, 0.0, 0.0);
uniform vec4 my_vec4 = vec4(1.0, 1.0, 1.0, 1.0);
uniform bool my_bool = false;

// 纹理
uniform sampler2D my_texture : source_color;
uniform sampler2D my_normal : hint_normal;
uniform sampler3D my_noise : source_color;

// 提示修饰符
uniform float roughness : hint_range(0.0, 1.0) = 0.5;
uniform vec4 albedo : source_color = vec4(1.0);

// 带 hint 的 uniform 会在编辑器中显示为特定控件
// hint_range(0, 1) → 滑块
// source_color → 颜色选择器
// hint_normal → 法线贴图提示

代码设置 Uniform

extends MeshInstance3D

@onready var mat: ShaderMaterial = material_override

func _ready():
    mat.set_shader_parameter("speed", 2.0)
    mat.set_shader_parameter("color", Color(1, 0, 0))
    mat.set_shader_parameter("main_texture", preload("res://icon.svg"))

func _process(delta):
    # 动态更新
    mat.set_shader_parameter("time_offset", Time.get_ticks_msec() * 0.001)

Varying

varying vec3 world_position;
varying vec2 v_uv;

void vertex() {
    world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    v_uv = UV;
    // Godot 自动传递 UV、NORMAL、COLOR 等
}

void fragment() {
    // 在片段着色器中使用
    vec3 pos = world_position;
}

CanvasItem 着色器(2D)

2D 纹理动画

shader_type canvas_item;

uniform float scroll_speed = 0.5;
uniform vec2 uv_scale = vec2(1.0, 1.0);

void fragment() {
    vec2 uv = UV * uv_scale;
    uv.y += TIME * scroll_speed;
    vec4 tex = texture(TEXTURE, uv);
    COLOR = tex;
}

2D 溶解效果

shader_type canvas_item;

uniform float dissolve_amount : hint_range(0.0, 1.0) = 0.0;
uniform sampler2D noise_texture;
uniform vec4 dissolve_color : source_color = vec4(1.0, 0.5, 0.0, 1.0);
uniform float edge_width = 0.05;

void fragment() {
    vec4 tex = texture(TEXTURE, UV);
    float noise = texture(noise_texture, UV).r;

    // 溶解阈值
    if (noise < dissolve_amount) {
        discard;
    }

    // 边缘发光
    float edge = smoothstep(dissolve_amount, dissolve_amount + edge_width, noise);
    vec3 final_color = mix(dissolve_color.rgb, tex.rgb, edge);

    COLOR = vec4(final_color, tex.a);
}

2D 描边效果

shader_type canvas_item;

uniform float outline_width = 2.0;
uniform vec4 outline_color : source_color = vec4(0.0, 0.0, 0.0, 1.0);
uniform vec2 texture_size = vec2(64.0, 64.0);

void fragment() {
    vec4 tex = texture(TEXTURE, UV);

    if (tex.a < 0.1) {
        // 检查周围像素
        vec2 pixel_size = 1.0 / texture_size;
        float max_alpha = 0.0;

        for (float x = -1.0; x <= 1.0; x += 1.0) {
            for (float y = -1.0; y <= 1.0; y += 1.0) {
                vec2 offset = vec2(x, y) * pixel_size * outline_width;
                float a = texture(TEXTURE, UV + offset).a;
                max_alpha = max(max_alpha, a);
            }
        }

        if (max_alpha > 0.1) {
            COLOR = outline_color;
            return;
        }
    }

    COLOR = tex;
}

Spatial 着色器(3D)

3D 标准材质模拟

shader_type spatial;

uniform vec4 albedo_color : source_color = vec4(1.0);
uniform sampler2D albedo_texture : source_color;
uniform float metallic : hint_range(0.0, 1.0) = 0.0;
uniform float roughness : hint_range(0.0, 1.0) = 0.5;
uniform sampler2D normal_texture : hint_normal;
uniform float normal_strength : hint_range(0.0, 2.0) = 1.0;

void fragment() {
    vec4 tex = texture(albedo_texture, UV);
    ALBEDO = tex.rgb * albedo_color.rgb;
    METALLIC = metallic;
    ROUGHNESS = roughness;

    // 法线贴图
    vec3 normal_map = texture(normal_texture, UV).rgb;
    normal_map = normal_map * 2.0 - 1.0;
    normal_map.xy *= normal_strength;
    NORMAL_MAP = normalize(normal_map);
}

溶解效果(3D 版)

shader_type spatial;

uniform float dissolve : hint_range(0.0, 1.0) = 0.0;
uniform sampler2D noise_texture;
uniform vec4 edge_color : source_color = vec4(0.0, 1.0, 0.5, 1.0);
uniform float edge_width = 0.1;

void fragment() {
    vec4 tex = texture(noise_texture, UV);
    float noise = tex.r;

    if (noise < dissolve) {
        discard;
    }

    // 边缘发光
    float edge = 1.0 - smoothstep(dissolve, dissolve + edge_width, noise);
    ALBEDO = mix(ALBEDO, edge_color.rgb, edge);
    EMISSION = edge_color.rgb * edge * 3.0;
}

全息效果

shader_type spatial;

uniform vec4 holo_color : source_color = vec4(0.0, 0.8, 1.0, 1.0);
uniform float scan_line_speed = 5.0;
uniform float scan_line_count = 50.0;
uniform float flicker_speed = 10.0;
uniform float fresnel_power = 2.0;

void fragment() {
    // 扫描线
    float scan_line = sin((UV.y + TIME * scan_line_speed) * scan_line_count);
    scan_line = scan_line * 0.5 + 0.5;

    // 闪烁
    float flicker = sin(TIME * flicker_speed) * 0.1 + 0.9;

    // 菲涅尔效果(边缘发光)
    float fresnel = pow(1.0 - abs(dot(NORMAL, VIEW)), fresnel_power);

    ALBEDO = holo_color.rgb;
    ALPHA = (scan_line * 0.3 + fresnel * 0.7) * flicker * holo_color.a;
    EMISSION = holo_color.rgb * (fresnel + scan_line * 0.3) * 2.0;

    // 透明混合
    // 需要设置材质 transparency = ALPHA
}

水面效果

shader_type spatial;

uniform vec4 water_color_deep : source_color = vec4(0.0, 0.2, 0.4, 0.8);
uniform vec4 water_color_shallow : source_color = vec4(0.0, 0.6, 0.8, 0.6);
uniform float wave_speed = 1.0;
uniform float wave_strength = 0.3;
uniform float wave_frequency = 3.0;
uniform float fresnel_power = 3.0;
uniform sampler2D normal_map_1 : hint_normal;
uniform sampler2D normal_map_2 : hint_normal;

varying vec3 world_pos;

void vertex() {
    // 波浪顶点偏移
    world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    VERTEX.y += sin(world_pos.x * wave_frequency + TIME * wave_speed) * wave_strength;
    VERTEX.y += cos(world_pos.z * wave_frequency * 0.7 + TIME * wave_speed * 0.8) * wave_strength * 0.5;
}

void fragment() {
    // 双层法线贴图滚动
    vec2 uv1 = UV + vec2(TIME * 0.05, TIME * 0.03);
    vec2 uv2 = UV * 1.5 + vec2(-TIME * 0.03, TIME * 0.05);

    vec3 n1 = texture(normal_map_1, uv1).rgb * 2.0 - 1.0;
    vec3 n2 = texture(normal_map_2, uv2).rgb * 2.0 - 1.0;
    NORMAL_MAP = normalize(n1 + n2);

    // 菲涅尔
    float fresnel = pow(1.0 - abs(dot(NORMAL, VIEW)), fresnel_power);

    // 深浅颜色混合
    vec3 water_color = mix(water_color_deep.rgb, water_color_shallow.rgb, fresnel);

    ALBEDO = water_color;
    ALPHA = mix(water_color_deep.a, water_color_shallow.a, fresnel);
    METALLIC = 0.3;
    ROUGHNESS = 0.1;
    EMISSION = water_color * fresnel * 0.3;
}

Sky 着色器

shader_type sky;

uniform vec4 day_color : source_color = vec4(0.3, 0.5, 0.8, 1.0);
uniform vec4 sunset_color : source_color = vec4(1.0, 0.4, 0.1, 1.0);
uniform vec4 night_color : source_color = vec4(0.02, 0.02, 0.05, 1.0);
uniform vec3 sun_direction = vec3(0.0, 1.0, 0.0);

void sky() {
    float sun_height = dot(SKY_COORDS, sun_direction);
    float day_factor = smoothstep(-0.1, 0.3, sun_height);
    float sunset_factor = smoothstep(-0.1, 0.0, sun_height) * smoothstep(0.3, 0.0, sun_height);

    vec3 color = mix(night_color.rgb, day_color.rgb, day_factor);
    color = mix(color, sunset_color.rgb, sunset_factor);

    // 太阳光晕
    float sun_dot = max(0.0, dot(SKY_COORDS, sun_direction));
    float sun_disk = smoothstep(0.999, 0.9999, sun_dot);
    float sun_glow = pow(max(0.0, sun_dot), 64.0);

    color += vec3(1.0, 0.9, 0.7) * sun_disk;
    color += vec3(1.0, 0.6, 0.3) * sun_glow * 0.5;

    COLOR = color;
}

Fog 着色器

shader_type fog;

uniform float density = 0.02;
uniform vec4 fog_color : source_color = vec4(0.7, 0.8, 0.9, 1.0);
uniform float height_falloff = 0.1;

void fog() {
    float height_factor = exp(-WORLD_POSITION.y * height_falloff);
    DENSITY = density * height_factor;
    ALBEDO = fog_color.rgb;
}

内置函数与变量

常用内置变量

变量 着色器类型 说明
VERTEX spatial/canvas_item 顶点位置
NORMAL spatial 法线
UV all 纹理坐标
UV2 all 第二 UV 通道
COLOR all 顶点颜色
TANGENT spatial 切线
BINORMAL spatial 副法线
ALBEDO spatial 漫反射颜色
METALLIC spatial 金属度
ROUGHNESS spatial 粗糙度
EMISSION spatial 自发光
ALPHA all 透明度
TIME all 时间(秒)
PI all 圆周率 3.14159
TAU all
MODEL_MATRIX spatial 模型矩阵
VIEW_MATRIX spatial 视图矩阵
PROJECTION_MATRIX spatial 投影矩阵
VIEW spatial 视线方向
NORMAL_MAP spatial 法线贴图
SCREEN_UV spatial/canvas_item 屏幕 UV
SCREEN_TEXTURE spatial 屏幕纹理
DEPTH_TEXTURE spatial 深度纹理
TEXTURE canvas_item 节点纹理
POINT_SIZE canvas_item 点大小

常用内置函数

函数 说明
sin(x) / cos(x) / tan(x) 三角函数
asin(x) / acos(x) / atan(x) 反三角
pow(x, y) 幂运算
exp(x) / log(x) 指数/对数
sqrt(x) 平方根
abs(x) 绝对值
sign(x) 符号
floor(x) / ceil(x) 取整
fract(x) 小数部分
mod(x, y) 取模
min(x, y) / max(x, y) 最小/最大值
clamp(x, min, max) 钳制
mix(x, y, a) 线性插值
step(edge, x) 阶跃函数
smoothstep(e0, e1, x) 平滑阶跃
length(x) 向量长度
distance(x, y) 距离
dot(x, y) 点积
cross(x, y) 叉积(仅 vec3)
normalize(x) 归一化
reflect(I, N) 反射
refract(I, N, eta) 折射
texture(sampler, uv) 纹理采样

全息效果实战(完整)

# 创建 ShaderMaterial 并应用
extends MeshInstance3D

func _ready():
    var shader = Shader.new()
    shader.code = """
shader_type spatial;
render_mode blend_add, cull_disabled, unshaded;

uniform vec4 color : source_color = vec4(0.0, 0.8, 1.0, 1.0);
uniform float scan_speed = 3.0;
uniform float scan_lines = 80.0;
uniform float fresnel_power = 2.0;
uniform float flicker_speed = 8.0;

void fragment() {
    float scan = sin((UV.y + TIME * scan_speed) * scan_lines * PI) * 0.5 + 0.5;
    float flicker = sin(TIME * flicker_speed) * 0.1 + 0.9;
    float fresnel = pow(1.0 - abs(dot(NORMAL, VIEW)), fresnel_power);

    float alpha = (scan * 0.2 + fresnel * 0.8) * flicker;

    ALBEDO = color.rgb;
    ALPHA = alpha * color.a;
    EMISSION = color.rgb * (fresnel * 2.0 + scan * 0.5);
}
"""
    var mat = ShaderMaterial.new()
    mat.shader = shader
    material_override = mat

着色器性能优化

策略 说明
减少纹理采样 每次 texture() 都有开销
使用 discard 谨慎 会打断 GPU 流水线
避免分支(if) GPU 并行处理分支效率低
降低精度 mediump 替代 highp(移动端)
避免复杂数学 减少 pow/exp/sin 调用
使用预计算 噪声纹理替代实时噪声函数
LOD 着色器 远处用简单着色器

💡 提示:在 Godot 编辑器中,点击 ShaderMaterialShader 资源可以实时预览着色器效果。善用 hint_rangesource_color 提示,让 uniform 参数在编辑器中显示为滑块和颜色选择器,大幅提高调试效率。

精度优化

// 使用低精度变量(移动端重要)
mediump vec3 color;
lowp float alpha;
highp vec3 world_pos;  // 只在需要时用 highp

游戏开发场景

场景 着色器技术
角色溶解 dissolve 着色器 + noise
水面反射 双层法线 + 菲涅尔
全息 UI 扫描线 + 边缘发光
像素风 nearest filter + 分辨率缩放
阴影投射 spatial light 着色器
地形混合 splatmap + 多纹理混合
轮廓描边 视角法线偏移

⚠️ 常见陷阱

  1. Godot 着色语言不完全是 GLSL,变量名和语法有差异
  2. discard 会打断 Early-Z 优化,大量使用影响性能
  3. 透明物体需要设 render_mode blend_mix,否则不透明
  4. SCREEN_TEXTURE 在 Godot 4.3+ 需要特殊访问方式
  5. source_color 提示确保颜色正确伽马校正
  6. 着色器编译有延迟,首次使用可能卡顿(shader warmup)

扩展阅读