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

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

着色器(Shader)深入

概述

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

着色器类型适用节点说明
canvas_item2D 节点Sprite2D、Control 等
spatial3D 节点MeshInstance3D 等
skySky天空渲染
fogFogVolume体积雾
particlesGPUParticles粒子着色器
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;
}

内置函数与变量

常用内置变量

变量着色器类型说明
VERTEXspatial/canvas_item顶点位置
NORMALspatial法线
UVall纹理坐标
UV2all第二 UV 通道
COLORall顶点颜色
TANGENTspatial切线
BINORMALspatial副法线
ALBEDOspatial漫反射颜色
METALLICspatial金属度
ROUGHNESSspatial粗糙度
EMISSIONspatial自发光
ALPHAall透明度
TIMEall时间(秒)
PIall圆周率 3.14159
TAUall
MODEL_MATRIXspatial模型矩阵
VIEW_MATRIXspatial视图矩阵
PROJECTION_MATRIXspatial投影矩阵
VIEWspatial视线方向
NORMAL_MAPspatial法线贴图
SCREEN_UVspatial/canvas_item屏幕 UV
SCREEN_TEXTUREspatial屏幕纹理
DEPTH_TEXTUREspatial深度纹理
TEXTUREcanvas_item节点纹理
POINT_SIZEcanvas_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)

扩展阅读