软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 开发杂谈 -> 法线贴图原理 -> 正文阅读

[开发杂谈]法线贴图原理


   法线贴图的出现,是为了低面数的模型模拟出高面数的模型的" 光照信息 ".光照信息最重要的当然是光入射方向与入射点的法线夹角.法线贴图本质上就是记录了这个夹角的相关信息.光照的计算与某个面上的法线方向息息相关.
我们知道计算机里的
模型,是通过多个多边形面组合来近似模拟一个物体的.它不是圆滑的.面数越多,则越接近真实物体.光照到某个面当中的一点时,法线是通过这个面的几个顶点通过插值得到的.插值其实也是为了模拟这个点"正确"的法线方向,不然整个面所有点的法线一致的话,光照上去,我们看到的模型夸张点就像一面面镜子拼接起来了.但法线插值不可避免的仍然会失真.模型的面数越高,失真的程度自然越小.要是能无限细分到人眼看不出的地步,根本不用插值了.
面数高,需要计算的量和内存需求就高.前辈找到了法线贴图(前身是凹凸贴图)这个办法,使低模能够近似享受高模的光照细节信息.代价是有的,就是需要一个记录这些信息的文件.这是程序中常用的存储空间换计算时间的做法.3D程序中偏爱使用这个手法.谁叫存储硬件的单位价格比计算硬件的单位价格降低速度快很多呢.
显卡包括包括与之相辅的图形api,读的数据最初来源是图片.所以记录这个信息的文件就被我们保存为图片格式.法线贴图后边2个字就这么来的.很好你已经明白一半了.
因为面数少,低模上某个区域的一个面,可能就是高模上相同区域的几个面.看下图的高模与低模的对比(为了简便我们抽象为2维的线段)
上边凹凹凸凸的曲线表示高模.下边比较平滑的表示低模.因为高模细节多,所以在某段区域它的方向变化自然比平平板板的低模多.上图看不懂我表示无能为力.
看到这图,一些人应该有所感觉了.没感觉也不要紧,接下再来.
不管高模还是低模,反正最后还是要被上色的.假设模型已经被渲染完成有颜色了,现在我们想象用剪刀把模型展开(类似给动物扒皮的过程),得到2张差不多一样大小的皮,毕竟面积不会差太多.高模的皮当然肤白体嫩精度高,低模的皮就有些糙了.现在再想象这么个过程:逐渐把高模的皮移到低模的皮上方一定高度直到水平重叠.
现在这个样子你有感觉没有?没感觉也不要紧,接下再来.
虽然模型精度不一样,无论如何,这2张皮每一点都是有颜色了的(插值的功劳).两张皮上相同一点的颜色,高模这张皮上的更真实,因为在计算最终颜色信息所依赖的法线,高模上的 点比低模上的点更精确.我们如何给低模这张皮美容,使它能够接近高模的效果呢?换句话说,找到办法,使土肥圆演变为黑木耳,质变为白富美是不可能的,那得下辈子.
办法很暴力.现在再想象你用一根针,从上往下,刺穿高模的皮,再刺到低模的皮.保证针垂直,这样就刺到同一点了.再想象如果这针有魔力的话,它刺穿高模皮的过程中,盗取了一些信息,传送到低模皮上边.低模皮依靠这些信息计算,成功蜕变为黑木耳.这些信息是什么呢?当然是法线信息了.现在 高模这张皮被密密麻麻插满了针眼,换句话说,保存高模泄漏来的信息,必定是点对点的.即这张皮上的每个点,都得被保存.所以法线贴图跟原始的贴图是一样大小的,贴图内每个点都保存了对应高模某个点的法线信息.实际的计算,只会关心由贴图里得来的法线信息,低模上的那些法线,被抛弃了.
现在这个样子你有感觉没有?没感觉也不要紧,接下再来.
为什么我之前强调垂直呢?不只是为针能扎到同一点.现在请把这个过程,想象到上图中.图中的箭头,表示高模上某个点的法线方向.如何记录这个方向信息?现在请想象逐渐把高模和低模重叠在一起,为了方便想象,低模小一些被高模包住了.或者你干脆想象高模的面在低模面的正上方.再想象有一束光线(针的等价物),从上往下照射,把高模上的法线投射到低模上.
前戏大功告成,现在我们来处理稍微细节些的问题了.这是一个投影过程.但是影子是2维的啊?向量是由x,y,z三个分量构成的.高模上某点投影到低模上对应点所在平面,只剩2个分量的投影了.好比我们现在只知道法线在x-y平面的投影方向,那在z轴的方向呢?只要我们确保投影前法线是单位向量,那很简单z=1-x*x-y*y.这样我们还可以省下保存z的空间.其实我们既然已经知道这个法线方向(高模object space内的法线方向),而且被单位化了,直接保存也是可以的.投影过程只是个思想实验,实际是不会有什么光线由上到下投射的.
到此可以明确了,"正统"的法线贴图生成,是高模,低模不可缺一的.因为没有高模就不知道法线方向,没有低模,就不知道高模上某点的法线对应于低模上哪个点.
因为某点的法线信息是被保存到法线贴图上对应像素点的.实际计算是把法线x,y,z方向大小映射到颜色空间rgb里.就是把x值存在r里,把y值存在g里,把z值存在b里.因为rgb是8字节为单位的.所以高模的法线信息存储到像素里是要丢失精度的.而且前面计算高模与低模对应点也不可能完全匹配到,本来就是个模拟过程.自然法线贴图也不是无敌的.
现在我们可以回答之前的Photoshop根据diffuse贴图生成法线贴图的问题了.实际的diffuse贴图是根本没有包含模型上的法线信息的.因此它根据diffuse贴图得出的法线贴图根本就是错误的.但为什么能够应用呢?请想象高模的精度高的吓人,高到渲染后把高模皮扒下来后,就成了一张照片.再想象之前高模上的贴图是布满了铁锈.于是你就得到了一张铁锈照片.Photoshop处理这张铁锈照片,其实是根据一些算法(sobel等等)把颜色值转化为梯度值,近似模拟了法线.因为我们其实不关心铁锈的精确分布,像那么一回事就可以了,所以这种情况下如此处理是可以将就的,坑坑洼洼效果最适合如此做法.photoshop这种脱离高模低模的做法容易让人迷惑,导致新手以为法线是从diffuse贴图上来的,或者干脆被阻断了思路.
我们上边计算法线贴图所用到的法线,又是从哪里来的.如果这个法线方向,是处于世界坐标中的(world space),那称为world space normal.如果是处于物体本身局部坐标中的,那称为object space normal.很容易想象,world space normal一旦从贴图里解压出来后,就可以直接用了,效率很高.但是有个缺点,这个world space normal 是固定了,如果物体没有保持原来的方向和位置,那原来生成的normal map就作废了.因此又有人保存了object space normal.它从贴图里解压,还需要乘以model-view矩阵转换到世界坐标,或者转换到其他坐标取决于计算过程及需求.object space normal生成的贴图,物体可以被旋转和位移.基本让人满意.但仍有一个缺点.就是一张贴图只能对应特定的一个模型,模型不能有变形(deform).
>> tangent space normal map
为解决适应变形的normal map,我们仍能从这两种方法中得到启示.world space normal直接保存的是世界坐标系中的高模法线方向.因此低模取出该点法线就可以直接使用,前提是低模的世界坐标系与高模一致,一点旋转都不能有,不然法线方向就改变了.object space normal保存的是模型空间坐标系中的高模方向,低模取出该点取出来法线,还需要乘以所在的model-view矩阵,转化为低模的世界坐标系中的方向,也就是说低模端还需要做一个运算.因此即使低模任意旋转也不怕,有model-view矩阵可以把法线贴图中的值转换两者效率由高到低,灵活度由低到高.问题来了,我们是否能找到高模上的另外一个坐标系统,使低模变形也时也能较正确的变换法线到世界坐标系中?
我们考察一下object space.当一个低模旋转时,因为是刚体不变形,相当于每个点都乘以一个旋转矩阵R,之后各点关系保持不变.实际上,我们保持物体不旋转,将object space的坐标系(x,y,z三个轴)旋转,得到的结果是一样的.这个关系相信大家都能理解.换句话说,法线针对于object space是固定不动的,物体保持在object space固定,只管跟随坐标系的移动,旋转就行了.现在我们想象低模的某个点需要变形时,那原则上也可以通过让object space坐标系乘以某个变形矩阵T来达到.但是不同的点有不同的变形,不可能存在一个矩阵T即适合这个点又适合这个点.因此object space坐标系是不能用的.会有哪个单一的坐标系能存在一个所有点都共用的变形矩阵吗?显然无法想象.
变形时,顶点关系改变了,即面的形状,方向改变了.如果面上存在一个固定的坐标系,那当物体变形,移动,旋转时,这个坐标系必定跟着面一起运动,那么在这个坐标系里的某个点或向量(比如我们把高模法线转换到这个坐标系里),不需要变动.当整个面发生变化时,我们只需要计算面上的坐标系到世界坐标系的转换矩阵,那么定义在这个面上的点或坐标(固定的),乘以这个矩阵即可得到在世界中的坐标.这个坐标如何构造目前对我们不重要,请务必理解这个概念.我们不过是寻求一个局部坐标系,局部坐标系中的点坐标,乘以局部坐标系到世界坐标系的转换矩阵(这个矩阵是低模渲染时动态计算的的),得到局部坐标系中的点在世界坐标系中的坐标.这样法线贴图中存储的固定的值(法线方向),才能进行有意义的计算.
看到这里很明显的,这种做法需要数千个不同的定义在面上的坐标系.低模上有多少个面,就得有多少个这样的坐标系.这种方法的计算量自然是比object space normal map要大一些的.在低模的每个面上,要构造出这个坐标系.这个坐标系术语里称为tangent space.
object space normal map的中,低模的object space坐标系与高模中的object space坐标系是重合的.所以不需要构建,所以低模上某点才能直接用高模的法线替换自己的法线.坐标系重合这个概念很重要.新方法中,低模上的这个tangent space,也必须与高模上的坐标系tangent space.因为低模上的一个面,可能对应了高模上的几个面(精度高),按照新方法每个面都有一个局部坐标系,那对于低模上的每个面,高模因为存在好几个面,就会出现好几个面,这肯定是不行的.所以高模所用的tangent space,就是低模上的.生成法线贴图,必定会确认高模上哪些面都对应低模上的哪个面,然后高模上的这几个面的法线,都会转换为低模这个面上所构建的tangent的坐标.这样,当低模变形时,即三角面变化时,它的tangent space也会跟着变化,保存在贴图里的法线乘以低模这个面的tangent space到外部坐标系的转换矩阵即可得到外部坐标.顺便再提一点,高模保存的这个法线,是高模上object space里的法线,看到这里你该明白这是自然而然的.你搜索文章时可能会看到什么把光转换到tangent space里,确保处于同一个坐标系下的话.有一点3d数学知识的人都知道,还要你提的不痛不痒?我以为确保tangent sapce重合及做法,才是让人顿悟tangent space的诀窍点.
当我自己想到上边这段话时,tangent space的法线贴图原理就豁然开朗了.接下来我们构建这个tangent space坐标系.
面在动时,tangent space也得跟着动.面上的垂直法线是跟着动的,因此这个法线N可以作为tangent space的一个坐标轴. 非常非常需要注意的是,这里所说的面上垂直法线,不是指插值所得来的法线,那个法线正是是我们需要保存的内容.N单纯就是指垂直于这个面的方向.
我们考察上图,对于一个三角面,它的边V2V1,V3V1,V3V2我们总是能够确定的.边也定会在变形时跟着动.因此我们可以选择一条边作为tangent space的第二个坐标轴T.第三个坐标轴就简单了,直接根据叉积来B=T * N.这个坐标轴就订好了.其实,坐标轴的选定几乎可以是任意的,只要你能够确保每次都能构建出来.比如你可以先选择V1V3,V1V2作为坐标轴,N=V1V3 * V1V2.这里N恰好和前面一样方向.但如此一来这个坐标系中V1V2,V1V3不是垂直的,不正交的坐标基在矩阵运算中是不方便的,还得正交化.因此我们选择第一种最直观最清晰最方便的方法.
既然三个坐标轴都确定了,那构建object space到tangent space的矩阵O-TBN就简单了,我们把T,B,N单位化,分别作为tangent space的x,y,z轴.根据三个坐标基我们构造矩阵如下:
高模上object space内的某点法线,乘以这个矩阵,即得到tangent space内的法线方向,再把这个值映射到rgb空间,存为贴图即可.这个矩阵为什么是这样,这是题外话了.我简略说一下:object space的三个坐标轴(1,0,0),(0,1,0),(0,0,1)乘以这个矩阵,必须刚好为tangent space中的坐标轴,很自然矩阵就是上边的样子.而object space其他点的坐标都是x,y,z三个单位坐标的线性组合.所以这个矩阵对于其他点必定是正确的.
实际上在vertex shader中,我们只能知道当前顶点的信息,三角形的另外两个顶点我们是不知道的.但现代的shader能够为顶点提供一个tangent信息,表示在顶点处的切线.你可以想象一个足球上经过某点的切线.因此我们会把顶点的tangent方向作为上边的T向量.这也是tangent space叫这个名称的由来. 你会看到很多文章中提到纹理的u,v方向.因为面上某点的u,v是沿各条边线性插值的,所以u,v方向与边的方向相同.其实我们现在已经有现成的tangent可以用了.
  好了,现在高模面上各点的法线值,都转换为低模上的tangent space坐标了.现在我们考虑具体的低模上的渲染计算了.假设在低模上的某个面我们计算出了这个矩阵,并取出了面上某点的对应在法线贴图里法线值.现在需要计算光照.我们可以把光向量转换到tangent space里做计算.也可以把得到的法向量转换到world space与光向量进行计算.结果是一样的.实际考量,你会发现后一种方法不好.因为对于面上的每个点,都要计算一次normal到world space的准换.而前一种方法,对一个面上的所有点,只要计算一次光向量到tangent space的计算.然后再考虑到vertex shader与fragment shader的流程,你会发现刚好我们可以在vertex shader计算光线到tangent space的转换,在fragment sader取出法线值与前面得到的tangent space里的光线方向做计算即可.这里提醒一下,一般verteix shader中我们得到的光线方向是基于world space的,而法线贴图保存的是高模的object space内的方向然后再转换到tangent space,所以在vertex shader中,我们必须先把光线先转换到object space,再转换到tangent space.这样才能保证最终计算时,光线与法线是基于同一个坐标系的.
以上是法线贴图的原理.因为该原理的应用范围很广,也能够串起很多知识点,是非常值得搞清楚的
原图、法线图以及程序计算后的效果如下:
[img]http://img.blog.csdn.net/20160403180710928?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center[img]http://img.blog.csdn.net/20160403180941757?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center[img]http://img.blog.csdn.net/20160403180956898?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center[img]http://img.blog.csdn.net/20160403181220540?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center



......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2016-04-03 20:46:48  
开发杂谈 最新文章
BloomFilter
大学四年编程之历程
内核分析
造人论坛——意识的本质和一个人工脑模型
OFDM信号[matlab描述]
人类还会进化吗?
HDUACM1035RobotMotion简单模拟题
树、二叉树(二)
iisphpweb.config处理404,500等,跳转友好
DatabaseAsaFortress
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年1日历
2018-1-17 9:16:58
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --