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

Godot 4 GDScript 教程 / 3D 基础与网格

3D 基础与网格

概述

Godot 4 的 3D 系统基于 Vulkan 渲染器,支持高质量的 PBR 材质、全局光照和后处理。本节涵盖 3D 场景搭建、网格、材质、纹理和相机控制的基础知识。

核心节点 说明
Node3D 3D 空间基类(位置、旋转、缩放)
MeshInstance3D 网格实例显示
Camera3D 3D 相机
DirectionalLight3D 方向光
WorldEnvironment 环境设置
StaticBody3D 静态碰撞体

Node3D 基础

Transform 变换

extends Node3D

func _ready():
    # 位置
    position = Vector3(1, 2, 3)
    global_position = Vector3(0, 5, 0)

    # 旋转(弧度)
    rotation = Vector3(0, deg_to_rad(90), 0)
    rotation_degrees = Vector3(0, 90, 0)

    # 缩放
    scale = Vector3(2, 2, 2)

    # 四元数旋转
    var quat = Quaternion(Vector3.UP, deg_to_rad(45))
    transform.basis = Basis(quat)

    # Look At
    look_at(Vector3(10, 0, 10), Vector3.UP)

# 平滑旋转
func _process(delta):
    rotate_y(deg_to_rad(45) * delta)  # 每秒转 45 度

坐标转换

extends Node3D

func _ready():
    # 本地坐标 → 全局坐标
    var global_pos = to_global(Vector3(1, 0, 0))

    # 全局坐标 → 本地坐标
    var local_pos = to_local(Vector3(5, 0, 5))

    # 获取前方方向(-Z 轴)
    var forward = -global_transform.basis.z
    # 获取右方方向(X 轴)
    var right = global_transform.basis.x
    # 获取上方方向(Y 轴)
    var up = global_transform.basis.y

MeshInstance3D

基本网格类型

extends Node3D

func _ready():
    # 创建网格实例
    var mesh_instance = MeshInstance3D.new()

    # 盒子网格
    var box = BoxMesh.new()
    box.size = Vector3(1, 1, 1)

    # 球体网格
    var sphere = SphereMesh.new()
    sphere.radius = 0.5
    sphere.height = 1.0

    # 圆柱体
    var cylinder = CylinderMesh.new()
    cylinder.top_radius = 0.5
    cylinder.bottom_radius = 0.5
    cylinder.height = 2.0

    # 平面
    var plane = PlaneMesh.new()
    plane.size = Vector2(10, 10)

    # 胶囊体
    var capsule = CapsuleMesh.new()
    capsule.radius = 0.3
    capsule.height = 1.0

    mesh_instance.mesh = sphere
    add_child(mesh_instance)

内置网格类型速查表

网格类型 说明
BoxMesh 盒子
SphereMesh 球体
CylinderMesh 圆柱体
CapsuleMesh 胶囊体
PlaneMesh 平面
PrismMesh 三棱柱
TorusMesh 圆环
TorusMesh 圆环
PointMesh
RibbonTrailMesh 飘带
TubeTrailMesh 管状尾迹
TextMesh 3D 文字

3D 导入设置

glTF 导入(推荐)

glTF 2.0 是 Godot 推荐的 3D 格式,支持完整的场景、材质和动画。

# glTF 导入设置(在编辑器 Import 面板中)
# Meshes:
#   - Generate Lightmap UV: 启用(需要烘焙光照时)
#   - Ensure Tangents: 启用(法线贴图需要)
# Animation:
#   - Import: 启用
#   - FPS: 30
# Materials:
#   - Material Import: Standard(默认 PBR 材质)

FBX 导入

# FBX 需要 FBX2glTF 工具转换
# Godot 4.2+ 内置 FBX 导入支持
# 推荐将 FBX 转换为 glTF 后导入

💡 提示:优先使用 glTF 格式(.glb/.gltf),它与 Godot 的兼容性最好。Blender 可直接导出 glTF。


网格类型(ArrayMesh / SurfaceTool)

使用 SurfaceTool 创建自定义网格

extends MeshInstance3D

func _ready():
    mesh = create_triangle()

func create_triangle() -> ArrayMesh:
    var st = SurfaceTool.new()
    st.begin(Mesh.PRIMITIVE_TRIANGLES)

    # 设置法线和 UV
    st.set_normal(Vector3(0, 0, 1))
    st.set_uv(Vector2(0.5, 0))
    st.add_vertex(Vector3(0, 1, 0))

    st.set_normal(Vector3(0, 0, 1))
    st.set_uv(Vector2(0, 1))
    st.add_vertex(Vector3(-1, -1, 0))

    st.set_normal(Vector3(0, 0, 1))
    st.set_uv(Vector2(1, 1))
    st.add_vertex(Vector3(1, -1, 0))

    st.commit()
    return st.commit()  # 返回 ArrayMesh

使用 ArrayMesh 创建网格

extends MeshInstance3D

func _ready():
    mesh = create_quad()

func create_quad() -> ArrayMesh:
    var vertices = PackedVector3Array([
        Vector3(-0.5, 0.5, 0), Vector3(-0.5, -0.5, 0),
        Vector3(0.5, -0.5, 0), Vector3(0.5, 0.5, 0)
    ])
    var uvs = PackedVector2Array([
        Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)
    ])
    var normals = PackedVector3Array([
        Vector3(0, 0, 1), Vector3(0, 0, 1), Vector3(0, 0, 1), Vector3(0, 0, 1)
    ])
    var indices = PackedInt32Array([0, 1, 2, 0, 2, 3])

    var arrays = []
    arrays.resize(Mesh.ARRAY_MAX)
    arrays[Mesh.ARRAY_VERTEX] = vertices
    arrays[Mesh.ARRAY_TEX_UV] = uvs
    arrays[Mesh.ARRAY_NORMAL] = normals
    arrays[Mesh.ARRAY_INDEX] = indices

    var array_mesh = ArrayMesh.new()
    array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
    return array_mesh

材质系统(StandardMaterial3D)

PBR 材质基础

extends MeshInstance3D

func _ready():
    var mat = StandardMaterial3D.new()

    # 基础颜色(Albedo)
    mat.albedo_color = Color(0.8, 0.2, 0.2)
    mat.albedo_texture = preload("res://textures/wood_albedo.png")

    # 金属度
    mat.metallic = 0.8

    # 粗糙度
    mat.roughness = 0.3

    # 法线贴图
    mat.normal_texture = preload("res://textures/wood_normal.png")
    mat.normal_scale = 1.0

    # 环境光遮蔽
    mat.ao_texture = preload("res://textures/wood_ao.png")

    # 自发光
    mat.emission_enabled = true
    mat.emission = Color(0.2, 0.8, 0.2)
    mat.emission_energy_multiplier = 2.0

    # 透明度
    mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
    mat.albedo_color.a = 0.5

    # 双面渲染
    mat.cull_mode = BaseMaterial3D.CULL_DISABLED

    # 纹理过滤
    mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST  # 像素风格

    mesh.material_override = mat

材质属性速查表

属性 说明
albedo_color 基础颜色
albedo_texture 基础纹理
metallic 金属度(0~1)
roughness 粗糙度(0~1)
normal_texture 法线贴图
emission 自发光颜色
emission_enabled 启用自发光
transparency 透明模式
cull_mode 面剔除模式
uv1_scale UV 缩放
uv1_offset UV 偏移
texture_filter 纹理过滤模式
proximity_fade_enabled 近距离淡出
distance_fade_mode 远距离淡出

纹理类型

纹理类型 用途 说明
CompressedTexture2D 标准纹理 PNG/WebP 导入
ImageTexture 运行时创建 代码生成
NoiseTexture2D 噪声纹理 地形、云
GradientTexture1D 1D 渐变 颜色映射
GradientTexture2D 2D 渐变 背景
ViewportTexture 视口纹理 实时画面
CurveTexture 曲线纹理 动画映射
ProxyTexture2D 代理纹理 资源管理

运行时创建纹理

extends Node3D

func _ready():
    var image = Image.create(256, 256, false, Image.FORMAT_RGBA8)
    image.fill(Color(0.5, 0.5, 1.0))  # 蓝灰色

    # 绘制像素
    for x in range(256):
        for y in range(256):
            var noise_val = (sin(x * 0.1) + cos(y * 0.1)) * 0.5 + 0.5
            image.set_pixel(x, y, Color(noise_val, noise_val, noise_val))

    var texture = ImageTexture.create_from_image(image)

    var mat = StandardMaterial3D.new()
    mat.albedo_texture = texture
    $MeshInstance3D.material_override = mat

3D 场景搭建

extends Node3D

func _ready():
    setup_environment()
    create_scene()

