第二十七章:3D材质与着色器

第二十七章: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材质与着色器:

  1. StandardMaterial3D:PBR属性、高级效果、透明混合
  2. 着色器基础:类型、结构、内置变量
  3. 常用效果:菲涅尔、溶解、全息、力场
  4. ShaderMaterial:GDScript使用、动态实例
  5. 材质管理:材质库、切换
  6. 实际案例:水面着色器、材质控制器

下一章将学习3D光照系统。


上一章:网格与模型导入

下一章:3D光照系统

← 返回目录