热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

模型不等比缩放后法线不再垂直表面的问题

如果模型矩阵执行了不等比缩放,顶点的改变会导致法向量不再垂直于表面了。因此,我们不能用这样的模型矩阵来变换法向量。下面的图展示了应用了不等比缩放的模型矩

如果模型矩阵执行了不等比缩放,顶点的改变会导致法向量不再垂直于表面了。因此,我们不能用这样的模型矩阵来变换法向量。下面的图展示了应用了不等比缩放的模型矩阵对法向量的影响:

每当我们应用一个不等比缩放时(注意:等比缩放不会破坏法线,因为法线的方向没被改变,仅仅改变了法线的长度,而这很容易通过标准化来修复),法向量就不会再垂直于对应的表面了,这样光照就会被破坏。

 

修复这个行为的诀窍是使用一个为法向量专门定制的模型矩阵。这个矩阵称之为法线矩阵(Normal Matrix),它使用了一些线性代数的操作来移除对法向量错误缩放的影响。

法线矩阵被定义为「模型矩阵左上角的逆矩阵的转置矩阵」。

 

具体推导过程为:

vertexEyeSpace = modelViewMatrix * vertex;

modelEyeSpace = modelViewMatrix * vec4(normal, 0);

 

已知线段T = p2-p1;(P2和P1是两个顶点的位置)

T' = T * modelViewMatrix = (p2 - p1) * modelview;

= p2 * modelview - p1 * modelview;

也就是说相当于p‘2 - p'1

假设:

N = Q2-Q1;

因为线段和法线的点乘必须要为0

dot(T,N) = 0;

所以转换后的线段和法线的点乘也必须为0才是我们想要的

dot(T',N') = 0;

 

假设视图左上角的子矩阵为M

然后假设矩阵G是变换法向量的正确矩阵

得:

dot(G*N, M*T) = 0;

根据点乘公式得出dot(t,n)=|t|*|n|*cosα。

得:

dot(T', N') = dot(G*N, M*T) = inverse(GN) * MT;

 

已知矩阵GN和GN的逆矩阵的乘积为单元矩阵。

inverse(GN)*GN = I

同时正交矩阵的transpose(GN)*GN = I

两边都乘以转换矩阵时

transpose(GN)*inverse(GN) * GN = inverse(GN)

transpose(GN) = inverse(GN)

 

所以

dot(T', N') = transpose(GN) * MT;

拆解后得:

dot(T', N') = transpose(G) * transpose(N) * M*T;

 

假设transpose(G) * M = I时

dot(N', T') = dot(N, T) = 0;

所以得出我们要求的

G = transpose(inverse(M))

 

这就是“模型矩阵左上角的逆矩阵的转置矩阵”的整体推导过程。

 

另外:法向量只是一个方向向量,不能表达空间中的特定位置。同时,法向量没有齐次坐标(顶点位置中的w分量)。这意味着,位移不应该影响到法向量。因此,如果我们打算把法向量乘以一个模型矩阵,我们就要从矩阵中移除位移部分,只选用模型矩阵左上角3×3的矩阵(注意,我们也可以把法向量的w分量设置为0,再乘以4×4矩阵;这同样可以移除位移)。对于法向量,我们只希望对它实施缩放和旋转变换。

 

最终的做法是:

Normal = mat3(transpose(inverse(model))) * aNormal;


推荐阅读
author-avatar
无情的有情人家_834
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有