阿赵的MaxScript学习笔记分享十二《获取和导出各种数据》

news2024/12/24 22:12:31

大家好,我是阿赵,周日的早上继续分享MaxScript学习笔记,这是第十二篇,获取和导出各种数据

1、导出数据的目的

使用3DsMax建立3D模型后,很多时候需要输出模型到别的引擎去使用,常用的格式有Obj、FBX、SLT等。有时候,这些通用的格式里面的数据不够,比如obj里面就没有带顶点颜色。有时候是因为某些格式的解析比较复杂,比如FBX,之类,我们会需要根据自己的需要来导出各种的数据。
一般来说,导出模型数据会包括以下这些:
顶点坐标
UV
UV2
顶点颜色
顶点法线
面的索引信息
蒙皮信息
材质贴图
动画关键帧

首先要明白一个概念,比如一个box,他是由8个顶点、6个四角面组成的。但在导出到引擎使用的时候,其实他是24个点,12个面的,因为游戏引擎绘制的一般是三角面,所以一个四角面会拆分成2个三角面。然后从UV和法线的角度,同一个顶点,有可能是被多个面同时使用的,所以顶点在属于不同的面的时候,顶点的UV坐标和法线方向也是不一样的。
所以,在导出数据的时候,可以使用自己的方式去处理数据。其中一个方法是可以参数obj格式,他把重复的顶点、UV坐标、法线方向进行合并,每个数据有一个索引index,然后在记录面数据的时候,比如一个box有12个面,他会记录12个面的信息,每个面有3个顶点,每个顶点包含了顶点索引、法线索引和uv索引的数据。这样操作下来,存储的数据量最少,也不会丢失任何数据。当然如果想把顶点颜色的数据也加进去,也可以再增加一个顶点颜色的索引,把它带在面的顶点数据里面导出。
下面还是以一个box为思考的参考,列出了获取各种信息的方法,结合着之前讲过的字符串操作方法,和文件读写方法,就可以根据自己的需要取导出自己的模型格式了

2、各种数据的获取方式举例

--获得网格顶点数
GetNumVerts boxt1.mesh
8
--获得某个通道的顶点数据数量,1通道代表UV通道,
--这里我能用1通道,是因为我已经展过UV了,如果没有展过UV,会报错
polyop.getNumMapVerts boxt1 1
24
--获得实际的三角面数
GetNumFaces boxt1.mesh
12
--获得第一个顶点的坐标
getVert boxt1.mesh 1
[-12.5,-12.5,0]
--获得第12个面使用了哪几个顶点,对应网格顶点数的索引
getface boxt1.mesh 12
[3,1,5]
--获得第12个面的法线方向
GetFaceNormal boxt1.mesh 12
[-1,0,0]
--获得第12个面引用的UV索引,对应UV坐标索引
GetTVFace boxt1.mesh 12
[9,10,11]
--获得第9个UV索引点的UV坐标
getTVert boxt1.mesh 9
[0.0005,0.0005,0]

特别说一下法线的获取方式和设置方式

法线的获取和赋值比较特殊一点,需要注意以下几点:

1.如果不通过Edit_Normals修改器来获取,法线的数据是不正确的

box02 = box()
$Box:Box003 @ [0.000000,0.000000,0.000000]

converttopoly box02
$Editable_Poly:Box003 @ [0.000000,0.000000,0.000000]

faceNum = polyop.getnumfaces box02
6
for i in 1 to faceNum do
(
local faceNormal = polyop.getfacenormal box02 i
print faceNormal
)
[0,0,-1]
[0,0,1]
[0,-1,0]
[1,0,0]
[0,1,0]
[-1,0,0]
OK

vertNum = box02.numverts
8
for i in 1 to vertNum do
(
	local vertNormal = getNormal box02.mesh i
	print vertNormal
)
[0,0,-1.5708]
[0,0,-1.5708]
[0,0,-1.5708]
[0,0,-1.5708]
[0,0,1.5708]
[0,0,1.5708]
[0,0,1.5708]
[0,0,1.5708]
OK

