目录
一 为什么要使用法线贴图
二 二种不同法线方式的使用
2.1 插值法线
2.1 法线贴图
本章节源码 点击此处
一 为什么要使用法线贴图
法线贴图我们可以使用更少的顶点表现出同样丰富的细节。高精度网格和使用法线贴图的低精度网格几乎区分不出来。所以法线贴图不仅看起来漂亮,它也是一个将高精度多边形转换为低精度多边形而不失细节的重要工具。
我们知道不是所有的物体表面都是光滑平整的平面,比如我么要绘制一个人脸,但人脸会由,头发,五官,甚至是微表情等组成这些都是具有凹凸感的,那我们该如何体现出这种凹凸感呢?
- 第一种方式: 传入大量的顶点坐标,让大量的顶点来帮我们实现人脸中的各个细节。但这无疑是会增大额外的开销。
- 第二种方式: 我们使用法线贴图,来为每个像素上都设置不同的法线值,这样实现的效果和大量的顶点坐标几乎一样。
如下图所示: 左边第一个是传入了上百万的顶点坐标实现的效果,而最中间的是传入了500个顶点实现的效果,最后一个是在中间的基础上运用的法线贴图实现的效果,他们的效果几乎是一样的。
二 二种不同法线方式的使用
- 通俗的理解法线的作用是在计算光照时根据角度的不同,反映在颜色上的变化,比如如果法线与光线垂直那么就会光照效果最大,颜色最亮。
- 一般有两种不同方式来传递给GPU法线,一种是采样插值法线,也就是为每个顶点传递它的法线,另一种是采用法线贴图。
2.1 插值法线
- 观察下面这个图我们是使用插值法线得到的效果,我们传入的是6个顶点也就是2个三角形,而这6个顶点位于同一个平面,我们使用插值法线一般也是会插入6个法线向量,但同一个平面我们此时传入的法线其实是相同的,那会造成什么问题呢?
- 例如,砖块的表面。砖块的表面非常粗糙,显然不是完全平坦的:它包含着接缝处水泥凹痕,以及非常多的细小的空洞。如果我们在一个有光的场景中看这样一个砖块的表面,问题就出来了。下图中我们可以看到砖块纹理应用到了平坦的表面,我们并看不到砖缝以及砖块表面的一些细节或者说不是很明显。
- 对于插值法线,同一个片段上我们可以理解为法线是相同的,以光的视角来说,这个表面就是完全平坦的,即使本身提供的纹理图片上可能会看到一些砖缝但这并不能完全体现出这种凹凸不平的平面的细节。
- 我们可以使用一种技术,让每个顶点拥有自己单独的法线,当光源照射到顶点时,根据法线的不同就能体现出更过的细节。替代一个面上所有fragment使用同一个法线的技术叫做法线贴图(normal mapping)或凹凸贴图(bump mapping)
2.1 法线贴图
原理
- 在使用法线贴图前我们应该了解一下法线贴图的原理。其实就是用一个2D的纹理来保存每个片段的法线.
- 纹理中的rgb就刚好对应了法线的xyz坐标
- 这也就是我们为什么看到下面这个纹理大部分是蓝色,而在砖缝中表现的偏绿色,是因为这个纹理贴图大部分是指向z轴的也就是rgb中的b(蓝色),而砖缝中就偏向于绿色也就是指向y轴。
- 这里有个值得注意的点,纹理中的像素颜色是0-1之间的,我们需要把它转换到标准坐标中的-1-1之间。
vec3 normTemp = vec3(texture(texture_normal1,TexCoords));
norm = normalize(normTemp * 2.0 -1.0);
- 这样我们在片段着色器中,就能够获取到每个片段上对于的法线值了(其实就是纹理颜色值)。
效果:
- 这样我们就能看到这个墙壁的诸多细节了。
三 代码开关
- 在源码中我们使用一个开关bIsTexture,当我们点击dockwidgt中的插值法线时,该值为false,则采用默认的插值法线来计算,如果点击法线贴图改值为true,采用法线贴图来渲染
vec3 norm;
if(bIsTexture == true)
{
vec3 normTemp = vec3(texture(texture_normal1,TexCoords));
norm = normalize(normTemp * 2.0 -1.0);
}else
{
norm = normalize(Normal);
}