法线平滑算法
‘壹’ 计算机图形学(OPENGL):法线贴图
本文同时发布在我的个人博客上: https://dragon_boy.gitee.io
模型的网格构成其结构,纹理赋予其光影,但如果观察我们之前的所有例子,都会发现模型的表面都是扁平的,就算赋予了纹理也看起来不真实,因为现实生活中的大部分物体都是表面粗糙,凹凸不平的。
比如,一个贴有砖块纹理的平面。砖墙本身应该表面凹凸不平,有缝隙,有划痕,有孔洞,运用我们之前学过的技术,我们会在之前的平面上贴上纹理来模拟砖墙:
仔细观察的话,所有的凹凸不平、缝隙和孔洞这些细节全都没有表现出来,平面看上去非常扁平。我们其实可以利用一些技术来表现细节,比如使用高光贴图来让某些地方照亮的更少,但这不算是一种真正的解决方式。
如果我们从灯光的角度去思考的话:平面是怎么样被渲染为完全扁平的平面的?我们可以想到是平面的法线,决定物体形状的就是它的法线。平面使用的是单独的法线,所以表面没有起伏变化,那么如果我们为每个片段计算不同的法线,并操作这些法线产生一些变化的话,表面应该就能看起来起伏了。下面的图说明了这个想法:
为了使用法线贴图技术我们需要每个片段的法线。就像使用漫反射贴图和高光贴图,我们可以将每一片段的法线信息存储在一张纹理中。
由于法线向量由几何方式存储,而纹理存储的是颜色信息,所以这二者的转换不那么直接。纹理中颜色由r、g、b三个组件构成向量表示,法线由x、y、z三个组件构成向量表示。法线的组件取值范围为[-1,1],颜色的范围是[0, 1],所以我们需要进行一下映射:
这样的话,我们就可以将法线信息转化为颜色信息存储在纹理中。下面是砖块纹理的法线贴图:
可以看到纹理图片大部分颜色与蓝色相关,这是因为大部分法线倾向于垂直于平面,也就是倾向于指向z轴,对应的是b通道,所以倾向于蓝色,依此类推解释其它的颜色。
通过这个法线贴图,我们可以结合漫反射纹理来渲染一个平面。(记住,OpenGL纹理的原点在左下角,大部分图片的原点在左上角)我们需要做的就是按常规加载这张法线贴图,并设置相关参数。注意,在片元着色器中我们将使用法线贴图中的法线信息计算光照:
加载法线贴图,并根据纹理坐标映射后,我们的到法线信息,首先将其标准化到[-1,1],接着照常计算光照。
最后的结果就是这样:
但存在一个限制法线贴图使用的问题,法线贴图中存储的法线信息大部分都是指向z轴的,如果平面也指向z轴,效果的确很好,但如果不是,比如下面的平面指向y轴,结果就不对了:
另一种可行的方法是在不同的坐标空间中计算光照,来保证从法线贴图中采样的法线向量始终指向z轴,其它相关的向量也会转换到相同的坐标空间中。使用这种方法,我们可以复用一张法线贴图,而这种坐标空间被称为切线空间。
在法线贴图中的法线向量在切线空间中表示,在这个空间中,法线基本都会大致指向+z轴的方向。切线空间是相对于每个平面三角形的空间,我们将这个空间作为法线贴图自己的空间,用来描述法线向量。当我们想要使用法线贴图中的法线进行计算时,我们就可以使用一个特殊的矩阵变换将法线从切线空间转化到世界空间或视图空间,这样就可以与物体相对应。
所以解决上面法线贴图不正确的方法就是定义一个特殊的矩阵将切线空间中的法线进行一些转化,让法线大致指向+y轴。
而这么一个特殊的矩阵被称为TBN矩阵,每个字母分别代表切线(Tangent)、双切线(Bitangent)、法线(Normal)向量,这三个向量将用来构成一个矩阵。为了获取这三个矩阵,我们定义切线空间的三个轴,上、右、前。
我们已经有了代表上的轴向,即法线向量,右和前轴分别是切线和双切线向量,下面是图例说明:
我们首先定义一个平面的四个顶点和每个顶点的纹理坐标(123和134两个三角形),以及平面的法线朝向:
根据上面提到的步骤,计算第一个三角形的两条边和两条边对应的ΔUV坐标:
然后,我们就可以按照等式计算切线和双切线了:
作为结果,切线和双切线的值应该为(1,0,0)和(0,1,0),它们和法线(0,0,1)构成TBN矩阵,在平面上显示就是这样:
我们首先在着色器中定义TBN矩阵,可以在顶点着色器中传入我们计算好的切线、双切线以及法线作为顶点属性:
接着在main方法中创建TBN矩阵:
我们首先将TBN三个向量分别转化到我们想要工作的空间中,然后组装为TBN矩阵。为了跟精确一些,我们可以将TBN三个向量分别进行和我们之前处理向量一样的操作,因为我们只关心这些向量的指向。
使用这个TBN矩阵的方式有两种:
在片元着色器中输入:
使用TBN矩阵我们将采样的法线向量转化世界空间:
接着介绍第二种,我们将TBN矩阵的逆输出到片元着色器:
注意到我们使用的是转置,这是因为TBN矩阵是一个正交矩阵,它的逆等于它的转置,所以我们避免使用inverse来避免巨大的开销。
接着在片元着色器中将所有与灯光计算相关的向量转化到切线空间:
看起来第二种方式更为复杂,因为要计算的东西更多,但第二种方式有它的优点:我们可以将所有转化工作在顶点着色器中进行。这是可行的,因为lightPos和viewLPos这种向量并不会在片元着色器中更新,同时也可以在顶点着色器中计算fs_in.FragPos计算切线空间的位置。的确,考虑效率,不必在片元着色器中进行空间的转化。
所以接下来我们在顶点着色器中完成这些操作:
这样将相关变量传入片元着色器中就可以直接进行计算。
为了观察光照是否正确,我们可以让平面一直旋转:
最后的结果如下:
针对复杂模型,我们并不经常手动计算切线空间的相关向量。比如在导入模型时,我们可以借助assimp库来帮助我们计算。
assimp库有一个读文件方法有一个配置选项为aiProcess_CalTangentSpace,这样assimp可以为每个顶点都计算切线和双切线:
我们可以像下面这样获取切线:
这样的话,我们可以为模型加载它的法线贴图,我们使用aiTextureType_Height选项:
针对复杂模型时,切线往往是经过许多顶点计算的,这样就可以得到一个平均值来获得平滑的结果,这样会造成一个问题,那就是T、B、N三个向量可能不会相互垂直了,也就是TBN矩阵不是正交的了。
针对这一问题,我们可以使用格拉姆施密特方法来重新正交化TBN矩阵,在顶点着色器中这么做:
最后,贴出原文地址供参考: https://learnopengl.com/Advanced-Lighting/Normal-Mapping
‘贰’ maya如何低线段做平滑
通过你上面的图片,可以看出,你说的低线段平滑,就是我们所说的法线光滑
法线光滑可以让两个面之间硬的交界线做一个平滑过渡,
它在maya软件的命令位置为:建模板块Modeling下面 Mesh Display命令下面的Soft Edge
如下图所说
‘叁’ Max小技巧(一)锁法线(使模型光滑组平滑过渡)
有的模型给多个光滑组效果并不好,给一个光滑组会有渐变效果,结构不能很平滑的过渡,不是想要的效果,怎么处理呢。
方法:给模型Edit Normals编辑器,然后选择面Face,在点Selected锁住法线。
‘肆’ 2019-12-23法线原理及法线处理
法线有两种:一种是多边形法线(也可以i 叫做表面法线),一种是顶点法线。shift+V进入视图设置,勾选法线显示,在视窗可以看到法线,短小的白线就是,并且其总是垂直于表面,多边形法线可以运用在多个工具配合使用 比如网格——创建工具——偏移(M-Y);专门配合法线的工具:网格——移动工具——法线移动、法线旋转、法线缩放;
法线处在多边形的重心位置,其会随着多变形形状的变化发生变化,不过立方体轴线就没有那么聪明,依旧处在多边形边界框(BOX模式线可以查看边界框)的重心,移动工具——轴向——选择模式下便可以查看,如果你想把立方体轴向移动到法线的同等位置,移动工具——轴向——点模式下(重心点的位置就会选取所有点的平均值,即与法线位置重合)
法线的反面,shift+V 忽略背面。多边形就会消失
但在添加材质的时候要添加的是顶点法线,C4D中默认的灯光和摄像机的位置和角度都是一样的,在点编辑模式下选中vertext normal tool(顶点法线编辑工具),选中多边形,在多边形的定点可以看到法线。通过旋转法线可以看到多边形表面颜色发生变化,其原理和平滑着色标签有息息相关的联系,当平滑着色的角度小于90的时候,转折处生硬,当大于90度的时候则会很渐变看起来(为什么转折处很生硬,是因为转折处的点有两个互相垂直的法线,为什么看起来圆润渐变,两法线合并并法线角度发生转变(平均分配),平分的角度则是转折角度的一半,但还是垂直于表面的)
U-A 对齐法线和反转法线功能相似,
选择——平滑着色器断开的 用来显示断开了平滑着色的过度线——在属性窗口选择全部,选择断开的边 然后用倒角工具倒角 倒角处与其他面不符合,是因为倒角工具在选项中默认选择勾选了断开勾选平滑圆角 断开平滑着色圆角的作用就是把原表面与新产生的面断开分离开来 恢复的方法是 网格工具——法线——恢复平滑着色 OK看起来就会好很多了
倒角变形器也会有相同的结果 处理方法则是 取消勾选平滑着色断开圆角并在细分处添加细分 OK了
低面几何体就有着色不平滑的问题,造成这个问题的原因就是一个几何体和其他几何体权重角度的不同,也就是每个多边形,其顶点法线角度是不统一的,但是平滑着色标签默认的状态又不能解决这个问题,对于低面几何体的法线整修主要是通过顶点法线编辑插件解决:选择所有没有倒角的面,对其法线进行重新定向,
snapselection——UNselected(shift cycle)用来反向选择倒角的面,并吸附到刚才校正的法线角度
对于倒角,倒角后法线垂直角度被新产生的加上原来的角度所平分,要想保持效果,可以进行内部挤压,先产生的倒角线可以再次倒角一次,OK效果完美。在这里为什么不使用倒角
法线贴图其实就是每像素XYZ轴法线信息的集合,但凹凸贴图不一样,他是灰度图上下信息的集合
‘伍’ Blender拆边和平滑法线的简单应用
本质上,拆边和平滑法线的效果是一样的,那就是给一个物体正确的平滑显示。
那么既然它们效果一样,为什么开发者要分开来弄这两个功能呢?他们当然不是吃饱了撑的。在游戏模型的建造里,为了尽量减少draw call,我们会把多个网格合并成一个网格。但是,有些部件之间的平滑法线角度要求可能不一样,比如刀刃的部分可能需要26.8°,刀柄部分可能需要54°,可一旦这两个网格合并在了一起时,就会使用活跃物体的平滑法线度数,单独使用平滑法线功能会有差劲的表现。
因此这个时候,如果使用拆边修改器单独对刀刃和刀柄进行对应角度的法线平滑,再把它们合为一体时就不会受到平滑法线角度的影响了。
不过要注意的问题是,拆边修改器顾名思义就是给模型进行拆边,尖锐边上的点会被拆开,这样会导致后续对模型进行更改的时候不方便选择。
‘陆’ maya 平滑法线 在哪里
maya中smooth是将一个面分为4个面,通过4个面的法线矫正达到圆滑的目的,但随之而来的是大量面和大量的线,将占用更多的系统资源,不更改模型直接圆滑法线是比较好的选择,先选择需要圆滑物体的所有边,maya主菜单下Edit Polygons->Normals->Soften/Harden
点后面的小方块 在弹出的窗口中将角度值打到180就圆滑法线了