通过以上的代码,似乎可以通过polyop来获取某个面的法线方向,然后也可以用点的index通过getNormal的方法来获取到mesh里面某个点的法线方向。
但其实这个获取的方法是有问题的
(1)首先来说这个面的法线,这个面的发现方向,从一个box的4个面的角度看,是正确的,但如果我们通过添加Edit_Normals编辑器,修改其中一个面的法线,然后再次打印6个面的法线方向,会发现结果根本没有变化。所以我个人感觉,这个从polyop里面获取的面法线方向,并不是通过4个顶点的法线方向计算出来的值,只是通过4个顶点的坐标位置,算出来的一个法线方向。如果在不修改顶点法线的情况下,应该是可以用的,但我们谈论的获取法线方向,一般是指顶点的法线方向,所以这个面的法线方向其实没什么意义
(2)再来看通过getNormal方法获得的顶点法线,会发现这个值有点莫名其妙,首先,8个点只有2种法线方向,然后,这个法线也没有归一化,长度并不是1的,所以这个获取的结果,我觉得也是并没有什么用途的。实际上,一个顶点有可能不止一个法线方向的,下面会继续分析。

2.一个顶点具体包含多少个法线,要看具体的平滑组设置

在这里插入图片描述

box03 = box()
$Box:Box003 @ [0.000000,0.000000,0.000000]
converttopoly box03
$Editable_Poly:Box003 @ [0.000000,0.000000,0.000000]
select box03
OK
modPanel.addModToSelection (Edit_Normals()) ui:on
OK

通过一段代码,创建一个box,然后添加了一个Edit_Normals的修改器,这时候我们可以看到场景里面的box每个顶点上面伸出了3条线,这些线,其实就是法线了。由于一个box有8个顶点,所以这个时候,应该有24条法线。
为什么会有24条这么多呢?那是因为这个box每个面的光滑组都不一样,所以面与面之间,是硬转角,而虽然我们看来这个box只有8个顶点,但每个顶点同时被3个面共用,所以一个顶点需要3个法线方向来分别代表组成不同面时,该顶点的法线方向。
在这里插入图片描述

这时候,做一下修改,返回Editable Poly,选择所有面,把它们的光滑组都改成1
在这里插入图片描述

再回到Edit_Normals,这时候,可以看到每个顶点只有1根法线了。这究竟是真的一个顶点变成1根法线,还是由于同一个顶点的法线都重叠在一起了呢?我们可以通过代码去验证一下。

local myVerts = #{1,2,3,4,5,6,7,8}
local myNormals = #{}
local modN = box03.modifiers[1]
modN.ConvertVertexSelection &myVerts&myNormals
print myNormals

这里有一段代码,由于现在box03的第一个修改器就是Edit_Normals,所以modN获取了这个Edit_Normals修改器,然后通过ConvertVertexSelection ,获取指定的myVerts的8个顶点使用到的法线id到myNormals数组
当把box03的光滑组取消的时候,得到的打印结果是:
#{1…24}
当把box03的所有面光滑组设置成1后,得到的打印结果是:
#{1…8}
很明显,在把所有面的光滑组设置成1后,这个box03的法线方向的确是只剩下了8个。于是我们能得出结论,模型的一个顶点具体有多少个法线方向,是根据面的光滑组来分的。或者换个说法,面与面之间是硬边过渡,还是光滑过渡,是看组成面之间的边缘的顶点,是否共用法线。

3.通过Edit_Normals修改器来获取法线数据

通过了上面的实验,我们知道,只有通过Edit_Normals修改器,才能比较准确的获得和修改模型的法线。所以接下来就使用这个修改器来获取数据。
具体的步骤是这样的?
(1)通过ConvertVertexSelection 方法,获取到所有的NormalId
(2)通过GetFaceDegree方法逐个面获取当前的面有多少个不同角度的点组成
(3)通过GetNormalID方法,获得组成每个面每个顶点的对应NormalId
(4)根据1和4的结果,就可以知道每个顶点的发现方向了
具体代码:

local vertNum = box03.numVerts
	local myVerts = #{1..vertNum}
	local myNormals = #{}
	local modN = box03.modifiers[1]
	--通过ConvertVertexSelection把所有顶点用到的normalId存到myNormals数组
	modN.ConvertVertexSelection &myVerts&myNormals
	--通过GetNormal方法把每一个normalId对应的法线方向,存到normalArr里面
	local normalArr = #()
	for i in myNormals do
	(
		local norPos = modN.GetNormal i
		normalArr[i] = norPos

	)
	print "法线集合:"
	print normalArr
	--获得面的数量
	local faceNum = modN.GetNumFaces()
	local faceNormalIdList = #()
	for i in 1 to faceNum do
	(
		--corner的含义可以理解成这个面由多少个不同角度的点组成
		local corners = modN.GetFaceDegree i
		--遍历组成面的这些conner,通过GetNormalID方法,获得组成这个面的顶点对应的normalId
		local tempFaceNormalList = #()
		for j in 1 to corners do
		(
			local norInd = modN.GetNormalID i j
			append tempFaceNormalList norInd
		)
		faceNormalIdList[i] = tempFaceNormalList
		
	)	
	print "面对应法线Id"
	print faceNormalIdList

