网格编程基础--01

(原文链接:https://catlikecoding.com/unity/tutorials/procedural-grid/)
创建网格点
使用协程分析网格点的布局
定义三角面
自动生成法线
添加纹理坐标和切线
 
本篇教程学习简单的顶点和三角形网格的创建
 
需要具备的基础:
  1. 熟悉基本的Unity编程
  2. 了解协程
  3. 使用Unity5.0.1或更高版本
 
                                       
 
                                                                                                                 复杂的表面下是简单的几何结构
 
  1. 渲染知识
  • 如果你想在Unity中使某些物体可视化,那么使用网格(mesh)。可以从别的项目中导入的3D模型。它可以通过网格编程来生成。也可以是纹理、UI元素或者粒子系统。甚至是屏幕效果也是网格来渲染。
  • 那么什么是网格(mesh),从概念上来说,网格是图形硬件设备用来绘制复杂物体的构造体。它至少包含一个顶点集合用来定义3D空间上的点,加上链接这些顶点的三角形集合(三角形是最基础的2D图形)。三角形构成了网格代表的表面。
  • 因为三角形是平的并且有直线边界,因此能用来画出平面(visualize flat and straight things),比如cube的各个面。使用大量的三角形能够大致的画出圆曲面。如果三角形足够小,但不小于一个像素单位的话,你就不会察觉到这种误差。通常来说这在实时渲染中是不可行的,在某些角度看,这些可视表面会呈现出锯齿状。
 
 
 
                                         
                                                Unity中默认的capsule,cube,sphere  。     shaded  vs  wireframe  (着色的 vs 线框)
 
 
                                                                                            如何显示线框呢?
  • 如果你想显示3D模型,那么有两个必要的组件。 Mesh Filter:这个组件持有要显示的网格的引用。Mesh Renderer:网格如何被渲染,需要用到材质(material),不管是否需要投影或接收阴影等
 
                                                                                        为什么Mesh Renderer组件中有materials集合?
 
  • 通过调整材质可以完全改变网格的显示。Unity默认的材质是纯白色的。你能创建自己的材质球(Asset/Create/Material)并且拖拽到GameObject上。新创建材质默认使用标准着色器(Standard shader),通过着色器可以控制表面的呈现效果。
 
  • 赋值一张albedo map能够让你的网格呈现更多的细节。这是一张代表材质基础颜色的纹理。当然我们需要知道怎样将这张纹理投射到网格的三角形中,通过添加2D纹理坐标到顶点上可以完成这一步骤。二维纹理空间是指U和V。这些坐标的范围是((0,0)-(1,1)),覆盖整张纹理。在这个范围之外的坐标会被clamped或者tiling,取决于纹理设置。
    •                              
                                                                                                                     应用在Unity 网格中的UV测试纹理    
 
  1. 创建顶点网格
        你会怎样创建自己的网格呢?通过生成一个矩形网格找到答案吧。这个网格由单位长度的四边形方块组成。创建C#脚本并且把它变成一个带有水平和垂直大小的网格组建
                          
                                                                    需要System.Collections命名空间吗?
当我们把这个组件添加到一个GameObject时,应该同时添加Mesh Filter和Mesh Renderer组件。通过在类中添加特性,Unity就会自动添加
                           
 
现在创建一个空物体,并且挂载这个脚本,然后Unity就会自动添加Mesh Filter和Mesh Renderer组件。设置renderer组件的材质,但先不要设置filter组件的网格。在这里我把网格大小设置为10x5
 
                                                                                                
在Awake方法中调用生成网格的方法
                                          
 
先研究一下顶点的位置,三角形放在后面。声明Vector3数组来保存这些顶点。数组大小取决于网格大小。在每一个四边形角落都需要一个顶点,相邻的四边形可以共用相同的顶点。在每一个维度都需要多一个顶点。
(#x+1)(#y+1)
                                                                                                
 
                                                                                               4x2的网格中的顶点和四边形下标
 
 
                                                            
把这些顶点画出来才能确定它们的位置是否正确,在OnDrawGizmos方法中可以为每一个顶点在场景中绘制一个小黑点
                                                           
                                                                                                                    什么是gizmos呢?
 
当没有顶点时,在非Play模式下也不会报错,因为OnDrawGizmos方法在编辑模式下也是可以执行的。为了防止错误,要判断顶点数组是否存在,如果不存在就return
                                                            
                                                                                                            
 
在play模式下,在原点只看到一个圆圈。这是因为还没有放置这些顶点的位置,所以全部顶点都重叠在同一个位置了。需要用双层循环遍历这些位置
                                                            
 
                                                                            
                                                                                                                           顶点网格
 
 
如上图所示的顶点我们已经生成好了,但还不知道它们放置的顺序。可以通过上色来呈现这个顺序,也可以通过协程放慢这个生成过程来观察放置的顺序。using System.Collections
                                                            
 
 
-----------------这里可以放一个生成顶点过程的gif动画
 
  1. 创建网格
目前已经知道顶点的生成位置,那么就可以处理真正的网格了。此外在Grid组件中持有这个网格的引用,并且将它赋值给mesh filter组件。那么就可以将处理好的顶点赋值给网格了
 
                                            
 
                                            
                                                            在play模式下就可以看到mesh了
 
在play模式下已经有网格了,但没有赋值三角形之前是看不到这个网格的。三角形通过顶点数组的下标定义的。因为每一个三角形都有三个点,三个连续的下标就可以构成一个三角形。从只有一个三角形开始吧
                                                            
 
现在有一个三角形了,但是这三个点是在一条直线上的。这会生成一个degenerate triangle,它是看不见的。前两个顶点是没有错的,但第三个点应该是下一行的第一个顶点。
                                
 
 
这样才能得到一个三角形,但只有一个方向上才是可见的。在这种情况下,只有在Z轴的反方向才能看见这个网格。因此需要旋转视角才能看见。
三角形从哪个方向可以被看见是取决于顶点下标的方向的。默认情况下,如果是顺时针方向的,这个三角形就是前向的并且可见的。逆时针方向的三角形会被丢弃掉,因此就无需浪费时间在绘制物体的内部上了,通常情况下这也是不应该被看见的。
                                                                                                
                                                                                                                        三角形的两个面
为了能从Z轴方向上看到三角形,我们应该改变这个顺序。也就是调换一下第二第三个下标
                                
 
                                                                                                    
 
如上图,已经把第一个三角形给绘制出来了,现在绘制第二个
                                    
 
                                                                                                            
 
因为这些三角形共用两个顶点,所有只用四行代码就可以了,每一个顶点下标声明一次就行了
                           
 
                                                                                                        
 
通过一个循环就可以生成一行方块了。当我们对顶点和三角形下标进行遍历时,记得两个都要记录。把yield return加进这个循环中,就不用等待顶点显示了。
                           
顶点立即生成,而想看三角形逐个生成,那得在每一次循环中都更新网格,而不是循环结束后才更新。
 
                           
到这里,通过双层循环把整个网格铺满了三角形。要注意的是,跳到下一行时顶点下标也要加一,因为每一行方块不会多出来一个顶点的。
                             
 
                                                                                 
                                                                                                           填满整个网格
 
 
如同我们能看到的那样,网格被三角形填满了,一次一行,如果觉得可以了,那就可以去掉协程了,这样在生成的时候就不会有延迟了。
 
 
                                    
 
                                                                                为什么不用一个四边形呢??
  1. 生成额外的顶点数据
网格以一种特殊的方式生成了。是因为至今还没有给网格添加法线。默认的法线方向是(0,0,1),这个方向正好与我们需要的相反。        
                                                                                法线是如何起到效果的呢?
 
每一个顶点都会有法线,因此需要用另外一个Vector数组。或者我们可以让网格基于三角形为法线赋值。let's be lazy this time and do that
 
                              
                                                                             法线是怎么重新计算的呢?
                               
                                                                                         没有法线  vs 有法线
 
接下来就是UV坐标。现在的网格颜色是均衡的,即使它用的是一个带有albedo(反射)纹理的材质球。这也是说得通的,如果我们没有给UV坐标,那么默认是零。
为了让这张纹理适用整张网格,简单的用顶点位置除于网格维数:
                                  
                                                       
                                                                                       不正确的UV坐标,clamping  vs. wrapping 纹理
 
如上图,纹理没有铺满整张网格。能否正确显示取决于纹理的重复模式是否设置成了clamp or repeat。上图情况的原因是因为我们用整数除于整数(得到的还是整数)。为了能得到可以铺满整个网格的UV坐标(范围0-1),应该使用float(浮点型)
                                
现在,纹理可以投射到整个网格了。网格大小为10x5,纹理会有所水平拉伸。通过调整材质纹理的tiling设置可以改变这种情况。设置为(2,1)的话,U坐标翻倍,如果这张纹理是设置为repeat的话,就可以看到两个正方形瓦块。
 
                                  
                                                            正确的UV坐标,tiling (1,1) vs. (2,1)    
另一种增加表面细节的方法是使用法线映射。这些法线纹理包含了被编码为颜色的法线适量。使用法线贴图将会比单单使用顶点法线呈现出更多细节光照效果
                                
                                                                                                                一个崎岖不平的表面,使金属质感更带感
把这个材质应用到我们的网格上是不会有效果的,因为要先把切线矢量添加到网格中。
                                                                                        
                                                                                                切线是如何起到效果的呢?
 
因为是一个平面,所以所有的切线都会指向同一个方向--右边
                                
 
                                                                         
                                                                                            一张模仿颠簸纹理的平面
                                                                                                
到现在你已经知道如何创建简单的网格了,并且也知道如何使用材质可以将网格看起来更加复杂了。网格需要顶点位置和三角形,通常还有UV坐标(取决于它的四项设置),另外就是切线了。你可以增加顶点颜色,虽然Unity的标准着色器不会用到这个。你可以创建自己的shader并且使用这些颜色,敬请期待下一篇教程。
 
 
 
备注:
  1. 关于Texture一些属性的介绍
  • Texture.wrapMode 循环模式(Repeat or Clamp  : 重复或强制拉伸),在贴图边界以设置贴图的重复模式的方式避免不真实的情况,使用强制贴图边界拉伸:TextureWrapMode.Clamp.  或者 贴图重复平铺 TextureWrapMode.Repeat
  • TextureWrapMode.Clamp 钳制,钳制纹理到边框的最近像素,也就是单个图片,不重复平铺。这通常用于当映射一个图片到一个物体上时,并不像纹理平铺。纹理坐标被钳制在0-1之间。当UV大于1或小于0时,在边框的最近像素会被使用。
  • TextureWrapMode.Repeat 重复,平铺纹理,创建一个重复图案,当UV不在0-1范围内时,整数部分会被忽略,于是创建一个重复图案。
posted @ 2018-10-04 16:33  ToBeSo  阅读(858)  评论(0编辑  收藏  举报