两篇文章

法线变换详解(Normal Transform)

在图形学中,同样的一个模型视图变换矩阵可以用来变换点、线、多边形以及其它几何体,也可以变换多边形表面的切向量。比如:

posEyeSpace = ModelViewMatrix * posModelSpace。

但是,同样的方式通常却不能够用于法线的变换(注意:在有些情况下是可以的)。

一、法线和顶点坐标的区别

顶点坐标<x,y,z>表示缺省的<x,y,z,1>,而法线向量的<x,y,z>表示缺省的<x,y,z,0>。

法线向量只能保证方向的一致性,而不能保证位置的一致性。

下面我们通过一个例子来看看问题所在。

上图是针对一个多边形以及一条边上的法线进行缩放变换:X轴上缩放为原来的0.5倍。左边是变换前的状态,中间是将同样的模型变换矩阵应用在法线上的结果,显然是错的,法线并不垂直于切线。最右边的图是正确的结果。

二、法线变换:应该用变换矩阵的逆转置矩阵

假设Model space中的某条切线向量是T,法线向量是N。

那么由他们是垂直的可得到:TTN=0 【我的理解:点乘为0 ,表示夹角为90度】

假设他们变换到Eye space中后分别是T'和N'。那么他们应该仍然是相互垂直的:T’TN’=0  【我的理解:点乘为0 ,表示夹角为90度】

假设切线向量和法线的变换矩阵为M、G。则有:(MT)T(GN)=0    【我的理解:乘上各自的变换】

进一步推出:TTMTGN=0     【我的理解:  (MT)T    =  TTMT 】

由于TTN=0,因此我们猜想MTG=0.因此:

G=(M-1)T       【我的理解:( MT)-1   等于 (M-1)T     下面有推导】

即:应用于法线向量的变换矩阵是顶点变换矩阵的逆转置矩阵。

三、法线变换矩阵求解的优化

传统求解逆转置矩阵的方法固然有效,但是求逆矩阵有时候不是必须的,而且有的时候逆矩阵也不存在。逆矩阵是矩阵的伴随矩阵除以矩阵的行列式的值,但是当矩阵是奇异阵的时候,行列式的值是0,这个时候逆矩阵就不存在。

即便是求解一个4*4矩阵的伴随矩阵也是复杂的,而且有时候也不是必须的。由于平移操作不影响法线向量。而且大多数模型变换都是仿射变换。

关于仿射变换(AffineTransform):

