MAX SDK网格专题

2013-11-20 00:20  473人阅读  评论(0)  收藏  举报
  分类:
max编程(13) 

网格专题

一、网格几何结构

1.1 顶点(Vertex)

MAX中的模型顶点仍然采用常用的顶点数组方式,mesh的顶点数组成员为verts,verts为顶点三维坐标的一维数组, Mesh::numVerts为顶点的数目。

[c-sharp]  view plain copy
  1. int getNumVerts();  //返回当前顶点的数目
  2. BOOL Mesh::setNumVerts(int ct, BOOL keep = FALSE, BOOL synchSel = TRUE);   //该方法用来设置网格顶点的数目,它负责为网格重新分配顶点数组,而keep则标识是否保留以前的顶点数据;synchSel标识是否重置顶点选择状态位数组的长度,这个变量通常为TRUE。
  3. void setVert(int i, const Point3& xyz); //设置顶点的坐标
  4. void setVert(int i, float x, float y, float z); //设置顶点坐标
  5. Point3& getVert(int i); //得到顶点坐标
  6. Point3* getVertPtr(int i); //得到顶点坐标的指针

1.2 面片(Face)

Mesh网格中的面片均为三角形,有专门的Face类描述,Face类记录了一个面片的三个顶点在顶点数组中的索引,三条边的可见性、该面的可见性、所属的光滑组、以及关联的材质ID。而在Mesh类中,面片数组为faces,它是一个Face类型的数组,数组长度由Mesh::numFaces决定。相关联的方法有:

int Mesh::getNumFaces() const        //返回网格面片的数目

BOOL Mesh::setNumFaces(int ct, BOOL keep=FALSE, BOOL synchSel=TRUE);    //设置面片数目并分配面片数组

面片数组分配后,可以设置面片对应的顶点索引,该方法由Face类提供:

void Face::setVerts(int a, int b, int c);

当然Face类提供了设置边可见性、面可见性、光滑组、关联材质ID的存取方法。在Mesh类中,仅仅存在这样一个Face数组,有关面片具体的信息完全由Face类记录。

1.3 边(Edge)

网格除了有顶点和面信息外,当然还存在着边信息,当然,MAX中的也存在着边信息,然而边信息是“虚拟”的。为什么呢?因为面信息中自然就包含了边信息, 因此,Mesh并不需要记录边集,而是在需要的时候创建一个边列表。Edge类为描述边的类,它包含了两个端点的索引,以及关联三角形面片的索引。

1.4 几何元素的状态

在Mesh类中存在着三个选择状态标志位数组,分别是顶点选择状态位数组(vertSel),面片选择状态位数组(faceSel),边选择状态位数组(edgeSel),它们均是BitArray类型(位数组)。显然,vertSel位数组的长度为numVerts,faceSel位数组的长度为numFaces。但是我们刚才已经说过,边集并不存在,那么如何来确定边的索引呢?其实这里的edgeSel的长度为3*numFaces,这怎么来的应该很显然,一个三角形三条边,所以第i个三角形对应的三条边在edgeSel中分别为edgeSel[i*3], edgeSel[i*3+1], and edgeSel[i*3+2]。

另外还有一个位数组记录顶点的显示/隐藏状态,为vertHide,长度为numVerts,如果vertHide[i]为1则标志着该顶点处于隐藏状态。

1.5 几何信息的获取

Mesh类提供了很多方法让我们获取其相关的几何信息,并不仅仅是顶点以及面片信息,包括建立在此之上的其他几何信息,比如我们可以获取面法线或者顶点法线,可以求取两个面之间的角度等等。或者说,Mesh类已经实现了很多基于几何的计算,我们要做的就是调用它提供的方法即可。下面是一些常见的获取信息方法:

• 包围盒信息(Bounding Box): 所谓包围盒在MAX中一般为一个BOX,该BOX在局部坐标系中由两个拐点确定,分别是最低点和最高点。这里不要迷糊,觉得两个点怎么能描述一个BOX呢?仔细想想的话你就会觉得够了。BOX的棱与局部坐标轴平行,通俗点说就是“正”放在坐标系上,所以记录一个最低点(这里的最低点是说x, y, z三个分量都最小)和最高点(x, y, z三个分量均最大)就足以确定了一个包围盒的位置和大小。Mesh::getBoundingBox()方法可以获得当前的包围盒信息。

