当前位置: 首页 > news >正文

Godot4.2小白也能懂:用SurfaceTool从画一个三角形到生成你的第一个3D模型

Godot4.2小白也能懂:用SurfaceTool从画一个三角形到生成你的第一个3D模型

刚接触Godot引擎的3D功能时,看到"顶点"、"法线"、"UV"这些术语总让人望而生畏。但你知道吗?用代码创造自定义3D模型其实就像搭积木一样有趣。本文将带你从最基础的三角形开始,逐步构建复杂的3D物体,整个过程就像在数字世界里玩建模橡皮泥。

1. 准备工作:搭建你的第一个3D实验室

在开始建模前,我们需要一个简单的测试环境。打开Godot4.2,创建一个新场景并添加一个MeshInstance3D节点——这就是我们的3D画布。接着创建一个EditorScript脚本,它能在编辑器里实时显示我们的创作成果。

@tool extends EditorScript func _run(): var mesh_instance = get_scene().get_node("MeshInstance3D") mesh_instance.mesh = create_mesh() func create_mesh(): var st = SurfaceTool.new() # 这里将填充我们的建模代码 return st.commit()

这个框架就像画家的空白画布,create_mesh()函数就是我们的调色板。SurfaceTool是Godot提供的强大建模工具,它允许我们通过代码精确控制每个顶点。

提示:使用@tool修饰脚本可以让它在编辑器中实时运行,这是调试3D模型的绝佳方式

2. 从三角形开始:3D建模的"Hello World"

2.1 绘制第一个彩色三角形

三角形是3D世界的原子,所有复杂模型都由它构成。让我们创建一个彩色三角形:

func create_mesh(): var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) # 设置材质(让三角形有颜色) var material = StandardMaterial3D.new() material.albedo_color = Color(1, 0, 0) # 红色 st.set_material(material) # 第一个顶点(左下角) st.set_normal(Vector3(0, 0, 1)) st.set_uv(Vector2(0, 0)) st.add_vertex(Vector3(-1, -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)) return st.commit()

运行这段代码,你会看到一个红色的直角三角形。这里有几个关键点:

  • begin(Mesh.PRIMITIVE_TRIANGLES):声明我们要绘制三角形
  • set_normal:定义表面的朝向(影响光照效果)
  • set_uv:设置纹理坐标(即使现在没使用纹理)
  • add_vertex:添加顶点位置

2.2 理解顶点顺序的重要性

3D模型有正反面之分,由顶点顺序决定(顺时针或逆时针)。试着改变顶点顺序:

# 修改后的顶点顺序(逆时针) st.add_vertex(Vector3(1, 1, 0)) # 右上 st.add_vertex(Vector3(-1, 1, 0)) # 左上 st.add_vertex(Vector3(-1, -1, 0))# 左下

你会发现三角形"消失"了——其实它还在,只是背面朝向我们。在Godot中,默认只渲染正面。

3. 进阶挑战:从三角形到立方体

3.1 构建四边形:两个三角形的组合

四边形实际上是两个三角形组成的。让我们创建一个正方形:

func create_square(): var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) # 第一个三角形 add_square_vertex(st, Vector3(-1, -1, 0), 0) # 左下 add_square_vertex(st, Vector3(-1, 1, 0), 1) # 左上 add_square_vertex(st, Vector3(1, 1, 0), 2) # 右上 # 第二个三角形 add_square_vertex(st, Vector3(1, 1, 0), 2) # 右上 add_square_vertex(st, Vector3(1, -1, 0), 3) # 右下 add_square_vertex(st, Vector3(-1, -1, 0), 0) # 左下 return st.commit() func add_square_vertex(st, position, uv_index): st.set_normal(Vector3(0, 0, 1)) st.set_uv([Vector2(0,0), Vector2(0,1), Vector2(1,1), Vector2(1,0)][uv_index]) st.add_vertex(position)

这里我们引入了辅助函数add_square_vertex来简化重复代码。注意两个三角形如何共享顶点来构成四边形。

3.2 创建立方体:六个面的组合

立方体就是六个四边形(12个三角形)的组合。让我们一步步构建:

