【Godot4.x】Mesh相关知识总结

概述

很早之前发布过一篇关于几何体程序生成的文章,当时对于三角面和网格的构造其实还没有特别深入的认识,直到自己脑海里想到用二维数组和点更新的方式构造2D类型的多边形Mesh结构,也意识到在Godot中其实Mesh不仅是3D网格,也可以构造2D网格。

在查看一些文章以及复习Godot相关的内置文档之后,发现基于三角面的Mesh构造,其基本结构包含两种形式,一种是三角扇,一种是三角带。几乎所有的几何图形和三维立体结构,都可以用这两种结构组合生成。

所以本文是仍然一个很基础的总结,但是后续基于这些总结,能搞出什么,就不一定了。

网格(Mesh)的基础知识

网格分类

按照计算机图形学,网格(Mesh)大致可以分为三角网格(Triangle Mesh)和多边形网格。三角网格的所有面都是由三角形组成的,而多边形网格是由四边形或更多边形组成的。

而在Godot中,Mesh(网格)是一种资源类型,存储三维空间中基于三角面的几何体数据,通常用于3D场景的MeshInstance3D节点显示3D物体。

但同时,在2D场景中,也有对应的MeshInstance2D节点,可以显示2D网格。

Godot并没有提供直接编辑模型网格的能力。网格还是需要使用像Blender这样的3D建模软件来创建,然后再导入到Godot中使用。

三角和三角面

无论是2D平面还是3D空间,都需要使用三个点来定义一个三角形。在Mesh中,每三个点定义一个三角面,这个三角面有正面和背面之分,而正面、背面是由定义它的三个顶点的环绕顺序和最终的三角面法向量决定的。

环绕顺序

当我们定义一组三角形顶点时,我们会以特定的环绕顺序来定义它们,可能是顺时针(Clockwise)的,也可能是逆时针(Counter-clockwise)的。每个三角形由3个顶点所组成,我们会从三角形中间来看,为这3个顶点设定一个环绕顺序。 – 面剔除 - LearnOpenGL CN (learnopengl-cn.github.io)

在这里插入图片描述

Godot 对三角形图元模式的正面使用顺时针环绕顺序。而OpenGL默认将逆时针顶点所定义的三角形当做是正向三角形。

三角扇(Triangle Fan)

巧妙的从一个“共点”出发,可以更轻松的构造连续性的“共边”三角面集合,也就更容易创建多边形。这种基于共点构造的连续相邻三角面结构被称为“三角扇”,可以构造开放和闭合的三角扇结构。

在这里插入图片描述

矩形是三角扇的一种特殊形式,可以拓扑为一般的三角扇形式。

在这里插入图片描述

三角带(Triangle Strip)

还有一种三角面结构交“三角带”,特点是相邻三角形共边

在这里插入图片描述

三角网格数据表示

了解三角网格的两种连续结构——三角带和三角扇之后,就可以基于两种结构进行顶点存储的设计。三角带和三角扇的顶点存储顺序是不一样的。

在这里插入图片描述

一般用两个数组,分别存储顶点数据和三角面数据,它们分别被称为顶点表和索引表。

  • 顶点表:用于顺序存储顶点(不重复)
  • 索引表:用每三个顶点的索引值代表一个三角面

三角带和三角扇如果顶点表顺序得当,就可以暗含三角形的信息。

可以通过遍历形式,快速生成索引表。

比如:

  • 上图左的三角扇,其三角形集合为:[[0,1,2],[0,2,3],[0,3,4],[0,4,5]],其规律十分明显,用遍历顶点表的方式可以快速生成
  • 上图右的三角带,其三角形集合为:[[0,1,2],[3,2,1],[2,3,4],[5,4,3]],其实可以理解为:[[0,1,2],[1,2,3].reverse(),[2,3,4],[3,4,5].reverse()],也很容易通过遍历顶点表生成。

三角带与三角扇的顶点数与三角形的关系

顶点数三角形数
31
42
53
64

可以看到无论是三角带还是三角扇,都符合顶点数 = 三角形数 + 2的规律。