• 法线信息(Normals): getNormal()/setNormal()可以得到一个顶点的法线(注意,这里的法线可能不是根据几何信息计算的,因为RVertex的法线可以人工指定);getFaceNormal()/setFaceNormal()获取和设置三角形面片的法线(当然,面法线也可以人工指定)。

• 元素信息(Element): 如可以取得相连通的面片集元素,比较容易理解的就是Edit Mesh中的元素选择工具。ElementFromFace()方法可以得到指定范围内的元素信息。

• 面片中心(Center of Face): 也就是所说的三角形重心。虽然比较容易计算,但无论如何Mesh已经提供了FaceCenter()方法。

• 二面角(Angle Between Faces): 这个要比三角形重心难表示一些了,好在Mesh(AngleBetweenFaces())也帮我们算好了。在自己采取某种光滑算法时,二面角的计算可谓是非常频繁。

• 三角形权值(Bary Coordinates): 这样的翻译可能还不够准确,它的实际意义时用三角形的三个顶点来表示三角形所在平面上的任意一个点,BaryCoords()能够返回一点用三角形顶点表示时各个顶点的权值,通俗一点说就是p = k1*v0 + k2*v1+k3*v2,该方法返回(k1, k2, k3)。这个方法在特定的需求下还是相当有用的。

• 边界边(Open Edges): 也许这里我不得不再次重申MAX建议创建网格时应该要注意的事项,比如每条边至多关联两个三角形,正反方向各一次,否则连“边界边”的定义都成问题。所谓边界边就是说只关联一个三角形的边。FindOpenEdge()方法可以将所有的边界边在EdgeSel位数组总标识出来。

• 孤立点(Isolated Verts): 这些点毫无用处,留着它们干嘛..

• 病态三角形(llegal Faces): 越界啊之类,无用。

• 退化三角形(Degenerate Faces): 和孤立点一样毫无用处,指顶点索引重复的三角形,它已经退化为直线或点了。

• 射线求交(Intersect Ray): 这玩意自己算起来还是比较麻烦的,Mesh提供了IntersectRay()方法不但能够求得射线与网格的交点,还能范围与哪个面片存在交点,交点处的发现是多少,交点在三角形中的位置权值。也就说,求取交点后能够得到的信息全部都扔出来了。

其实还能得到很多有用的信息,特别是在引入MNMesh后能够计算的信息就更多,这里简单例举,只是想说,要想得到什么信息的时候,别急着动手写,先看看SDK上有没有,我想这么多版本之后,MAX实现的算法应该在大多数时候比你自己的写的要健壮。(虽然MAX时不时的蹦个莫名其妙的错)

二、网格纹理贴图

甚至我们已经介绍过材质纹理贴图之类的东西了,但是还是翻出来说一下。MAX中的材质是存在于结点层级的,每个结点关联一个材质,材质也可以是多材质(Multi-Material)类型,在实际的渲染过程中,Mesh匹配其对应结点上的定义的材质类型,包括贴图纹理等等。

2.1 贴图通道(Map Channels)

这里再次赘述一下,MAX可以支持多个通道,除了0标识顶点颜色外,1-99标识纹理贴图通道。应该来说,现在用顶点颜色来表现网格细节的方式已经很少了,大部分情况下都是用各种纹理贴图的方式来表现网格的细节,比如漫反射贴图。

Mesh中支持通道的数量由numMaps属性决定,默认情况下是2,也就是说支持顶点颜色通道和一个默认的纹理贴图通道,但可以根据需要来更改支持通道的数目,getNumMaps()/setNumMaps()可以设置支持的通道数目,mapSupport()用于确定通道是否被支持。

这里要注意的时,如果支持多余一个纹理贴图通道的话,那么该通道对应的纹理顶点和纹理坐标存放在什么地方呢?这里就不得不介绍Mesh的maps成员了,maps只是一个MeshMap类型的指针,它的每一个实例记录一个通道对应的纹理顶点和纹理面片信息,也就是所,当增加一个纹理贴图通道,就会新建一个MeshMap实例,并且该实例存储通道映射信息。而Mesh类的maps成员仅仅维持这些实例的指针,从而可以在需要的时候得到任何通道的纹理贴图信息。这样,Mesh类就可以支持多个通道的切换使用了。(我曾猜想,这大概是便于多个美工同时对一个模型进行贴图映射时,各自产生了不同的UV分布图,然后又根据各自的UV分布图绘制了多张纹理贴图,而后难分伯仲或者作为某种可选择的方式,使得可以在MAX中随意切换)