运行的结果:

"法线集合:"
[0,0,-1]
[0,0,-1]
[0,0,-1]
[0,0,-1]
[0,0,1]
[0,0,1]
[0,0,1]
[0,0,1]
[0,-1,0]
[0,-1,0]
[0,-1,0]
[0,-1,0]
[1,0,0]
[1,0,0]
[1,0,0]
[1,0,0]
[0,1,0]
[0,1,0]
[0,1,0]
[0,1,0]
[-1,0,0]
[-1,0,0]
[-1,0,0]
[-1,0,0]
"面对应法线Id"
#(1, 2, 3, 4)
#(5, 6, 7, 8)
#(9, 10, 11, 12)
#(13, 14, 15, 16)
#(17, 18, 19, 20)
#(21, 22, 23, 24)

根据需要,把这些数据保存你的文件里面就可以了。

4.修改法线

顺便提一下怎样修改法线。
之前用GetNormalId可以获取到某个点的NormalId,然后用GetNormal可以获得对应NormalId的具体法线方向值。那么反过来,通过SetNormal方法,就可以改变对应NormalId的点的法线方向了,比如,刚才那个例子,需要把第16个NormalId的值改成[0.5,0.6,0.7],可以这样:

modN.SetNormal 16 [0.5,0.6,0.7]

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

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

相关文章

geoserver之BlobStores使用

概述 geoserver是常用的地图服务器之一,除了基本的能力之外,也提供了很多的插件方便大家使用。在本文,讲述一下如何在geoserver中使用BlobStores和gwc-sqlite-plugin插件实现地图的切片和部署。 BlobStores简介 在geoserver中,…

Linux安装Nginx和Nginx基础配置

下载Nginx 方式一:通过官网下载后上传 通过官网下载安装包。下载地址https://nginx.org/en/download.html 这里选择稳定版的进行下载。 这里使用FinalShell终端工具操作,使用其他工具操作亦可。FinalShell工具下载地址:http://www.hostbuf…

带加权的贝叶斯自举法 Weighted Bayesian Bootstrap

在去年的文章中我们介绍过Bayesian Bootstrap,今天我们来说说Weighted Bayesian Bootstrap Bayesian bootstrap 贝叶斯自举法(Bayesian bootstrap)是一种统计学方法,用于在缺乏先验知识的情况下对一个参数的分布进行估计。这种方…

【Node.js】HTTP协议、HTTP的请求报文和响应报文

HTTP协议、HTTP的请求报文和响应报文HTTP协议HTTP主要特点HTTP的请求报文和响应报文请求报文请求行请求消息头空行请求体响应报文响应状态行响应消息头空行响应体总结HTTP协议 HTTP 全称为超文本传输协议,是用于从WWW服务器传输超文本到本地浏览器的传送协议&#…

硬件基础专题-01电阻篇

目录 课程链接 学习目的 电阻 电阻理论讲解 电阻定义​ 欧姆定律​ 电阻单位换算 电阻选型考虑 安装方式 常见电阻值 各种贴片电阻的读取方式 确定电阻值的方法 电阻数据手册 电阻测量 电阻的产品应用​ 零欧姆电阻 热敏电阻 光敏电阻 课程链接 视频专辑 - 硬件…

linux入门---权限

目录标题什么是权限人的分类为什么会有所属组查看文件属性文件的分类如何查看权限文件不同权限的表现rwx权限修改八进制权限修改umask有关内容文件中人的修改目录不同权限的表现rwx什么是权限 首先来看一个例子:比如说我没有爱奇艺的vip,那么我也就没有…

训练CV模型常用的方法与技巧

最近参加一个CV比赛,看到有参赛者分享了自己训练图像识别模型时常用到的小技巧,故对其进行记录、整理,方便未来继续学习。整理了很多,它们不一定每次有用,但请记在心中,说不定未来某个任务它们就发挥了作用…