三角带与三角扇网格生成函数

# 通过顶点表返回三角扇结构的三角形集合
func triangle_fan_3d(vetexs:PackedVector3Array) -> Array[PackedVector3Array]:var triangles:Array[PackedVector3Array]if vetexs.size() >2:for i in range(vetexs.size()-2):var tri:PackedVector3Array = [vetexs[0],vetexs[i+1],vetexs[i+2]]triangles.append(tri)return triangles# 通过顶点表返回三角带结构的三角形集合
func triangle_strip_3d(vetexs:PackedVector3Array) -> Array[PackedVector3Array]:var triangles:Array[PackedVector3Array]if vetexs.size() >2:for i in range(vetexs.size()-2):var tri:PackedVector3Arrayif i % 2 == 0: # 奇数项tri = [vetexs[i],vetexs[i+1],vetexs[i+2]]else:tri = [vetexs[i],vetexs[i+1],vetexs[i+2]]tri.reverse()  # 翻转triangles.append(tri)return triangles

测试:

@tool
extends EditorScriptvar points:PackedVector3Array = [Vector3(0,0,0),Vector3(0,1,0),Vector3(1,1,0),Vector3(1,0,0),
]func _run() -> void:print(triangle_fan_3d(points))

打印输出:

[[(0, 0, 0), (0, 1, 0), (1, 1, 0)], [(0, 0, 0), (1, 1, 0), (1, 0, 0)]]
@tool
extends EditorScriptvar points:PackedVector3Array = [Vector3(0,0,0),Vector3(0,1,0),Vector3(2,0,0),Vector3(0,3,0),
]func _run() -> void:print(triangle_strip_3d(points))

输出:

[[(0, 0, 0), (0, 1, 0), (2, 0, 0)], [(0, 3, 0), (2, 0, 0), (0, 1, 0)]]

注意

这里为了方便演示效果,直接获取了三角形的顶点组合,实际中,主需要生成顶点索引组成的三角形列表就可以了。


顶点着色

三角网格的每个顶点都可以设置一个单独的颜色。

贴图与UV坐标

三角网格的每个顶点都可以设置一个UV坐标,用于对应贴图的一部分。

法向量与切向量

平滑组

程序式几何体生成

涉及SurfaceToolArrayMeshImmediateMesh以及 MeshDataTool等内部类。

使用SurfaceTool

在这里插入图片描述
在这里插入图片描述

依次添加顶点形式

@tool
extends Node3D@onready var mesh_instance_3d: MeshInstance3D = $MeshInstance3D
@onready var mesh_instance_2d: MeshInstance2D = $MeshInstance2Dfunc _enter_tree() -> void:await ready	var sf = SurfaceTool.new()sf.begin(Mesh.PRIMITIVE_TRIANGLES)# 添加第1点sf.set_color(Color.AQUA)sf.set_uv(Vector2(0,0))sf.add_vertex(Vector3(0,0,0))# 添加第2点sf.set_color(Color.AQUAMARINE)sf.set_uv(Vector2(0,1))sf.add_vertex(Vector3(0,1,0))# 添加第3点sf.set_color(Color.RED)sf.set_uv(Vector2(1,1))sf.add_vertex(Vector3(1,1,0))mesh_instance_2d.mesh = sf.commit()

在这里插入图片描述

  • 顶点颜色在MeshInstance3D不起作用,在MeshInstance2D中起作用
  • MeshInstance2D中,要让纹理起作用,必须在添加顶点之前用set_uv()设定UV坐标

)

用数组形式添加

@tool
extends Node3D@onready var mesh_instance_3d: MeshInstance3D = $MeshInstance3D
@onready var mesh_instance_2d: MeshInstance2D = $MeshInstance2Dfunc _enter_tree() -> void:await ready	var sf = SurfaceTool.new()sf.begin(Mesh.PRIMITIVE_TRIANGLES)# 多个三角形组成的三角扇var triangles:= [# 第1个三角形Vector3(0,0,0),Vector3(0,1,0),Vector3(1,1,0)]var uvs:=[# 第1个三角形对应顶点的UV坐标Vector2(0,0),Vector2(0,1),Vector2(1,1)]sf.add_triangle_fan(triangles,uvs)mesh_instance_3d.mesh = sf.commit()mesh_instance_2d.mesh = sf.commit()