2.2 纹理顶点(UV Verts)

纹理顶点同网格几何顶点一样,采用数组的方式存储。不要误会纹理顶点的数目会和几何顶点的数目一样,这完全是两个概念,纹理顶点存在于纹理空间,和几何顶点没有必然的对应关系,甚至在纹理映射时完全用不到某些纹理顶点。纹理顶点的数目由Mesh::numTVerts决定,如果非要手动设置纹理顶点并且自己分配纹理顶点位置时,setNumTVerts()可以帮助你完成纹理顶点数组的分配,getNumTVerts()返回纹理顶点数目。虽然美工都是通过插件工具来自动半自动甚至人工来进行UV展开,但对于程序员来说,这些方法是经常需要用到的。最简单的例子莫过于导入插件,从某种3D文件中导入纹理坐标时,必然会用到该方法。

我似乎忘记说了,UV坐标并不表示它是对应于平面图像的二维坐标,UV坐标实际上是三维坐标。除了平面展开UV外,圆柱展开、球型展开也是很常见的。

2.3 纹理面片(UV Faces)

刚才说纹理顶点和网格几何顶点无对应关系,但纹理面片就不一样了,它必然和网格面片一一对应。大家一定没有发现Mesh类存在一个什么诸如"numTVFaces"之类的成员来标识纹理面片的数量。这当然毫无必要,纹理面片的数目不但和几何面片的数目相同,而且在索引上也是一一对应的关系。tvFace就是纹理面片数组,显然faces[i]对应的纹理面片是tvFace[i]。

纹理面片的结构由TVFace类决定,这个类结构很简单,里面记录的就是三个顶点对应的纹理顶点在纹理顶点数组中的索引。这和Face类很相似,只不过Face类还记录了其他一些附加信息。TVFace提供了纹理顶点索引的设置和获取方法。

2.4 UV展开(UV Unwrap)

Mesh类甚至提供了几种UV展开方法。MakeMapPlanar()仅仅为某个贴图通道提供平面展开,UV平面展开只需要把几何顶点投影到平面上就可以得到UV展开了。通常情况下我们用不到这种方法,对于复杂的模型我们通常很难自动的实现UV的展开,因为它可能还需要“剥皮”等复杂操作以及一些美术上的观感,这就是为什么有这么多的UV辅助展开插件工具。虽然Mesh也提供了其他几种UV展开方法:ApplyUVWMap()方法提供了包括平面、柱形、球形等数种展开方式,甚至开可以对平铺、镜像参数进行设置,但是,实际的开发中可能不容易用到。

2.5 顶点颜色(Vertex Color)

前面已经说过,由于纹理贴图的大量使用,顶点颜色通道其实已经很少被用到,但考虑其在图形学初期时的应用以及一直被MAX的各种版本所支持,这里再介绍一下。

顶点颜色通道与纹理贴图通道方式有所不同,它首先维持一个颜色顶点数组vertColor(对应于纹理贴图中的纹理顶点tVerts)为网格的每个面片三角形定义一个颜色面片vcFace(对应于纹理贴图中的纹理面片tvFace)。三角形面片中的每个点的颜色值由其三个顶点对应颜色插值决定,这在早期是比较常用的方法。

• 颜色顶点(Color vertices) 颜色顶点是一组VertColor类型的颜色值,其实这个类型就是Point3,三个分量分别是RGB分量。Mesh维持一个颜色顶点数组vertColor,数目由numCVerts决定,getNumVertCol()/setNumVertCol()用以返回和设置颜色顶点的数目。

• 颜色面片(Color Faces) 颜色面片存储的类型仍然是TVFace,因为其结构一样,只需要记录三个顶点对应的颜色索引。显然,同纹理面片相同,颜色面片和网格的几何面片是一一对应的关系。Mesh维持一个颜色面片数组vcFace,setNumVCFaces()可以设置颜色面片数组的大小。