JavaScript-扫盲

文章目录1. 前言2. 第一个 JavaScript 程序3. javaScript 的基础语法3.1 变量3.2 数据类型3.3 运算符3.4 条件语句3.5 数组3.6 函数3.7 作用域3.8 对象4. WebAPI4.1 DOM 基本概念4.2 常用 DOM API4.3 事件4.4 操作元素4.5 网页版猜数字游戏4.6 留言版1. 前言 提问 java 和 java…

数字图像学笔记 —— 17. 图像退化与复原(自适应滤波之「最小二乘方滤波」)

文章目录维纳滤波的缺点约束最小二乘方滤波给一个实际例子吧维纳滤波的缺点 维纳滤波(Wiener Filter),虽然是一种非常强大的退化图像还原算法,但是从实验过程我们也发现它存在着致命的缺陷,那就是要求输入退化系统的 …

医疗器械之模糊算法(嵌入式部分)

模糊控制 所谓模糊控制,就是对难以用已有规律描述的复杂系统,采用自然语言(如大,中,小)加以描述,借助定性的,不精确的以及模糊的条件语句来表达,模糊控制是一种基于语言的…

Java虚拟机JVM-运行时数据区域说明

及时编译器 HotSpot虚拟机中含有两个即时编译器,分别是编译耗时短但输出代码优化程度较低的客户端编译器(简称为C1)以及编译耗时长但输出代码优化质量也更高的服务端编译器(简称为C2),通常它们会在分层编译…

【Linux】手把手教你在CentOS上使用docker 安装MySQL8.0

文章目录前言一. docker的安装1.1 从阿里下载repo镜像1.2 安装docker1.3 启动docker并查看版本二. 使用docker安装MySQL8.02.1 拉取MySQL镜像2.2 创建容器2.3 操作MySQL容器2.4 远程登录测试总结前言 大家好,又见面了,我是沐风晓月,本文主要…

docker系列1:docker安装

传送门 docker官网地址: Docker: Accelerated, Containerized Application Development 安装地址:Install Docker Engine docker hub地址 docker hub:Docker 安装步骤 前置条件 由于安装docker,需要根据操作系统版本选择…

设计模式(只谈理解,没有代码)

1.什么是设计模式设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。2.为什么要学习设计模式看懂源代码:如果你不懂设计模式去看Jd…

【react全家桶】生命周期

文章目录04 【生命周期】1.简介2.初始化阶段2.1 constructor2.2 componentWillMount(即将废弃)2.3 static getDerivedStateFromProps(新钩子)2.4 render2.5 componentDidMount2.6 初始化阶段总结3.更新阶段3.1 componentWillRecei…

2023最新版本RabbitMQ的持久化和简单使用

上节讲了 RabbitMQ下载安装教程 , 本节主要介绍RabbitMQ的持久化和简单使用。 一、RabbitMQ消息持久化 当处理一个比较耗时得任务的时候,也许想知道消费者(consumers)是否运行到一半就挂掉。在当前的代码中,当RabbitM…

如何在本地跑FuzzBench的实验

概述 FuzzBench是谷歌做的一个评估模糊测试的benchmark,主要目的是为了评测覆盖率导向模糊测试工具能达到多少覆盖率,能够发现多少漏洞。本文主要介绍如何在本地运行fuzzbench的实验。当然也可以用谷歌的云服务来跑,具体教程在这&#xff1a…

计算机底层:BDC码

计算机底层:BDC码 BDC码的作用: 人类喜欢十进制,而机器适合二进制,因此当机器要翻译二进制给人看时,就会进行二进制和十进制的转换,而常规的转换法(k*位权)太麻烦。因此就出现了不同…

基本类型、包装类型、引用类型、String等作为实参传递后值会不会改变?

看了半天帖子,讲得乱七八糟,坑死了 [1] 先说结论 基本类型、包装类型、String类型作为参数传递之后,在方法里面修改他们的值,原值不会改变!引用类型不一定,要看是怎么修改它的。 [2] 为什么基本类型、包装类…

程序设计语言-软件设计(二十一)

数据结构与算法(二十)快速排序、堆排序(四)https://blog.csdn.net/ke1ying/article/details/129269655 这篇主要讲的是 编译与解释、文法、正规式、有限自动机、表达式、传值与传址、多种程序语言特点。 编译的过程 解释型 和 编译型 编译型过程&#…