创建2个三角面组成矩形

@tool
extends Node3D@onready var mesh_instance_3d: MeshInstance3D = $MeshInstance3D
@onready var mesh_instance_2d: MeshInstance2D = $MeshInstance2Dfunc _enter_tree() -> void:await ready	var sf = SurfaceTool.new()sf.begin(Mesh.PRIMITIVE_TRIANGLES)# 多个三角形组成的三角扇var triangles:= [# 第1个三角形Vector3(0,0,0),Vector3(0,1,0),Vector3(1,1,0),# 第2个三角形Vector3(1,1,0),Vector3(1,0,0),Vector3(0,0,0),]var uvs:=[# 第1个三角形对应顶点的UV坐标Vector2(0,0),Vector2(0,1),Vector2(1,1),# 第2个三角形对应顶点的UV坐标Vector2(1,1),Vector2(1,0),Vector2(0,0),]sf.add_triangle_fan(triangles,uvs)mesh_instance_3d.mesh = sf.commit()mesh_instance_2d.mesh = sf.commit()

在这里插入图片描述

使用“共点”和构造扇面的思维,顶点和uv数据都可以大大简化。

# 不重复的顶点
var vertexs = [Vector3(0,0,0),Vector3(0,1,0),Vector3(1,1,0),Vector3(1,0,0),
]
# UV坐标
var uv_arr = [Vector2(0,0),Vector2(0,1),Vector2(1,1),Vector2(1,0),
]# 多个三角形组成的三角扇
var triangles:= [# 第1个三角形vertexs[0],vertexs[1],vertexs[2],# 第2个三角形vertexs[0],vertexs[2],vertexs[3],
]var uvs:=[# 第1个三角形对应顶点的UV坐标uv_arr[0],uv_arr[1],uv_arr[2],# 第2个三角形对应顶点的UV坐标uv_arr[0],uv_arr[2],uv_arr[3],
]

直接构造ArrayMesh实例

@tool
extends Node3D@onready var mesh_instance_3d: MeshInstance3D = $MeshInstance3D
@onready var mesh_instance_2d: MeshInstance2D = $MeshInstance2Dfunc _enter_tree() -> void:await ready	# 多个三角形组成的三角扇var triangles:PackedVector3Array= [# 第1个三角形Vector3(0,0,0),Vector3(0,1,0),Vector3(1,1,0),# 第2个三角形Vector3(1,1,0),Vector3(1,0,0),Vector3(0,0,0),]var uvs:PackedVector2Array=[# 第1个三角形对应顶点的UV坐标Vector2(0,0),Vector2(0,1),Vector2(1,1),# 第2个三角形对应顶点的UV坐标Vector2(1,1),Vector2(1,0),Vector2(0,0),]var mesh = ArrayMesh.new()var arr = []arr.resize(Mesh.ARRAY_MAX)arr[Mesh.ARRAY_VERTEX] = trianglesarr[Mesh.ARRAY_TEX_UV] = uvsmesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,arr)mesh_instance_3d.mesh = meshmesh_instance_2d.mesh = mesh

可以看到,Mesh的核心数据,是一个固定结构的二维数组,其每个子数组分别对应不同的数据信息。ArrayMesh通过这样的数组,可以直接创建Mesh也就不足为奇。

var arr = [[],  # 0,ARRAY_VERTEX[],  # 1,ARRAY_NORMAL[],  # 2,ARRAY_TANGENT[],  # 3,ARRAY_COLOR[],  # 4,ARRAY_TEX_UV[],  # 5,ARRAY_TEX_UV2[],  # 6,ARRAY_CUSTOM0[],  # 7,ARRAY_CUSTOM1[],  # 8,ARRAY_CUSTOM2[],  # 9,ARRAY_CUSTOM3[],  # 10,ARRAY_BONES[],  # 11,ARRAY_WEIGHTS[],  # 12,ARRAY_INDEX
] 