4.0版本后,对颜色顶点的数据组织方式进行了增强,所谓增强就是使得颜色顶点数组/颜色面片数组可以指向一个外部数组。curVCChan标识正在使用的颜色通道。

[cpp]  view plain copy
  1. VertColor *vertColoArray;   //该数组指针仍然指向一个默认的内部数组,即以前的vertCol;
  2. VertColor *curVCArray;      //指向一个外部数组,默认为NULL
  3. int curVCChan;              //标识正在使用的映射通道,默认为0
  4. TVFace *vcFaceData;         //默认使用vcFace数组,当其他映射通道使用时,则使用该数组

三、网格拓扑编辑

Max提供了对网格进行编辑的修改器插件Edit Mesh,对应于Edit Mesh的功能,SDK包含了能实现这些功能的方法,并且通过SDK可以实现进一步增强的功能。在介绍这些编辑功能之前,不得不再次赘述网格的一些规则限制。

1. 在同一个方向上一条边最多只能被引用一次;
      2. 避免面片的自相交情况;
      3. 在创建面片时不要时面片的顶点出现重合;
      4. 不要用一个点来联接两个不同的网格部分;
      5. 将网格分解成较容易理解的子元素(对应地,如果它看上去是一个元素,那么它应该就是一个元素);
      6. 在任何方便的时候封闭网格;

之所以再次提起这些规则,是因为MAX在创建网格时并没有限制,你完全可以创建不符合这里任意一条规则的网格,我从网上下载到的一些模型显然是在跟这些规则叫板的结果。虽然不符合规则的网格并不受MAX的强制限制,但是,如果使用SDK来处理这类型网格时会遇到麻烦。这里特别强调的是1和4两个规范,它们对于SDK中的一些算法来说,是一种几何基础。也就是说,如果不符合这些规范,很多针对网格的插件的部分功能可能出现错误的结果。既然这样,那么你为什么非要创建这些不易被处理的网格呢?好在我注意到,一些正规的3D制作公司的模型要求是非常符合这些规范的,一方面这些规范容易遵守并且适合在各类插件上使用,另一方面也因为它们内部经常需要使用SDK编写一些小插件来取得一些特定的效果。

我们简单举一个例子,假设一个网格不满足1或4条件,那么Edit Mesh中的Select Element将会出现问题,而Select Open Edges同样会出现怪异的结果。所以我们下面提到的一些编辑网格的功能,都是针对这些比较符合规范的网格。

3.1 Mesh类相关方法

Mesh类作为网格的实体类,提供了很多基础的编辑方法。以下会挑选一些常用的编辑方法加以介绍。

• 查找/选择/隐藏/删除元素

(1) 顶点元素

◇ FindVertsUsedOnlyByFaces(): 查找指定面片集用到的顶点。这个功能在某些情形下经常会用到,类似于 Unwarp UVW中的“Convert Face to Vertex”。主要用于已经选择了面片集,但是想知道当前以被选择了面片中包含的顶点集。

◇ GetIsoVerts(): 查找孤立点,这些点没有被任何面片用到。

◇ VertSel(): 返回顶点的选择状态位数组,也可以通过该位数组设置任何一个顶点的选择状态。

◇ VertHide: 该位数组标识顶点是否处于隐藏状态。

◇ DeleteIsoVerts(): 删除孤立顶点,孤立顶点没有存在的必要。

◇ DeleteSelected(): 如果选择级别处于顶点级别,被选择的顶点将被删除。与此同时,与这些顶点相关联的面片也将被删除。

◇ DeleteVertSet(): 删除指定的顶点集。该顶点集可以是任意指定的顶点集,删除后与之相关联的面片也将被删除。

(2) 面片元素

◇ DoesFaceExist(): 查找指定顶点的面片是否存在。

◇ ElementFromFace(): 查找与指定面片连通的所有面片。也就是Edit Mesh中的选择Element功能了。

◇ PolyFromFace(): 查找包含指定面片的多边形。这个概念有必要解释一下,所谓包含该面片的多边形是指这样一个面片集,这些面片共享这些隐藏边,并且这些隐藏边的角度(即边关联面片的二面角)小于一个阈值,则仍未这些面片组成了一个多边形。也就是Edit Mesh中的选择Polygon功能。