AffineTransform类描述了一种二维仿射变换流程图射变换的功能,它是一种二维坐标到二维坐标之间的线性变换,保持二维图形的“平直性”(译注: straightness,即变换后直线还是直线不会打弯,圆弧还是圆弧)和“平行性”(译注:par  常用的仿射变换:旋转、倾斜、平移、缩放allelness,其实是指保二维图形间的相对位置关系不变,平行线还是平行线,而直线上点的位置顺序不变,另特别注意向量间夹角可能会发生变化。仿射变换可以通过一系列的原子变换的复合来实现,包括:平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和错切(Shear)。

因此,他们不会改变齐次坐标的w分量。例如,没有投影变换的时候。因此,这个时候只需要计算矩阵左上方3*3子矩阵的伴随矩阵即可。

在很多情况下,甚至是求解上述的3*3伴随矩阵都不需要。假设我们知道变换矩阵是由一系列平移、旋转、等方缩放(uniform scaling)组成的,平移不影响法线,等方缩放的只影响法线的长度。剩下需要考虑的就只有旋转变换了。而旋转矩阵有一个很重要的特定就是它的转置矩阵和逆矩阵相等。因此,这个时候逆转置矩阵就是原来的矩阵,这个时候就不需要任何计算。

最后需要注意的是,对法线向量进行归一化不总是必须的。如果只有平移和旋转矩阵,那么法线长度不会发生改变。如果也有等方缩放,那么直接用它的缩放因子进行归一化。

针对那些在变换之后,依据三角形边的叉积计算法线的情况,就不需要计算法线变换矩阵了。注意:切向量和法线不同,切线是依据原始的矩阵进行变换的。

参考资料:

OpenGL Normal Vector Transformation

http://www.lighthouse3d.com/tutorials/glsl-tutorial/the-normal-matrix/

http://www.arcsynthesis.org/gltut/Illumination/Tut09%20Normal%20Transformation.html

===============================================

3D 变换中法向量变换矩阵的推导潘李亮2003-11-23

xheartblue@etang.com

在一个 3D几何管道中,输入的顶点要经过一系列的变换,最终变换到一个投影空间里来,去掉最后的一个Z-坐标后就是一个规格化的2D的屏幕坐标。变换通常分成两个步骤,一是视图/模型变换(D3D里把这个分开成了两个变换世界变换和视图变换),二是投影变换Project.

当我们不去为一个顶点指定一个法向量的时候,一个多边形的顶点的法向量会由系统自动计算生成,计算的方法是由交成这个顶点的两条边做个叉积(叉积的时候注意叉积的方向),这个步骤通常在变换到视图空间后进行的。所以通常情况下我们是没有必要关心法向量是如何变换到视空间中来的。(为何是视图空间?而不是投影完成后的投影空间?原因是光照等计算都是在需要视图空间中的参数进行的。投影空间中的坐标只是为了裁剪和Z-Test用的)。

那么何时需要我们自己去关心法向量是如何变换的呢?这个谁也说不上来。但是肯定是需要的,比如你自己写一个Vertex Shader的时候,也许你需要用到法向量,这时就需要你用正确的变换方法把它变换到视图空间中来------当然你自己如果要写一个软件渲染器的话⋯⋯。下面我先来用数学公式严密的推导出变换公式,再来解释一些其他的误区。

假设:我们的 Model View变换矩阵为M,N 为世界空间中的法向量, P,P 为WV12

世界空间中的两个顶点,两个顶点所在的平面和N垂直。则我们很快就有如下的关系N•(P−P)= 0,即P−P和N的点积为零。在这里提醒大家一下。点积也可以看做是

12 12

一种运算。所以我们把它写 N ⊗ (P−P)T=0---------(1)。(⊗在这里表示矩阵相乘,12

(P−P)T为(P−P)的转置矩阵,下同)。1212

如果我们标记P',P',N'为P,P,N变换到视空间中的点和对应的法向量,同理我们12 12

也有N'⊗(P'1−P'2)T=0--------(2)
M ⊗PT−M⊗PT=M⊗(P−P)T=(P'−P1)T----------(3)

wv 1 wv2wv1 2 1 2联立1,2,3得:

1:N⊗(P−P)T=012

2:N'⊗(P'1−P'2)T=0⇒N=N′⊗M
wv

3:M⊗(P−P)T=(P'−P1)Twv1 2 1 2

(因为我们使用的左乘的变换矩阵,还要做进一步推算)⇒NT=MT⊗(N')T⇒(N')T=(MT)−1⊗NT

wv wv

到这里我们已经得到法向量的变换公式为 (MT)−1。其中M为Model View变换矩wv wv

阵。(注意,不要把投影矩阵也乘上去)。我们在使用法向量变换的时候最大的一个误区就是直接把Model View变换矩阵Mwv当

成法向量变换矩阵使用,而且肯定有人还曾经认为这是正确的,他们的理由有两个:一是法向量也是一个Vector。而Vertics表示的也是一个 Vector。为什么不是同一个矩阵?二是我用Model View矩阵去变换法向量的时候,结果看上去的也是正确的。对于第一种理由,我只能告诉你:法向量表示的是一个方向,而顶点表示的是一个位置,是不同的东西。对于第二种理由,主要是大家差不多都忘记了线性代数。如果一个变换矩阵只包含旋转的话,它一定

是个正交的矩阵,即: MT=Mwv

wv

而且,这时还有 M

wv

T=M−1的关系。结合一下可wv

知在一个只包含旋转的变换里。法向量的变换矩阵的确就是Model View矩阵。但是如果变换中包含非正交的因素,如:平移、错切等。那情况就不一样。即使你看到了所谓的正确的结果。那也是近似正确的,至少在理论上,它就是不正确的^_^。

法线变换详解 和 3D 变换中法向量变换矩阵的推导相关推荐

  1. 数字图像处理,图像的频域变换(四)——K-L变换详解 以及 哈尔变换Haar简介

    https://zh.wikipedia.org/wiki/%E5%8D%8F%E6%96%B9%E5%B7%AE%E7%9F%A9%E9%98%B5 KL中将五副图像作为原始图像的五个分量,然后使用 ...

  2. OpenCV-Python击中击不中HITMISS形态变换详解

    ☞ ░ 前往老猿Python博客 https://blog.csdn.net/LaoYuanPython ░ 一.引言 从学习完黑帽变换后的这段时间,都在学习和钻研基本形态变换的最后一个变换–击中击不 ...

  3. [Python从零到壹] 四十五.图像增强及运算篇之图像灰度非线性变换详解

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  4. Box-Cox变换详解

    Box-Cox变换详解 1 什么是 Box-Cox变换 box-cox变换是一种广泛应用于数据转换和归一化的方法,可以使数据更接近正态分布.它由两位统计学家 box 和 cox 发明,适用于连续的.正 ...

  5. python英语字典程序修改_详解如何修改python中字典的键和值

    我们知道python中字典是无序的,它们都是通过hash去对应的.一般的如果我们需要修改字典的值,只需要直接覆盖即可,而修改字典的键,则需要使用字典自带的pop函数,示例如下: t = {} t['a ...

  6. 详解Linux2.6内核中基于platform机制的驱动模型

    原文地址:详解Linux2.6内核中基于platform机制的驱动模型 作者:nacichan [摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了P ...

  7. 详解Java多线程编程中LockSupport类的线程阻塞用法

    转载自  详解Java多线程编程中LockSupport类的线程阻塞用法 LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语.LockSupport实际 ...

  8. 详解在Visual Studio中使用git版本系统 [转]

    详解在Visual Studio中使用git版本系统    作者:掷鸡蛋者 , 发布于2012-6-21   这篇教程的预期,是希望没有任何版本使用基础的新手也可以掌握,所以细节较多,不当之处,欢迎指 ...

  9. php 内存池,内存详解: 详解PHP内存池中的存储层_php

    php的内存管理器是分层(hierarchical)的.这个管理器共有三层:存储层(storage).堆(heap)层和 emalloc/efree 层.存储层通过 malloc().mmap() 等 ...

最新文章

  1. RANSAC鲁棒参数估计
  2. InnoDB的启动,关闭,恢复
  3. 【商务智能】数据仓库 ( 多维数据模型 | 多维数据分析 )
  4. 1.深度好文:带缓冲I/O 和不带缓冲I/O的区别与联系
  5. android自动跑马灯,Android-最强跑马灯
  6. 【并查集】Supermarket(poj 1456/luogu-UVA1316)
  7. 如果你是壁纸控,高清图片这里找!
  8. linux 网络端口全连接扫描,端口全连接扫描程序(Linux, socket):TCP的connect方式...
  9. 远程mysql用ssh连接_使用SSH密钥连接到远程MySQL服务器
  10. 轻轻松松统计代码行数
  11. wpf 开发 -TextBox背景自定义-Decorator
  12. 几种经典病毒动力学模型【基于matlab的动力学模型学习笔记_3】
  13. NTC与PTC压敏电阻在电源电路中起的作用
  14. 数据结构WSADATA
  15. [渝粤教育] 中国地质大学 大学语文 复习题 (2)
  16. linux pkg解压工具,Pkg 1.3.0 发布,FreeBSD 的包管理工具
  17. 云原生落地实践的25个步骤
  18. python正负数转换_python – 将正/负数舍入到最接近的“整数”
  19. 【渝粤题库】陕西师范大学180108 有效教学的理论与实践
  20. 华为笔记本在linux下越狱苹果设备(2022.2.27更新)

热门文章

  1. 优秀课程案例|如何用scratch画折线统计图
  2. 标准差计算-python(有偏无偏)
  3. 【Maven】---Linux搭建Nexus3.X私服
  4. 基于Hi3861的听话的狗子
  5. 广东某银行基于阿凡搭在信创环境下打造全行科技一体化服务平台
  6. 深入理解RPC-RPC要解决的核心问题和在企业服务中的地位
  7. js将数组转化成json
  8. PHP底层入门的一些概念
  9. 【离散数学】数理逻辑 第一章 命题逻辑(7) 命题逻辑的推理理论
  10. MySQL数据库(三)-表行的语句使用