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

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

3D 基础与网格

概述

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

核心节点说明
Node3D3D 空间基类(位置、旋转、缩放)
MeshInstance3D网格实例显示
Camera3D3D 相机
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管状尾迹
TextMesh3D 文字

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_scaleUV 缩放
uv1_offsetUV 偏移
texture_filter纹理过滤模式
proximity_fade_enabled近距离淡出
distance_fade_mode远距离淡出

纹理类型

纹理类型用途说明
CompressedTexture2D标准纹理PNG/WebP 导入
ImageTexture运行时创建代码生成
NoiseTexture2D噪声纹理地形、云
GradientTexture1D1D 渐变颜色映射
GradientTexture2D2D 渐变背景
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,会导致子节点不可见

扩展阅读