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 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 → 法线贴图提示
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 | 2π |
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 编辑器中,点击 ShaderMaterial 的 Shader 资源可以实时预览着色器效果。善用 hint_range 和 source_color 提示,让 uniform 参数在编辑器中显示为滑块和颜色选择器,大幅提高调试效率。
精度优化
// 使用低精度变量(移动端重要)
mediump vec3 color;
lowp float alpha;
highp vec3 world_pos; // 只在需要时用 highp
游戏开发场景
| 场景 | 着色器技术 |
|---|
| 角色溶解 | dissolve 着色器 + noise |
| 水面反射 | 双层法线 + 菲涅尔 |
| 全息 UI | 扫描线 + 边缘发光 |
| 像素风 | nearest filter + 分辨率缩放 |
| 阴影投射 | spatial light 着色器 |
| 地形混合 | splatmap + 多纹理混合 |
| 轮廓描边 | 视角法线偏移 |
⚠️ 常见陷阱
- Godot 着色语言不完全是 GLSL,变量名和语法有差异
discard 会打断 Early-Z 优化,大量使用影响性能- 透明物体需要设
render_mode blend_mix,否则不透明 SCREEN_TEXTURE 在 Godot 4.3+ 需要特殊访问方式source_color 提示确保颜色正确伽马校正- 着色器编译有延迟,首次使用可能卡顿(shader warmup)
扩展阅读