◇ FaceSel(): 返回面片的选择状态位数组,也可以通过该位数组来设置任意一个面片的选择状态。

◇ FACE_HIDDEN: 这个标识是Face类的flags标记,用以标识该面片是否隐藏,可以通过设置该标识来隐藏面片。

◇ RemoveDegenerateFaces(): 删除所有退化面片。

◇ RemoveIllegalFaces(): 删除所有错误的面片(索引越界等)。

◇ DeleteSelected(): 如果选择级别处于面片级别,被选择的面片将被删除。与此同时,仅与这些面片关联的顶点也将被删除。

◇ DeleteFaceSet(): 删除指定的面片集。可以是任意指定的面片,删除后仅与此关联的顶点也一并删除。

◇ DeleteFlaggedFaces(): 删除被标记的面片。

(2) 边元素

◇ FindOpenEdges(): 查找所有的边界边。边界边是指只关联了一个三角形面片的边。

◇ DisplayAllEdges(): 是否显示所有的边。

◇ EDGE_A EDGE_B EDGE_C EDGE_ALL: 这个标识是Face类的flags标记,标识该边是否显示。

• 分解/细分/挤压元素

(1) 顶点元素

◇ BreakVerts(): 将指定顶点拷贝并分解多份,并保证分解后每个顶点只关联一个三角形。这个方法看上去很酷,但是我一直没有用到它。我只希望在创建网格的时候尽量把在一起的顶点焊接起来,而不是分解它们。当然这可能是在分离元素的时候用的上。

(2) 面片元素

◇ DivideFace(): 分解三角形,通过三角形的两条边上的点,将该三角形分成三个小三角形。在这个过程中同时创建了两个新顶点。

◇ FaceCenterTessellate(): 细分三角形,通过增加一个新顶点(新顶点位于三角形的中心),将三角形细分成三个小三角形。

◇ ExtrudeFaces(): 挤压面片,挤压生成的新面片并没有改变位置,而是处于被挤压面片的上方。同Edit Mesh中的挤压功能。

◇ IndentSelFaces(): 面片倒角,用于凸起或凹陷。

• 翻转/统一法线

◇ FlipNormal(): 翻转法线。法线不对就看不见了,翻转一下试试。

◇ UnifyNormal(): 统一法线。这个对于乱糟糟的法线不对的情况下非常有效,可以统一法线的朝向。

• 网格优化

◇ Optimize(): 所谓优化,其实就是简化网格。给定一个阈值,当相邻的面片法线的差异小于这个阈值时,就将这两个面片合并为一个面片。诸如此类,实现化简的目标。

◇ AutoSmooth(): 平滑,即根据表面的法线来设置光滑组,实现光滑效果。

• 网格布尔/组合

◇ CalcBoolOp(): 实现网格之间的布尔操作,包括布尔并、布尔交、布尔差。这里要注意,布尔操作只对封闭网格有效,如果网格不封闭将会得到奇异结果。

◇ CombineMeshes(): 实现网格组合,即将两个网格对象组合成一个网格对象,先前的两个网格变成新网格的一个子元素。

以上这些都是常用的方法,更多的或者特定的方法就需要到SDK中慢慢找了,实在找不到,就只能依托已有的方法来实现所需要的方法了。后面还将说到MNMesh,那是个强大的网格处理类,能有的都差不多有了。

3.2 MNMesh类相关方法

MNMesh类作为增强的处理网格的类,在2.0版本之后开始使用,它提供了更强更兼容的网格处理方法。包括可以处理对于三个顶点的多边形,并且能处理更为复杂的拓扑编辑。对于程序员来说,使用MNMesh会非常方便,但是对于使用者来说,它的这些方法将耗费更多的时间,和更多的内存。