func setup_environment():
    # 环境光
    var env = Environment.new()
    env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
    env.ambient_light_color = Color(0.1, 0.1, 0.15)
    env.ambient_light_energy = 0.5

    # 天空
    var sky = Sky.new()
    var sky_mat = ProceduralSkyMaterial.new()
    sky_mat.sky_top_color = Color(0.3, 0.5, 0.8)
    sky_mat.sky_horizon_color = Color(0.6, 0.7, 0.9)
    sky.sky_material = sky_mat
    env.sky = sky

    # 色调映射
    env.tonemap_mode = Environment.TONE_MAP_ACES
    env.tonemap_exposure = 1.0

    # SSAO
    env.ssao_enabled = true
    env.ssao_radius = 1.0

    # 后处理
    env.glow_enabled = true
    env.glow_intensity = 0.3

    var world_env = WorldEnvironment.new()
    world_env.environment = env
    add_child(world_env)

func create_scene():
    # 地面
    var ground = StaticBody3D.new()
    var ground_mesh = MeshInstance3D.new()
    var plane = PlaneMesh.new()
    plane.size = Vector2(50, 50)
    ground_mesh.mesh = plane
    ground.add_child(ground_mesh)

    var shape = CollisionShape3D.new()
    var plane_shape = WorldBoundaryShape3D.new()
    shape.shape = plane_shape
    ground.add_child(shape)
    add_child(ground)

第/三人称相机

第三人称相机

extends Camera3D

@export var target_path: NodePath
@export var distance: float = 5.0
@export var height: float = 3.0
@export var smooth_speed: float = 5.0

@onready var target: Node3D = get_node(target_path)

func _process(delta):
    if not target:
        return

    var target_pos = target.global_position
    var desired_pos = target_pos + Vector3(0, height, distance)

    # 平滑跟随
    global_position = global_position.lerp(desired_pos, smooth_speed * delta)

    # 始终看向目标
    look_at(target_pos + Vector3(0, 1.5, 0))  # 略高于目标脚底

第一人称相机

extends Camera3D

@export var mouse_sensitivity: float = 0.002
var rotation_x: float = 0.0
var rotation_y: float = 0.0

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _input(event):
    if event is InputEventMouseMotion:
        rotation_y -= event.relative.x * mouse_sensitivity
        rotation_x -= event.relative.y * mouse_sensitivity
        rotation_x = clamp(rotation_x, deg_to_rad(-89), deg_to_rad(89))

        rotation.y = rotation_y
        rotation.x = rotation_x

    if event.is_action_pressed("ui_cancel"):
        Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

3D 角色基础移动

extends CharacterBody3D

@export var speed: float = 5.0
@export var jump_velocity: float = 4.5
@export var mouse_sensitivity: float = 0.002

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var rot_y: float = 0.0
@onready var camera: Camera3D = $CameraPivot/Camera3D

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _input(event):
    if event is InputEventMouseMotion:
        rot_y -= event.relative.x * mouse_sensitivity
        rotation.y = rot_y

        camera.rotation.x -= event.relative.y * mouse_sensitivity
        camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-80), deg_to_rad(80))

func _physics_process(delta):
    if not is_on_floor():
        velocity.y -= gravity * delta

    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = jump_velocity

    var input_dir = Input.get_vector("left", "right", "forward", "back")
    var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

    if direction:
        velocity.x = direction.x * speed
        velocity.z = direction.z * speed
    else:
        velocity.x = move_toward(velocity.x, 0, speed)
        velocity.z = move_toward(velocity.z, 0, speed)

    move_and_slide()

游戏开发场景

场景 推荐方案
场景搭建 MeshInstance3D + StaticBody3D
角色模型 glTF 导入 + Skeleton3D
程序化地形 SurfaceTool + HeightMap
第一人称 Camera3D + 鼠标输入
第三人称 Camera3D + SpringArm3D
天空盒 WorldEnvironment + Sky

⚠️ 常见陷阱

  1. 旋转使用弧度rotation_degrees 可用角度但内部转弧度
  2. glTF 是推荐格式,FBX 需要额外工具
  3. 法线贴图需要切线空间,导入时确保 “Ensure Tangents” 开启
  4. Mesh 的 material_override 会覆盖所有材质槽,用 set_surface_override_material() 覆盖单个
  5. Node3D 的缩放不要为 0,会导致子节点不可见

扩展阅读