其中:顶点数组可以是PackedVector2Array也可以是PackedVector3Array

ImmediateMesh

在这里插入图片描述

示例:

@tool
extends Node3D@onready var mesh_instance_3d: MeshInstance3D = $MeshInstance3D
@onready var mesh_instance_2d: MeshInstance2D = $MeshInstance2Dfunc _enter_tree() -> void:await ready	# 多个三角形组成的三角扇var triangles:PackedVector3Array= [# 第1个三角形Vector3(0,0,0),Vector3(0,1,0),Vector3(1,1,0),# 第2个三角形Vector3(1,1,0),Vector3(1,0,0),Vector3(0,0,0),]var uvs:PackedVector2Array=[# 第1个三角形对应顶点的UV坐标Vector2(0,0),Vector2(0,1),Vector2(1,1),# 第2个三角形对应顶点的UV坐标Vector2(1,1),Vector2(1,0),Vector2(0,0),]var mesh = ImmediateMesh.new()mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES)  # 开始创建表面for i in range(triangles.size()):mesh.surface_set_uv(uvs[i])               # 设定顶点UV坐标mesh.surface_add_vertex(triangles[i])     # 添加顶点mesh.surface_end()                            # 结束mesh_instance_3d.mesh = meshmesh_instance_2d.mesh = mesh

