http://blog.csdn.net/racehorse/article/details/6664775

法线矩阵
在很多顶点shader中都用到了gl_NormalMatrix。这里将介绍这个矩阵是什么,以及它的作用。
大部分计算是在视图空间内完成的,主要原因是光照的运算要放在这个空间内,否则一些依赖观察点坐标的效果,比如镜面反射光就很难实现。
所以我们需要将法线变换到视图空间。变换一个顶点到视图空间的方法如下:

  1. vertexEyeSpace = gl_ModelViewMatrix * gl_Vertex;

对法线也能如此操作吗?一个法线是3个浮点数组成的向量,而模型视图矩阵是一个4×4的矩阵。另外,因为法线是一个向量,我们只需要变换它的方向,而模型视图矩阵中左上方的3×3子矩阵正好包含了旋转变换。所以可不可以用法线来乘这个子矩阵呢?
下面的代码很简单地实现了这个要求:

  1. normalEyeSpace = vec3(gl_ModelViewMatrix * vec4(gl_Normal,0.0));

这样的话,gl_NormalMatrix还有什么用呢?只是为了简化代码书写吗?实际当然不是这么简单,上面的代码在某些情况下是有效的,但不能应对所有情况。
让我们看看潜在的问题:

在上图中我们可以看到一个三角面以及它的法线和切线。下图显示了如果模型视图矩阵包含非一致缩放(non-uniform scale)的话会发生什么。

注意,如果缩放是一致的(uniform),那么法线方向保持不变,变的只是长度,而且可以通过归一化修正这个影响。
在上图中,模型视图矩阵应用到所有顶点以及法线上,最后的结果明显错误:法线不再与三角面垂直了。
现在我们知道模型视图矩阵在某些情况下,不能用来变换法线向量。下面的问题就是:那么该使用哪个矩阵?
考虑一个3×3矩阵G,让我们看看要正确变换法线,这个矩阵该是什么样子。
我们知道,变换前切线和法线是垂直的,即T•N = 0,在变换后切线和法线同样应该保持垂直,即T’•N’ =
0。现在假设G是正确变换法线的矩阵,同时模型视图矩阵的左上3×3子矩阵M可以正确变换切线T(T是一个向量,所以w成分为0)。因为T可以通过两个顶
点的差来计算,所以变换顶点的矩阵同样可以用来变换T。由此可以得到如下等式:

向量的点乘相当于向量的内积,所以有:

我们知道相乘的转置等于分别转置再交换顺序相乘:

已知N和T点乘结果为0,所以如果下式成立就可以满足等式为0:

即有:

可见变换法线的正确矩阵是M的逆的转置。OpenGL计算出的这个矩阵就保存在gl_NormalMatrix里。
在本节开始讨论过,某些情况下使用模型视图矩阵也可以。当模型视图矩阵的左上3×3子矩阵M正交时,可以得到:

一个正交矩阵的所有行/列都为单位向量,并且互相正交。当两个向量乘上正交矩阵时,它们之间的夹角在变换前后不变。由于这种保角变换的关系,所以法线和切线依然保存垂直。此外,向量的长度也保持不变。
M在什么时候能确定为正交的呢?当我们把几何变换限制为旋转和平移时(在OpenGL应用程序中只使用glRotate和glTranslate,而不使用glScale),就可以保证M正交。注意:gluLookAt同样建立正交矩阵。

关于法线归一化

当一个法线到达顶点shader后,我们一般会将它归一化:

  1. normal = normalize(gl_NormalMatrix * gl_Normal);

法线与gl_NormalMatrix矩阵相乘,将会被变换到视图空间。归一化向量可以保证使用点乘得到余弦值。
我们可以避免归一化计算吗?在某些情况下是可行的。如果gl_NormalMatrix是正交矩阵,那么经过变换后输入法线的长度不会变,依然等于gl_Normal。所以如果在OpenGL程序中法线已经是归一化的,那么在shader中就不需要在重复了。
也就是说,如果我们使用gluLookAt设置照相机,对模型值进行旋转和平移变换,就可以在shader中避免使用归一化操作。这对于归一化过的光线向量也是适用的。
片断shader的情况
在片断shader中,我们经常发现需要重新归一化在顶点shader中归一化的法线。这是必要的吗?答案是肯定的。
考虑一个包含三个不同顶点法线的三角面。片断shader接收经过插值的法线,插值基于距离三个顶点的远近。这样得到的法线方向是对的,但不再是单位长度了。
下图显示了原因。图中黑线表示三角面,顶点法线用蓝色表示,插值得到的片断法线用绿色表示。所有的插值法线排列在黑色的点划线上。从图上可以看出绿色的插值法线大小小于单位长度的顶点法线。