MAX SDK网格专题相关推荐

  1. MAX SDK之插件概述(一)

    一.MAX 插件概述 1.1 插件的功能 所谓插件,就是开发者自己开发的一组程序用以扩展MAX的功能.如果使用过3DS MAX,我们会发现,MAX的每组功能都封装在某一类插件中,如创建物体,修改器等等 ...

  2. 3ds max sdk导出插件编写的心得

    3ds max sdk导出插件编写的心得 作者:yhchinabest 来自:CG先生-3D图形插件开发网http://www.cgsir.com 写在前面 为什么要写这个心得?去年11月份的时候我写 ...

  3. 3DS MAX分配网格时出错,减少面数以渲染此场景

        错误如下:   原因分析: 场景太大物体数太多. 解决方法: 1.检查物体数: 2.检查空物体,空点: 3.重命名材质球: 4.残余痕迹,线段,动作痕迹: 5.用排除法,逐一隐藏物体进行渲染: ...

  4. 3D MAX导出插件编写

    文章版权归博客园 BigCoder所有,转载请于明显位置标明原文作者及出处,以示尊重!! 原文出处:http://www.cnblogs.com/csyisong/archive/2009/09/01 ...

  5. 1660用哪个驱动稳定_3DS MAX哪个版本更稳定更好用?各个版本来分析

    3DS MAX现在已经更新到2020了,每个刚入门的小伙伴都会问哪个版本更稳定更好用?今天就来分析下使用比较多的几个版本. 首先个人是比较倾向于使用3DS MAX2018或2020,搭配或,搭配或,需 ...

  6. CososCreator (Android)-AppLovin MAX 广告聚合平台接入+Firebase统计

    CososCreator 2.4.4 Android Studio :4.2.1 接入SDK有:接max聚合及中介平台(Admob,FB, applovin,pangle,mintegral,vung ...

  7. 3ds max文件导出osg或者ive格式

    osg/osgEarth系列文章目录 文章目录 osg/osgEarth系列文章目录 前言 参考 前言 首先下载插件osgexp Osgexp的下载地址 安装上之后,如果3ds max导出里面已经可以 ...

  8. 3ds Max .NET二次开发的基础入门篇

    3ds Max .NET SDK由以下.NET程序集组成. Autodesk.Max.dll - Contains wrappers that expose most of the API eleme ...

  9. 3D MAX导出插件编写I

    3D MAX导出插件编写I 想想研究3D MAX 的SDK已经有了不短的时间,真正算起来也有两个月了吧,但是讲到收获,确实不大.作为一个3D MAX二次开发的学习者,我首先学习了导出插件的编写,网上有 ...

最新文章

  1. Python在ubuntu中更改Python和pip指向
  2. [裴礼文数学分析中的典型问题与方法习题参考解答]5.1.21
  3. office文档、图片、音/视频格式转换工具
  4. 《R语言编程艺术》——2.5 使用all()和any()
  5. Python做文本挖掘的情感极性分析
  6. java超时导致oracle锁表_java – 正确的设计,以避免Oracle死锁?
  7. deepin卸载了python_Deepin-Linux下python卸载与安装(失败的折腾)
  8. xs128 双定时器PIT0和PIT1
  9. opencv光流例程_OpenCV 4.4 发布!新增YOLOv4 和 EfficientDet 推断支持
  10. Another FTP daemon is already running?
  11. Spring 切面 注解模式-练习Demo
  12. 未来的几年,不可能再有岁月静好
  13. LINUX SHELL中,如何查找某些文件并删除
  14. 阿里 java 变量命名规范_阿里巴巴Java开发手册-命名规约
  15. 少儿学编程系列 --- 使用python程序暴力求解:数学游戏 24 Game的答案
  16. HoudahGeo 6 for Mac(地理位置信息软件)
  17. 作为程序员,应该更关注代码质量还是只需要完成功能就好了?
  18. 移动硬盘和电脑内置硬盘使用时的区别
  19. word文档最上面有一条不是页眉的线
  20. 使用EditPlus技巧,提高工作效率(附英文版、自动完成文件、语法文件下载)(转载)...

热门文章

  1. Excel逆向查询的多种方法,赶快学起来
  2. 高级艺术二维码制作保姆级教程!
  3. 阿狸写给桃子的四十四封信 、有没有哪份感动了你?????
  4. 统计图像中各像素值的数量
  5. java输入一串歌曲名称歌曲名称用-间隔_Java练习 SDUT-2053_整理音乐
  6. vs2017下搭建libuv环境
  7. 如何进入开源世界并打造自己的明星 Project?
  8. 盘点 | 这些都是明星代言的奶粉,你更青睐谁?
  9. SUV,MPV,RV概念
  10. 周志华《机器学习》课后习题解答系列(四):Ch3.3 - 编程实现对率回归