第二十七章:3D材质与着色器
"材质决定物体的外观,着色器则是实现任何视觉效果的关键。"
材质系统是3D渲染的核心。本章将讲解Godot的PBR材质系统、着色器语言以及常用特效的实现。
27.1 StandardMaterial3D
27.1.1 基本属性
extends MeshInstance3D
func setup_standard_material():
var mat = StandardMaterial3D.new()
# 反照率(基础颜色)
mat.albedo_color = Color(0.8, 0.2, 0.2)
mat.albedo_texture = preload("res://textures/diffuse.png")
# 金属度
mat.metallic = 0.5
mat.metallic_texture = preload("res://textures/metallic.png")
mat.metallic_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_RED
# 粗糙度
mat.roughness = 0.3
mat.roughness_texture = preload("res://textures/roughness.png")
mat.roughness_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_GREEN
# 法线贴图
mat.normal_enabled = true
mat.normal_texture = preload("res://textures/normal.png")
mat.normal_scale = 1.0
# 环境光遮蔽
mat.ao_enabled = true
mat.ao_texture = preload("res://textures/ao.png")
mat.ao_light_affect = 0.5
material_override = mat
27.1.2 高级属性
func setup_advanced_material():
var mat = StandardMaterial3D.new()
# 自发光
mat.emission_enabled = true
mat.emission = Color(1, 0.5, 0)
mat.emission_energy_multiplier = 2.0
mat.emission_texture = preload("res://textures/emission.png")
# 次表面散射(皮肤、蜡烛等)
mat.subsurf_scatter_enabled = true
mat.subsurf_scatter_strength = 0.5
mat.subsurf_scatter_skin_mode = true
# 背光(树叶等)
mat.backlight_enabled = true
mat.backlight = Color(0.5, 1.0, 0.5)
# 折射(玻璃等)
mat.refraction_enabled = true
mat.refraction_scale = 0.05
# 边缘光
mat.rim_enabled = true
mat.rim = 0.5
mat.rim_tint = 0.5
# 清漆层(汽车漆)
mat.clearcoat_enabled = true
mat.clearcoat = 0.5
mat.clearcoat_roughness = 0.1
# 各向异性(拉丝金属)
mat.anisotropy_enabled = true
mat.anisotropy = 0.5
material_override = mat
27.1.3 透明与混合
func setup_transparent_material():
var mat = StandardMaterial3D.new()
# 透明模式
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
# TRANSPARENCY_DISABLED - 不透明
# TRANSPARENCY_ALPHA - Alpha透明
# TRANSPARENCY_ALPHA_SCISSOR - Alpha裁剪
# TRANSPARENCY_ALPHA_HASH - Alpha哈希
# TRANSPARENCY_ALPHA_DEPTH_PRE_PASS - 深度预通道
# Alpha裁剪阈值
mat.alpha_scissor_threshold = 0.5
# 混合模式
mat.blend_mode = BaseMaterial3D.BLEND_MODE_MIX
# BLEND_MODE_MIX - 正常混合
# BLEND_MODE_ADD - 加法混合
# BLEND_MODE_SUB - 减法混合
# BLEND_MODE_MUL - 乘法混合
# 剔除模式
mat.cull_mode = BaseMaterial3D.CULL_BACK
# CULL_BACK - 背面剔除
# CULL_FRONT - 正面剔除
# CULL_DISABLED - 双面显示
# 深度绘制
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_OPAQUE_ONLY
# 阴影
mat.no_depth_test = false
mat.shading_mode = BaseMaterial3D.SHADING_MODE_PER_PIXEL
material_override = mat
27.2 着色器基础
27.2.1 着色器类型
# Godot着色器类型
# shader_type spatial; - 3D着色器
# shader_type canvas_item; - 2D着色器
# shader_type particles; - 粒子着色器
# shader_type sky; - 天空着色器
# shader_type fog; - 雾着色器
27.2.2 基本3D着色器
// simple.gdshader
shader_type spatial;
// Uniforms(可从GDScript设置)
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;
// 顶点着色器
void vertex() {
// VERTEX是本地空间顶点位置
// 可以修改顶点位置实现变形效果
}
// 片段着色器
void fragment() {
// 采样纹理
vec4 tex_color = texture(albedo_texture, UV);
// 设置输出
ALBEDO = albedo_color.rgb * tex_color.rgb;
METALLIC = metallic;
ROUGHNESS = roughness;
ALPHA = albedo_color.a * tex_color.a;
}
// 光照函数(可选,自定义光照)
void light() {
// DIFFUSE_LIGHT, SPECULAR_LIGHT
// LIGHT, LIGHT_COLOR, ATTENUATION
// NORMAL, VIEW
}
27.2.3 内置变量
// 顶点着色器内置变量
// 输入
// VERTEX - 顶点位置(本地)
// NORMAL - 法线
// TANGENT, BINORMAL - 切线、副切线
// UV, UV2 - 纹理坐标
// COLOR - 顶点颜色
// INSTANCE_ID - 实例ID
// INSTANCE_CUSTOM - 实例自定义数据
// 输出/可修改
// POSITION - 裁剪空间位置
// POINT_SIZE - 点大小
// MODEL_MATRIX - 模型矩阵
// MODELVIEW_MATRIX - 模型视图矩阵
// PROJECTION_MATRIX - 投影矩阵
// 片段着色器内置变量
// 输入
// FRAGCOORD - 屏幕坐标
// VERTEX - 视图空间顶点位置
// NORMAL - 法线
// UV, UV2 - 纹理坐标
// VIEW - 视图方向
// FRONT_FACING - 是否正面
// 输出
// ALBEDO - 反照率颜色
// ALPHA - 透明度
// METALLIC - 金属度
// ROUGHNESS - 粗糙度
// SPECULAR - 高光强度
// EMISSION - 自发光
// NORMAL_MAP - 法线贴图
// RIM, RIM_TINT - 边缘光
// AO - 环境光遮蔽
// SSS_STRENGTH - 次表面散射
27.3 常用着色器效果
27.3.1 菲涅尔效果
// fresnel.gdshader
shader_type spatial;
uniform vec4 fresnel_color : source_color = vec4(0.0, 0.5, 1.0, 1.0);
uniform float fresnel_power : hint_range(0.0, 10.0) = 3.0;
void fragment() {
float fresnel = pow(1.0 - dot(NORMAL, VIEW), fresnel_power);
ALBEDO = vec3(0.1);
EMISSION = fresnel_color.rgb * fresnel;
}
27.3.2 溶解效果
// dissolve.gdshader
shader_type spatial;
uniform sampler2D noise_texture;
uniform float dissolve_amount : hint_range(0.0, 1.0) = 0.0;
uniform vec4 edge_color : source_color = vec4(1.0, 0.5, 0.0, 1.0);
uniform float edge_width : hint_range(0.0, 0.2) = 0.05;
uniform vec4 albedo_color : source_color = vec4(1.0);
void fragment() {
float noise = texture(noise_texture, UV).r;
// 丢弃溶解区域
if (noise < dissolve_amount) {
discard;
}
// 边缘发光
float edge = smoothstep(dissolve_amount, dissolve_amount + edge_width, noise);
ALBEDO = mix(edge_color.rgb, albedo_color.rgb, edge);
EMISSION = edge_color.rgb * (1.0 - edge) * 2.0;
}
27.3.3 全息效果
// hologram.gdshader
shader_type spatial;
uniform vec4 hologram_color : source_color = vec4(0.0, 1.0, 1.0, 1.0);
uniform float scan_speed : hint_range(0.0, 10.0) = 2.0;
uniform float scan_lines : hint_range(0.0, 500.0) = 100.0;
uniform float flicker_speed : hint_range(0.0, 10.0) = 5.0;
void fragment() {
// 扫描线
float scan = sin((VERTEX.y + TIME * scan_speed) * scan_lines) * 0.5 + 0.5;
scan = pow(scan, 2.0);
// 闪烁
float flicker = sin(TIME * flicker_speed * 20.0) * 0.1 + 0.9;
// 菲涅尔
float fresnel = pow(1.0 - dot(NORMAL, VIEW), 2.0);
ALBEDO = hologram_color.rgb * 0.5;
EMISSION = hologram_color.rgb * (scan * 0.5 + fresnel) * flicker;
ALPHA = (scan * 0.3 + fresnel * 0.7) * flicker;
}
27.3.4 力场效果
// forcefield.gdshader
shader_type spatial;
render_mode unshaded, cull_disabled;
uniform vec4 field_color : source_color = vec4(0.0, 0.5, 1.0, 1.0);
uniform float pulse_speed : hint_range(0.0, 5.0) = 1.0;
uniform sampler2D hex_pattern;
void fragment() {
// 六边形图案
vec4 hex = texture(hex_pattern, UV * 5.0);
// 脉冲动画
float pulse = sin(TIME * pulse_speed) * 0.5 + 0.5;
// 菲涅尔边缘
float fresnel = pow(1.0 - dot(NORMAL, VIEW), 3.0);
// 交汇点(接触其他物体时变亮)
float intersection = 0.0; // 可通过深度缓冲计算
ALBEDO = field_color.rgb;
EMISSION = field_color.rgb * (fresnel + hex.r * pulse) * 2.0;
ALPHA = fresnel * 0.8 + hex.r * 0.2 * pulse;
}
27.4 ShaderMaterial使用
27.4.1 GDScript中使用着色器
extends MeshInstance3D
@onready var shader_material: ShaderMaterial
func _ready():
# 创建ShaderMaterial
shader_material = ShaderMaterial.new()
shader_material.shader = preload("res://shaders/dissolve.gdshader")
# 设置参数
shader_material.set_shader_parameter("dissolve_amount", 0.0)
shader_material.set_shader_parameter("edge_color", Color(1, 0.5, 0))
shader_material.set_shader_parameter("noise_texture", preload("res://textures/noise.png"))
material_override = shader_material
func dissolve(duration: float = 1.0):
var tween = create_tween()
tween.tween_method(
func(value): shader_material.set_shader_parameter("dissolve_amount", value),
0.0, 1.0, duration
)
tween.tween_callback(queue_free)
func set_dissolve(amount: float):
shader_material.set_shader_parameter("dissolve_amount", amount)
27.4.2 动态材质实例
extends MeshInstance3D
func _ready():
# 为每个实例创建独立的材质
var mat = material_override.duplicate()
material_override = mat
func set_color(color: Color):
material_override.set_shader_parameter("albedo_color", color)
func set_glow(enabled: bool, color: Color = Color.WHITE):
if enabled:
material_override.set_shader_parameter("emission", color)
material_override.set_shader_parameter("emission_energy", 2.0)
else:
material_override.set_shader_parameter("emission_energy", 0.0)
27.5 材质管理
27.5.1 材质库
# material_library.gd (Autoload)
extends Node
var materials: Dictionary = {}
func _ready():
_load_materials()
func _load_materials():
materials["metal"] = preload("res://materials/metal.tres")
materials["wood"] = preload("res://materials/wood.tres")
materials["glass"] = preload("res://materials/glass.tres")
materials["plastic"] = preload("res://materials/plastic.tres")
func get_material(name: String) -> Material:
if materials.has(name):
return materials[name]
push_error("材质不存在:" + name)
return null
func get_material_instance(name: String) -> Material:
var mat = get_material(name)
if mat:
return mat.duplicate()
return null
27.5.2 材质切换
extends MeshInstance3D
@export var materials_list: Array[Material] = []
var current_index: int = 0
func next_material():
if materials_list.is_empty():
return
current_index = (current_index + 1) % materials_list.size()
material_override = materials_list[current_index]
func set_material_by_name(name: String):
var mat = MaterialLibrary.get_material(name)
if mat:
material_override = mat
27.6 实际案例
27.6.1 水面着色器
// water.gdshader
shader_type spatial;
uniform vec4 water_color : source_color = vec4(0.0, 0.3, 0.5, 0.8);
uniform vec4 foam_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform sampler2D wave_noise;
uniform float wave_speed : hint_range(0.0, 2.0) = 0.5;
uniform float wave_height : hint_range(0.0, 1.0) = 0.1;
uniform float foam_amount : hint_range(0.0, 1.0) = 0.3;
varying float vertex_height;
void vertex() {
vec2 wave_uv = VERTEX.xz * 0.1 + TIME * wave_speed * 0.1;
float wave = texture(wave_noise, wave_uv).r;
VERTEX.y += wave * wave_height;
vertex_height = wave;
}
void fragment() {
// 菲涅尔
float fresnel = pow(1.0 - dot(NORMAL, VIEW), 3.0);
// 泡沫
float foam = step(1.0 - foam_amount, vertex_height);
ALBEDO = mix(water_color.rgb, foam_color.rgb, foam);
ROUGHNESS = 0.1;
METALLIC = 0.0;
SPECULAR = 0.5;
ALPHA = water_color.a + fresnel * 0.2;
}
27.6.2 动态材质控制器
# material_controller.gd
class_name MaterialController
extends Node
@export var target: MeshInstance3D
@export var material: ShaderMaterial
var _parameters: Dictionary = {}
func _ready():
if target and material:
target.material_override = material
func set_parameter(name: String, value: Variant):
_parameters[name] = value
if material:
material.set_shader_parameter(name, value)
func get_parameter(name: String) -> Variant:
return _parameters.get(name)
func animate_parameter(name: String, from: Variant, to: Variant, duration: float, trans: Tween.TransitionType = Tween.TRANS_LINEAR):
var tween = create_tween()
tween.tween_method(
func(value): set_parameter(name, value),
from, to, duration
).set_trans(trans)
return tween
func pulse_parameter(name: String, base_value: float, amplitude: float, speed: float):
var tween = create_tween().set_loops()
tween.tween_method(
func(value): set_parameter(name, value),
base_value - amplitude, base_value + amplitude, 1.0 / speed
).set_trans(Tween.TRANS_SINE)
tween.tween_method(
func(value): set_parameter(name, value),
base_value + amplitude, base_value - amplitude, 1.0 / speed
).set_trans(Tween.TRANS_SINE)
return tween
本章小结
本章讲解了3D材质与着色器:
- StandardMaterial3D:PBR属性、高级效果、透明混合
- 着色器基础:类型、结构、内置变量
- 常用效果:菲涅尔、溶解、全息、力场
- ShaderMaterial:GDScript使用、动态实例
- 材质管理:材质库、切换
- 实际案例:水面着色器、材质控制器
下一章将学习3D光照系统。
上一章:网格与模型导入
下一章:3D光照系统