注意,如果顶点法线没有单位化,那么得到的插值法线的方向也将是错误的。所以,即使一个顶点没有在顶点shader用到,也可能要对它在顶点shader中进行归一化。
有一种情况,在片断shader中可以避免归一化操作,那就是每个顶点法线方向相同,而且顶点法线是经过归一化的。此时顶点法线插值得到的结果都相同。
以方向光为例,每个片断都需要考虑光线方向,如果光线向量已经在之前归一化了,在片断shader中就可以避免归一化这一步。

【GLSL教程】(九)其他说明 【转】的更多相关文章

  1. CRL快速开发框架系列教程九(导入/导出数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  2. 无废话ExtJs 入门教程九[数字字段:NumberField、隐藏字段Hidden、日期字段:DataFiedl]

    无废话ExtJs 入门教程九[数字字段:NumberField.隐藏字段Hidden.日期字段:DataFiedl] extjs技术交流,欢迎加群(201926085) 继上第六节内容,我们在表单里加 ...

  3. 黄聪:Microsoft Enterprise Library 5.0 系列教程(九) Policy Injection Application Block

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(九) Policy Injection Application Block 代理对象(Proxy Object) ...

  4. 【GLSL教程】(一)图形流水线 【转】

    http://blog.csdn.net/racehorse/article/details/6593719 这是一些列来自lighthouse3d的GLSL教程,非常适合入门.我将边学习边翻译该教程 ...

  5. RabbitMQ入门教程(九):首部交换机Headers

    原文:RabbitMQ入门教程(九):首部交换机Headers 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog ...

  6. WPF教程九:理解WPF中的对象资源

    在WPF中,所有继承自FrameworkElement的元素都包含一个Resources属性,这个属性就是我们这篇要讲的资源. 这一篇讲解的资源是不是上一篇的程序集资源(那个是在编译过程中打包到程序集 ...

  7. 【GLSL教程】(八)纹理贴图 【转】

    http://blog.csdn.net/racehorse/article/details/6664717 简单的纹理贴图(Simple Texture) 为了在GLSL中应用纹理,我们需要访问每个 ...

  8. 【GLSL教程】(六)逐顶点的光照 【转】

    引言 在OpenGL中有三种类型的光:方向光(directional).点光(point).聚光(spotlight).本教程将从方向光讲起,首先我们将使用GLSL来模仿OpenGL中的光. 我们将向 ...

  9. 【GLSL教程】(五)卡通着色 【转】

    http://blog.csdn.net/racehorse/article/details/6641623 引言 卡通着色可能是最简单的非真实模式shader.它使用很少的颜色,通常是几种色调(to ...

随机推荐

  1. Appium的三种等待时间设置方法

    #三种appium设置等待时间的方法 #作者:Mr.Dantes  #参考了网上的资料,然后进行了梳理   #第一种 sleep(): 设置固定休眠时间. python 的 time 包提供了休眠方法 ...

  2. springcloud 高可用分布式配置中心

    SpringCloud教程七:高可用的分布式配置中心(SpringCloud Config) 当服务有很多 都要从服务中心获取配置时 这是可以将服务中心分布式处理 是系统具备在集群下的大数据处理 主要 ...

  3. redis 集群添加新节点

    准备好需要添加的节点:如何创建节点 启动创建的节点: 启动成功: 添加新节点:redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000  第 ...

  4. 【bzoj3217】ALOEXT 替罪羊树套Trie树

    题目描述 taorunz平时最喜欢的东西就是可移动存储器了……只要看到别人的可移动存储器,他总是用尽一切办法把它里面的东西弄到手. 突然有一天,taorunz来到了一个密室,里面放着一排可移动存储器, ...

  5. 写js时常见错误

    最近几天写js时出现好多相同的错误,确实应该记下来了 ReferenceError: invalid assignment left-hand side 判断相等时把"=="写成& ...

  6. 挑战程序竞赛例题 4.1 Random Walk(高斯消元求期望值)

    给你一幅N*M的地图,地图中有不能到达的障碍物'#'与可以走的点'.',从(1,1)开始走到(N,M),其中每一次走动均等概率地向周围的可达的格子走去,求到达(N,M)的期望步数.(N,M<=1 ...

  7. swarm 集群管理

    1.创建服务 docker service create --replicas 1 --name hello busybox ping baiud.com 2.显示服务详细信息 3.扩展服务数量 4. ...

  8. 【14】redux 之 redux-actions

    redux-actions有两大法宝createAction和handleActions. createAction http://www.jianshu.com/p/6ba5cd795077 原来创 ...

  9. POJ3170 Bzoj1671 [Usaco2005 Dec]Knights of Ni 骑士

    1671: [Usaco2005 Dec]Knights of Ni 骑士 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 281  Solved: 180 ...

  10. 拖动层 拖动div 封装js 貌似不兼容FF,郁闷

    原文发布时间为:2009-12-02 -- 来源于本人的百度文章 [由搬家工具导入] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tran ...