参考

  • 面剔除 - LearnOpenGL CN (learnopengl-cn.github.io)
  • Godot4.3官方文档和引擎内置文档
  • 基本3D图形:多边形网格浅谈 - 知乎 (zhihu.com)
  • 三角网格(Triangle Mesh)与四角mesh网格理解总结 - 知乎 (zhihu.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/879948.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分子动力学工具篇二:Sobtop的安装与使用

文章目录 1. Sobtop的介绍1. Sobtop 的功能和特点2.主要应用场景 2.3.常见问题及解决方法 1. Sobtop的介绍 Sobtop 是一个用于自动生成分子拓扑文件的工具,特别是为 GROMACS 分子动力学模拟准备拓扑结构和参数。它的设计目标是通过自动化过程生成小分子、聚合物或其…

msvcp140.dll0丢失的解决方法,总结6种靠谱的解决方法

再使用计算机的过程中,我们经常会遇到一些错误提示,其中之一就是“msvcp140.dll丢失”。这个问题可能会影响到我们的正常使用,因此需要及时解决。经过一段时间的学习和实践,我总结了以下六种靠谱的解决方法,希望对大家…

Flask-WTF的使用

组织一个 Flask 项目通常需要遵循一定的结构,以便代码清晰、可维护。下面是一个典型的 Flask 项目结构: my_flask_app/ │ ├── app/ │ ├── __init__.py │ ├── models.py │ ├── views.py │ ├── forms.py │ ├── templat…

【HTTP】构造HTTP请求和状态码

状态码 用于响应中,表示响应的结果如何 正确?错误?什么原因? HTTP 中的状态码都是标准约定好的 200 OK 成功了,一切顺利 在抓包到的响应中 404 Not Found 访问的资源(URL 中的路径)没找…

数据结构之线性表——LeetCode:707. 设计链表,206. 反转链表,92. 反转链表 II

707. 设计链表 题目描述 707. 设计链表 你可以选择使用单链表或者双链表,设计并实现自己的链表。 单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。 如果是双向链表,则…

小程序——生命周期

文章目录 运行机制更新机制生命周期介绍应用级别生命周期页面级别生命周期组件生命周期生命周期两个细节补充说明总结 运行机制 用一张图简要概述一下小程序的运行机制 冷启动与热启动: 小程序启动可以分为两种情况,一种是冷启动,一种是热…

低代码门户技术:构建高效应用的全新方式

什么是低代码门户技术? 低代码门户技术是一种利用低代码平台构建企业门户网站或应用的技术。门户通常是企业内部和外部用户访问信息和应用的集中平台。低代码门户技术通过图形化界面和预置组件,允许用户快速搭建和定制这些门户平台,而无需深…

CefSharp_Vue交互(Element UI)_WinFormWeb应用(3)---通过页面锁屏和关机(含示例代码)

一、预览 实现功能:通过vue标题栏按钮锁屏和关机 1.1 预览 1.2 代码 锁屏代码csharp LockWorkStation() 关机代码chsharp 注意vue代码参数和此参数一致(0/1/2) 方法ExitWindowsEx()

mac电脑打不开rar文件怎么办 rar文件怎么转换成zip并打开

rar文件是一种常见的压缩文件格式,它可以将多个文件或文件夹打包成一个文件,从而节省空间和方便传输。但是,mac系统并没有自带的工具可以直接打开或解压rar文件,那么,mac用户该如何处理rar文件呢? 一、mac电…

初步认识C++模版

前言 在C语言中,我们知道函数的形参需要指定类型,但是在C中,我们可以模版实现各种类型参数的通用函数。 1. 泛型编程 我们通过函数重载实现多种类型的同一作用的函数。如交换函数: void Swap(int& left, int& right) …

微信getUserProfile不弹出授权框

当我们在微信小程序开发工具中想要使用getUserProfile来获取个人信息的时候,会发现不弹出授权框,这是什么原因呢? 早在2022年的小程序官方公告中就已经明确给出了小程序用户头像昵称获取规则调整公告 因此如果还想继续使用getUserProfile的弹…

功能测试干了三年,快要废了。。。

8年前刚进入IT行业,到现在学习软件测试的人越来越多,所以在这我想结合自己的一些看法给大家提一些建议。 最近聊到软件测试的行业内卷,越来越多的转行和大学生进入测试行业,导致软件测试已经饱和了,想要获得更好的待遇…

2024华为杯研赛E题保姆级教程思路分析

E题题目:高速公路应急车道紧急启用模型 今年的E题设计到图像/视频处理,实际上,E题的难度相对来说较低,大家不用畏惧视频的处理,被这个吓到。实际上,这个不难,解决了视频的处理问题,…

2024/9/21 408 20题

a b 58-130-180-199-42-15:c d a 184-182-187-176-19941 c d a a c b d c a c b c c c

MySQL基础基础篇 - SQL

01 SQL通用语法 02 SQL分类 03 DDL语句 04 DML语句 05 DQL语句(单表查询) 05_01 学习总览 05_02 基本查询 05_03 条件查询 【应用实例】: 05_04 聚合函数 05_05 分组查询 05_06 排序查询 05_07 分页查询 【boss题目】: 05_08 执行顺序 06 DCL语句 【概…

直接插入排序(C语言实现)

目录 1.直接插入排序介绍 2.实现思路 3.动图展示 4.代码实现 (升序) 单趟排序实现 单趟排序代码 直接插入排序函数 5.代码测试 6.时空复杂度分析 时间复杂度O(N^2) 空间复杂度O(1) 1.直接插入排序介绍 插入排序,又叫直接插入排序。…

61. 旋转链表【 力扣(LeetCode) 】

零、原题链接 61. 旋转链表 一、题目描述 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 二、测试用例 示例 1: 输入:head [1,2,3,4,5], k 2 输出:[4,5,1,2,3]示例 2: 输入…

AI小项目4-用Pytorch从头实现Transformer(详细注解)

目录 一、前期准备工作学习如何读AI论文读Transformer原始论文用Pytorch从头实现Transformer 二、我的完整代码实现1.导入库2.基本组件创建词嵌入位置嵌入自注意力 3.编码器4.解码器5.完整架构6.简单测试一下代码创建模型和准备简单的训练数据训练一次(前向传播&…

Istio下载及安装

Istio 是一个开源的服务网格,用于连接、管理和保护微服务。以下是下载并安装 Istio 的步骤。 官网文档:https://istio.io/latest/zh/docs/setup/getting-started/ 下载 Istio 前往Istio 发布页面下载适用于您的操作系统的安装文件,或者自动…

Python数据分析与可视化(Python绘图详解)

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…