`

法向量纹理贴图

 
阅读更多

概述:实时渲染时,在建模过程中,我们会为每个顶点设置一个特定的法向量,在这shader的时候进行光照计算。而由顶点组成的三角形面会有一个自己的法向量,因为整个面只有一个法向量,光照的时候都是按照这个法向量进行计算,渲染出来的结果是这个面很平。为了增加细节的描述,我们会改变这一现状,使用一个存储了法向量信息的纹理图来描述这个平面上每个像素点的法向量。这样渲染出来的结果就是凹凸不平的了,增加了很多真实的细节。

 

1.切线空间 :

在一个曲面上的任何一点,都有一个自己的切平面,并且有一个法向量与切平面垂直。所以,每个顶点都只有一个法向量。切平面上任意两个不平行的切向量(网上都说是一个切向量和一个副法向量,但是我觉得都是切向量),与这个法向量组成一个空间坐标系,就叫切线空间。这也说明对于曲面上任意一点,它的切线空间是有无数个的,因为两个切向量是不固定的。

 

2.切线空间坐标系:

现在将你的上方(沿着默认法线方向)作为Z轴,你的右边就是X轴,前方就是Y轴,其实这里说了个右边和前方,你站在一个切面上何为你的右?其实这里应该理解成切面上两个相互垂直的轴,这两个轴定义为X轴和Y轴。

这个图很清晰的呈现了法线坐标系,其中(0,0,1)就是法向量,而(1,0,0)和(0,1,0)分别是切向量X轴和切向量Y轴。图中的NT(0.3,0,0.8)就是纹理存储的新的法向量,其中(x,y,z)是由RGB三色分别存储的。

 

3.法向量贴图:

其实法向量贴图的工作原理上面已经说的很清楚了,就是把平面上的某一点的原本垂直平面的发向量改为一个不垂直于平面的新向量,法向量的方向改变了,这个点的切平面的方向肯定也就跟着改变了,然后计算光照还是那种方式进行计算。那么要实现发向量贴图有几个需要面对的问题:

(1)NT(0.3,0,0.8)这个坐标里面的值都是float的,而纹理存储的R为0-255的整数值,在读取RGB的时候需要转换到(-1,1)这个区间。

(2)NT(0.3,0,0.8)坐标是存在于法向量坐标系里面的一个坐标,而计算光照的其它的像光的方向,视角的方向都是世界坐标系里面的坐标。如果要进行光照计算,就必须把它们转换到同一坐标系里面。

(3)在进行转换前,我们必须找出法向量坐标系与世界坐标系之间的关系,就是求出法向量坐标系里面的X,Y,Z轴在世界坐标系里面的描述。通常用TBN来进行描述,TBN是指tagent,binormal 和normal。

 

4.RGB与TBN的映射:

我们颜色值是0-255,在hlsl里面表示的是(0,1.0)的浮点,而TBN是(-1,1),所以我们面对的是(0,1)与(-1,1)的转换而不是(0,255)与(-1,1)的转换,转换公式:

 

color * 2.0f - 1.0f

 

 这样就得到了TBN法线坐标系的坐标值。

 

5.为什么法向量不直接存储成世界坐标系?

我在网上找资料的时候看到过这个问题以及和对这个问题的解释,包括有人把纹理坐标和法向量坐标系扯到一起了(就因为这个,我硬是没整明白)。想整明白这个问题,只需要一句话解决,“是先有世界,还是先有法向量纹理?”。把这句话推广,是先有世界里面的元素,还是先有世界?很显然是先有世界里面的元素。对于一个3D虚拟世界来说,世界就是我们所说的场景,而世界坐标也就是我们所说的场景坐标。

世界坐标的意义就是告诉我们,场景中的某个点有什么,如果这个点是模型的某一个顶点,那么它会有颜色,这个颜色除了RGB以外还有(U,V)坐标映射的一个颜色值,还有TBN映射的一个法向量值(最终会生成一个颜色)。总结:我们在场景里通过世界坐标系把模型组织起来,建立起模型之间的联系。然后模型通过(U,V)把纹理和法向量组联系起来,这里又回到一个问题,为啥不把法向量存储为相对坐标。相对坐标是相对于一个模型自己都不知道自己参照谁那个点为原点建立的一个坐标系,它不关心参照的(0,0,0)点到底是在那个位置,它关心的是它与它周围点之间的关系。

不把法向量存储为相对坐标,是因为它与世界建立的联系的规则与模型不一样,模型通过简单平移缩放旋转与世界建立关系,而法向量是另一套规则。法向量最终想转换为世界坐标系里面的向量,还需要参考法向量对应的点在世界坐标中的位置等信息。(我试图把自己说明白,结果发现自己也有点不清楚了,但是我觉得是对的,就像模型有点对世界的坐标系一样。相对坐标系是用的(X,Y,Z)轴来描述,但是它不是世界坐标,它需要进行转换。而TBN只不过名字叫这个,其实它也是一个相对坐标系,只不过它的转换规则与模型的不一样)

总结:所以我按正常思维来看,应该把法线由法向量坐标系转换到世界坐标系,人家模型都是把自己相对坐标转换为世界坐标,法线为啥就搞特殊呢。

 

6.TBN坐标系的计算:

一般是通过一个三角形面的三个顶点来计算出这个面的TBN,然后每个顶点的TBN是它所有面的TBN求平均值。计算原理:

 

p = p1 + (u - u1)T + (v - v1)B; 
p = p2 + (u - u2)T + (v - v2)B; 
p = p3 + (u - u3)T + (v - v3)B;
==>
p2 - p1 = (u2 - u1)T + (v2 - v1)B; 
p3 - p1 = (u3 - u1)T + (v3 - v1)B;
==>
T = [(v3 - v1)(p2 - p1) - (v2 - v1)(p3 - p1)]/[(u2 - u1)(v3 - v1) - (u3 - u1)(v2 - v1)];  
B = [(u3 - u1)(p2 - p1) - (u2 - u1)(p3 - p1)]/[(u3 - u1)(v2 - v1) - (u2 - u1)(v3 - v1)]; 
N = Corss(T,B);

 

 N是法向量永远垂直于平面,而TB可以是切平面上任意的两条垂直的向量,所以不引入一个第三方的量来确定其中的一个向量,另外两个都不好求。这里面使用p1,p2,p3的法向量纹理的(u,v)坐标来作为参量,求出TBN来。(那个三元一次方程组是根据什么来的,没想明白)。

有了TBN后就可使用下面的矩阵将切线空间下的点转换到对象空间: 

 

[Tx    Bx    Nx ]  
[Ty    By    Ny ]  
[Tz    Bz    Nz ]

 

 使用Gram-Schmidt算法对矩阵做正交规格化,然后可以使用下面的矩阵将一个方向向量转换到切线空间下:

 

[T'x    T'y    T'z]  
[B'x    B'y    B'z]  
[Nx     Ny     Nz ]
其中: 
T' = Normalized(T - Dot(N,T)*N); 
B' = Normalized(B - Dot(N,B)*N - Dot(T',B)*T'); 

 TBN矩阵是正交矩阵,它的转置矩阵就是逆矩阵。

 

由此可见,只要一个模型的顶点相对位置和纹理坐标不变,那么这个模型绘制出的各个像素点的切线空间就不会变。 

于是将法线贴图的法线信息存放在切线坐标系下更加保险,可以保证模型经过旋转和位移之后,法线贴图中的法线依旧正确。(借鉴的别的推导结果)

 

关键代码:

 

// vs
float3x3 tangentToObject;
tangentToObject[0] = normalize(input.viBinormal); 
tangentToObject[1] = normalize(input.viTangent); 
tangentToObject[2] = normalize(input.viNormal); 
output.piTangentToWorld = mul(tangentToObject, mbWorld); 
 
// ps
//从范围[0,1]转换到[-1,1]
float3 normalT = (normalTextureColor - 0.5f)*2.0f; 
float3 N = mul(normalT, input.piTangentToWorld); 

法向量贴图关键是概念的理解,实现起来的代码并不多。

 

分享到:
评论

相关推荐

    openGL法线贴图混合纹理贴图以达到更好的物体表面细节

    openGL法线贴图混合纹理贴图以达到更好的物体表面细节 1.凹凸贴图是使用数学公式产生物体表面凹凸特效 2.凹凸贴图的一种替代方法是使用查找表来替换法向量 3.一种使用查找表的常见方法叫作法线贴图。 4.法线贴图使用...

    openGL法线贴图原理以及实现方法

    1.凹凸贴图的一种替代方法是使用查找表来替换法向量。这样我们就可以在不依赖数学函 数的情况下,对凸起进行构造,例如月球上的陨石坑所对应的凸起。一种使用查找表的常 见方法叫作法线贴图。 2.为了理解法线贴图的...

    使用cesium创建Geometry并加载图片作为贴图

    在cesium中,创建自定义的Geometry形状 并制定贴图规则 把图片作为材质贴到自己的geometry上 通过primitives加载自定义的geometry 可通过更换appearance实现动态切换图片

    android opengl es 飘动的国旗

    5. 计算各顶点所对应的法向量时遇到很大的麻烦,由于长时间没有接触相关的数学知识,对法向量的概念有点生疏了,后来查找资料对法向量的概念进行复习,我写的生成顶点法向量的方法比较麻烦,写了5个方法来实现顶点法...

    openGL高度贴图,使用纹理图像来存储高度值,然后使用该高度值来提升(或降低)顶 点位置。

    1.现在我们扩展法线贴图的概念——从纹理图像用于扰动法向量到扰乱顶点位置本身。实 际上,以这种方式修改对象的几何体具有一定的优势,例如使表面特征沿着对象的边缘可 见,并使特征能够响应阴影贴图。我们将会看到...

    基于三角面片顶点均值的网格编辑方法 (2009年)

    同时,为了增强网格模型显示的真实感,通过网格简化、网格平滑、网格数据点法向量计算、纹理贴图等方法,对网格模型进行优化,提高了整形后网格显示效果。实验证明,该方法交互方式简便,变形效果较好。

    raise-and-shine:带有自动法线贴图生成的高度图编辑器,用于2D纹理

    升起和闪耀带有自动法线贴图生成功能的高度图编辑器,用于2D纹理。 它具有高度和动态照明预览的3D可视化功能,使创建法线贴图非常容易。 由制成,即将推出!学分Heayale Heale的P1_2纹理: :

    精通DirectX.3D图形与动画程序设计.pdf

    13.1.4 法向量  13.1.5 纹理 13.1.6 框架和变换矩阵 13.1.7 动画 13.1.8 蒙皮信息 13.2 网格模型优化 13.2.1 网格模型优化函数 13.2.2 网格模型优化方式 13.2.3 示例程序optimizedmesh具体实现 13.3 层次细节网格...

    openGL增强表面细节,模拟地球表面地形

    2.现在我们扩展法线贴图的概念——从纹理图像用于扰动法向量到扰乱顶点位置本身。实 际上,以这种方式修改对象的几何体具有一定的优势,例如使表面特征沿着对象的边缘可 见,并使特征能够响应阴影贴图。我们将会看到...

    Android 3D游戏开发技术宝典-OpenGL ES 2.0 (吴亚峰) 源代码

    15.6.2 法向量纹理图的生成 396 15.6.3 案例的开发 399 15.7 平面阴影 404 15.7.1 案例效果与基本原理 405 15.7.2 开发步骤 406 15.8 阴影映射 409 15.8.1 案例效果与基本原理 409 15.8.2 ...

    OpenSceneGraph三维渲染引擎编程指南.pdf

    4.5.8 生成顶点法向量示例 112 第5章 渲染状态、纹理与光照 115 5.1 渲染状态 116 5.1.1 osg::StateSet类 116 5.1.2 渲染属性和渲染模式 117 5.1.3 状态继承 118 5.1.4 渲染状态示例 119 5.2 纹理映射 ...

    OpenSceneGraph三维渲染引擎编程指南

    4.5.8 生成顶点法向量示例 112 第5章 渲染状态、纹理与光照 115 5.1 渲染状态 116 5.1.1 osg::StateSet类 116 5.1.2 渲染属性和渲染模式 117 5.1.3 状态继承 118 5.1.4 渲染状态示例 119 5.2 纹理映射 121 ...

    ArcGIS Engine栅格数据使用总结

    另外,如果要用Multipatch表达复杂连续的几何对象,比如球体,为了平滑阴影平面之间的转折,需要在Multipatch的每一个顶点上记录它的法向量,同样法向量也是编码后存储在定点的M值中。如下面的代码: Dim pNormal ...

    3D游戏卷2:动画与高级实时渲染技术——2

    4.6.5 立方贴图和向量规范化 4.7 实现BRDF:可分离的近似 4.8 着色语言和着色器 4.8.1 着色语言:简单的历史回顾 4.8.2 RenderMan着色语言 4.8.3 实时渲染的着色语言 第5章 实时渲染:实践 5.1 基本着色器 5.1.1 ...

    3D游戏卷2:动画与高级实时渲染技术——1

    4.6.5 立方贴图和向量规范化 4.7 实现BRDF:可分离的近似 4.8 着色语言和着色器 4.8.1 着色语言:简单的历史回顾 4.8.2 RenderMan着色语言 4.8.3 实时渲染的着色语言 第5章 实时渲染:实践 5.1 基本着色器 5.1.1 ...

Global site tag (gtag.js) - Google Analytics