func create_cube(): var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) # 定义立方体的8个顶点 var vertices = { "front_bottom_left": Vector3(-1, -1, 1), "front_top_left": Vector3(-1, 1, 1), "front_top_right": Vector3(1, 1, 1), "front_bottom_right": Vector3(1, -1, 1), "back_bottom_left": Vector3(-1, -1, -1), "back_top_left": Vector3(-1, 1, -1), "back_top_right": Vector3(1, 1, -1), "back_bottom_right": Vector3(1, -1, -1) } # 定义六个面 var faces = [ # 前面 { "normal": Vector3(0, 0, 1), "verts": ["front_bottom_left", "front_top_left", "front_top_right", "front_bottom_right"] }, # 右面 { "normal": Vector3(1, 0, 0), "verts": ["front_bottom_right", "front_top_right", "back_top_right", "back_bottom_right"] }, # 后面 { "normal": Vector3(0, 0, -1), "verts": ["back_bottom_right", "back_top_right", "back_top_left", "back_bottom_left"] }, # 左面 { "normal": Vector3(-1, 0, 0), "verts": ["back_bottom_left", "back_top_left", "front_top_left", "front_bottom_left"] }, # 上面 { "normal": Vector3(0, 1, 0), "verts": ["front_top_left", "back_top_left", "back_top_right", "front_top_right"] }, # 下面 { "normal": Vector3(0, -1, 0), "verts": ["front_bottom_left", "front_bottom_right", "back_bottom_right", "back_bottom_left"] } ] # 为每个面添加两个三角形 for face in faces: var v = face["verts"] add_face(st, vertices[v[0]], vertices[v[1]], vertices[v[2]], vertices[v[3]], face["normal"]) return st.commit() func add_face(st, v1, v2, v3, v4, normal): # 第一个三角形 add_cube_vertex(st, v1, normal, Vector2(0, 0)) add_cube_vertex(st, v2, normal, Vector2(0, 1)) add_cube_vertex(st, v3, normal, Vector2(1, 1)) # 第二个三角形 add_cube_vertex(st, v3, normal, Vector2(1, 1)) add_cube_vertex(st, v4, normal, Vector2(1, 0)) add_cube_vertex(st, v1, normal, Vector2(0, 0)) func add_cube_vertex(st, position, normal, uv): st.set_normal(normal) st.set_uv(uv) st.add_vertex(position)

这段代码虽然较长,但结构清晰。我们首先定义立方体的8个顶点,然后定义6个面,每个面由4个顶点组成。最后,每个面被分解为两个三角形。

4. 高级技巧:程序化生成复杂模型

4.1 创建圆柱体:用循环简化代码

理解了基本原理后,我们可以用算法生成更复杂的形状。比如圆柱体:

func create_cylinder(sides=12, height=2, radius=1): var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) # 顶部和底部的中心点 var top_center = Vector3(0, height/2, 0) var bottom_center = Vector3(0, -height/2, 0) # 生成侧面顶点 var top_vertices = [] var bottom_vertices = [] for i in range(sides): var angle = 2 * PI * i / sides var x = radius * cos(angle) var z = radius * sin(angle) top_vertices.append(Vector3(x, height/2, z)) bottom_vertices.append(Vector3(x, -height/2, z)) # 绘制顶部圆面 for i in range(sides): var normal = Vector3(0, 1, 0) st.set_normal(normal) st.add_vertex(top_center) st.set_normal(normal) st.add_vertex(top_vertices[i]) st.set_normal(normal) st.add_vertex(top_vertices[(i+1)%sides]) # 绘制底部圆面 for i in range(sides): var normal = Vector3(0, -1, 0) st.set_normal(normal) st.add_vertex(bottom_center) st.set_normal(normal) st.add_vertex(bottom_vertices[(i+1)%sides]) st.set_normal(normal) st.add_vertex(bottom_vertices[i]) # 绘制侧面 for i in range(sides): var next_i = (i+1)%sides # 法线方向为顶点方向的水平分量 var normal_top = Vector3(top_vertices[i].x, 0, top_vertices[i].z).normalized() var normal_bottom = Vector3(bottom_vertices[i].x, 0, bottom_vertices[i].z).normalized() # 第一个三角形 st.set_normal(normal_top) st.add_vertex(top_vertices[i]) st.set_normal(normal_bottom) st.add_vertex(bottom_vertices[i]) st.set_normal(normal_top) st.add_vertex(top_vertices[next_i]) # 第二个三角形 st.set_normal(normal_top) st.add_vertex(top_vertices[next_i]) st.set_normal(normal_bottom) st.add_vertex(bottom_vertices[i]) st.set_normal(normal_bottom) st.add_vertex(bottom_vertices[next_i]) return st.commit()

这个函数通过改变sides参数可以控制圆柱体的面数,值越大圆柱越圆滑。这就是程序化建模的魅力——通过算法自动生成几何形状。

4.2 实用技巧:优化模型性能

当创建复杂场景时,模型优化很重要。SurfaceTool提供了一些优化方法:

# 在commit前调用可以优化网格 st.generate_normals() # 自动生成法线 st.generate_tangents() # 自动生成切线(用于法线贴图) st.optimize_indices() # 优化顶点索引

另一个重要技巧是共享顶点数据。在立方体例子中,每个顶点实际上被多个面共享,但我们在每个面都重复定义了它。更高效的方式是:

  1. 创建顶点列表
  2. 定义面时引用顶点索引
  3. 使用st.index()来重用顶点
# 创建顶点列表 var vertices = [v1, v2, v3, ...] # 添加顶点时使用索引 st.add_vertex(vertices[0]) st.add_vertex(vertices[1]) st.add_vertex(vertices[2]) st.index() # 标记当前顶点组合 # 下次使用相同顶点时 st.set_index(0) # 重用第一个顶点

5. 实战应用:创建自定义3D道具

现在你已经掌握了基础,让我们创建一个简单的游戏道具——一把宝剑:

func create_sword(): var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) # 剑柄(圆柱体) create_cylinder_segment(st, height=1, radius=0.1, segments=8, y_offset=-0.5) # 护手(十字形) create_cross_guard(st) # 剑身(细长棱柱) create_blade(st, length=2, width=0.3, thickness=0.05) # 剑尖(金字塔形) create_tip(st) return st.commit() func create_cylinder_segment(st, height, radius, segments, y_offset): # 类似前面的圆柱体代码,但只生成一段 pass func create_cross_guard(st): # 创建十字形护手 pass func create_blade(st, length, width, thickness): # 创建剑身 pass func create_tip(st): # 创建剑尖 pass

虽然这里省略了具体实现代码,但你可以看到如何将基本形状组合成复杂模型。这就是专业3D建模软件背后的基本原理。

http://www.jsqmd.com/news/732262/

相关文章:

  • 3D高斯泼溅与VolSplat:体素对齐的新视角合成技术
  • 2026金属圆锯机厂家合集:专业高速圆锯机厂家汇总 - 栗子测评
  • [题解]2026杭电DEBUG杯完整题解
  • MedMNIST医疗图像数据集深度解析:从标准基准到医学AI实战指南
  • Spring Boot 3.5 + MyBatis Plus + RabbitMQ:打造 AI 驱动的慢 SQL 监控与优化系统
  • C语言 宏嵌套的展开规则
  • 基于DINOv3、Swin Transformer、FastViT、ResNet的场景识别模型
  • 从`/proc/interrupts`输出看网络性能:以Realtek RTL8125网卡的中断风暴排查为例
  • 鑫豆娘豆腐店加盟——正规品牌护航,开一家火一家的刚需创业好项目 - 奔跑123
  • 把 SNC PSE 创建对,别让 STRUST 成为你上线前最后一个拦路虎
  • 雀魂牌谱屋完全指南:3步开启你的麻将数据分析之旅
  • 上海写字楼安保公司哪家好?2026正规商场/园区安保外包公司实力权威推荐 - 栗子测评
  • 从OBS插件到采集卡:聊聊那些伪装成‘正经软件’的AI自瞄,以及反作弊如何‘抓鬼’
  • 配置路径 + 数据路径架构
  • 2025届学术党必备的六大降重复率网站推荐
  • 【flutter for open harmony】第三方库Flutter 鸿蒙版 物流追踪 实战指南(适配 1.0.0)✨
  • 如何用3种方法让Mem Reduct显示中文界面?技术选型与实施指南
  • 2026江苏/南京安保服务哪家好?本地学校/商场安保服务商精选榜单 - 栗子测评
  • 企业如何利用Taotoken统一管理多个AI模型的API密钥与访问权限
  • 企业内如何构建安全可控的大模型API调用与管理体系
  • 在 Node.js 服务中集成多模型能力借助 Taotoken 统一 API 调用
  • GPT-5.5写文案、改稿、做大纲,写作全流程实测
  • 为什么85%的中文玩家都在寻找MASA模组汉化包?终极解决方案来了
  • 海外市场“可解释金融智能体”受关注,国内IT服务商如何参与竞争
  • CL9095 500mA输出可调线性稳压器
  • ComfyUI-Impact-Pack终极指南:解锁AI图像精细化处理的完整工作流
  • 把 ABAP 平台上的 SNC 真正配通,使用 SAP Cryptographic Library 完成 PSE、密钥对与应用服务器落地
  • 18年GitHub老用户因平台故障频发迁出项目,直言:若改进仍愿回归
  • 如何在10分钟内搭建高效的PlantUML Server?[特殊字符]
  • 安卓基础之《(29)—消息机制与异步任务》