当前位置:网站首页>切线空间以及TBN矩阵
切线空间以及TBN矩阵
2022-07-27 05:21:00 【122&&113】
参考书籍:UnityShader入门精要
参考文章:为什么要有切线空间(Tangent Space),它的作用是什么?
切线空间
首先需要了解一个前提,在图形学中我们会用到很多的坐标空间,而切线空间只是其中一个,其它的诸如 模型空间,世界空间,观察空间,裁切空间,屏幕空间 有时间会在后续写博客来加深自己的理解。
定义
切线空间的原点就是顶点本身,z轴是顶点的法线方向(n),x轴是顶点的切线方向(tangent, t)。Attention!!! 既然以及知道了两个轴,那么通过叉积运算,我们可以得到y轴,其被称为副切线(bitangent, b) 或者 副法线。

如图所示,
蓝轴为法线,红轴为切线,绿轴为副切线。
抛出问题?
切线定义:几何上,切线指的是一条刚好触碰到曲线上某一点的直线。更准确地说,当切线经过曲线上的某点(即切点)时,切线的方向与曲线上该点的方向是相同的。平面几何中,将和圆只有一个公共交点的直线叫做圆的切线。
对于三维空间中,更多是用切平面这个概念。
那么我们可以知道,一个平面存在无数条直线,如何来选择我们想要的切线呢?

有聪明的人想到了,要不干脆直接使用和纹理坐标方向相同的那条**tangent(T)**吧。只是我认为哈! 我们所使用到的法线贴图正好也是纹理,有个统一标准嘛。
LookLook!
该图片来自于为什么要有切线空间(Tangent Space),它的作用是什么? - Milo Yip的回答 - 知乎

左边是模型空间下的法线纹理,右边则是切线空间下的法线纹理,可以看到右边偏蓝,Why???
一个一个来理解~~~
模型空间:既然是模型空间,那么其法线方向是各种各样的,其 x , y , z x,y,z x,y,z 分量的范围主要是 [ − 1 , 1 ] [-1, 1] [−1,1] ,由于存储在纹理中,因此需要做一个映射,将区间映射到 [ 0 , 1 ] [0, 1] [0,1],即 f ( n ⃗ ) = n ⃗ 2 + [ 0.5 , 0.5 , 0.5 ] f(\vec{n}) = \frac{\vec{n}}{2} + [0.5, 0.5, 0.5] f(n)=2n+[0.5,0.5,0.5] ,这里有点偏题了。。回到重点,由于方向各异,导致其法线向量转存为纹理中的rgb值时颜色就比较花里胡哨了。
切线空间:映射和上面还是一样的,不过我们之前已经说到了切线空间下法线充当的是z轴,也就表明法线向量的主要分布是 n ⃗ = [ 0 , 0 , 1 ] \vec{n}=[0, 0, 1] n=[0,0,1],映射后的值为 [ 0.5 , 0.5 , 1.0 ] [0.5, 0.5, 1.0] [0.5,0.5,1.0],rgb中显示为浅蓝色,到这里其实已经可以清楚的知道为什么切线空间偏蓝了吧。当然也不全是浅蓝色,其他颜色即表示对其法线方向进行了扰动。你想哈,如果法线不变,那么其就是浅蓝色,稍微有点扰动,其颜色就会发生一点变化。
精华来了,优缺点!!!
模型空间下存储法线的优点:
- 实现简单,直观。不需要其它的数据,直接存就完事了,现存现用。
- 纹理坐标的缝合处或者边边角角,可以通过插值使得边界平滑。
切线空间下存储法线的优点:
- 自由度更高。模型空间用的相当于是绝对法线信息,也就是说独一无二,只能用在一个模型上,但是切线空间记录的是相对法线信息,即使放在其它模型上也能用。
- 可以进行UV动画。比如移动UV坐标来得到水流的效果等。
- 可重用。上面已经提到了:相对法线信息。
- 可压缩。切线空间下的法线方向的z轴方向都是正的,因此可以只记录xy方向,通过推导得到z方向。
缺点就是两者之间没有的优点
TBN矩阵
我个人觉得先了解坐标空间变换的概念,更有利于理解这个TBN矩阵。
坐标空间的变换
直接点,想象一下我们的世界,我们怎么来定位一个物体的,都是用的相对位置,因此在图形学中也是这样,不管在什么坐标空间,其实都是相对的。
如何转换呢?
A p = M c → p A C B c = M p → c B p A_p=M_{c\to p}A_C \\ B_c=M_{p\to c}B_p Ap=Mc→pACBc=Mp→cBp
下标带 p p p 的表示父坐标空间, c c c 表示子坐标空间,当然并不存在父子关系,Remember,相对!!!
M c → p , M p → c M_{c\to p}, M_{p\to c} Mc→p,Mp→c 表示子空间到父空间,父空间到子空间的变换矩阵,两者互为逆矩阵。
设 C C C 坐标空间下三个坐标轴以及原点在 P P P 空间下的表示为 x c , y c , z c , O c \mathbf{x}_c, \mathbf{y}_c, \mathbf{z}_c, \mathbf{O}_c xc,yc,zc,Oc。
给定 C C C 坐标空间下一点 A c = ( a , b , c ) A_c=(a,b,c) Ac=(a,b,c)。通过直白的方式获得 A p A_p Ap。
A p = O c + a x c + b y c + c z c A_p=\mathbf{O}_c + a\mathbf{x}_c+b\mathbf{y}_c+c\mathbf{z}_c Ap=Oc+axc+byc+czc
转换成矩阵形式:
A p = O c + ( x x c x y c x z c y x c y y c y z c z x c z y c z z c ) ( a b c ) = ( x O c , y O c , z O c ) + ( x x c x y c x z c y x c y y c y z c z x c z y c z z c ) ( a b c ) A_p=\mathbf{O}_c + \begin{pmatrix} x_{\mathbf{x}_c} & x_{\mathbf{y}_c} & x_{\mathbf{z}_c} \\ y_{\mathbf{x}_c} & y_{\mathbf{y}_c} & y_{\mathbf{z}_c} \\ z_{\mathbf{x}_c} & z_{\mathbf{y}_c} & z_{\mathbf{z}_c} \end{pmatrix} \begin{pmatrix} a \\ b \\ c \end{pmatrix} =(x_{\mathbf{O}_c} , y_{\mathbf{O}_c} , z_{\mathbf{O}_c}) + \begin{pmatrix} x_{\mathbf{x}_c} & x_{\mathbf{y}_c} & x_{\mathbf{z}_c} \\ y_{\mathbf{x}_c} & y_{\mathbf{y}_c} & y_{\mathbf{z}_c} \\ z_{\mathbf{x}_c} & z_{\mathbf{y}_c} & z_{\mathbf{z}_c} \end{pmatrix} \begin{pmatrix} a \\ b \\ c \end{pmatrix} Ap=Oc+⎝⎛xxcyxczxcxycyyczycxzcyzczzc⎠⎞⎝⎛abc⎠⎞=(xOc,yOc,zOc)+⎝⎛xxcyxczxcxycyyczycxzcyzczzc⎠⎞⎝⎛abc⎠⎞
使用齐次坐标空间来表示:
A p = ( x O c , y O c , z O c , 1 ) + ( x x c x y c x z c 0 y x c y y c y z c 0 z x c z y c z z c 0 0 0 0 1 ) ( a b c ) = ( ∣ ∣ ∣ ∣ x c y c z c O c ∣ ∣ ∣ ∣ 0 0 0 1 ) ( a b c 1 ) A_p=(x_{\mathbf{O}_c} , y_{\mathbf{O}_c} , z_{\mathbf{O}_c}, 1) + \begin{pmatrix} x_{\mathbf{x}_c} & x_{\mathbf{y}_c} & x_{\mathbf{z}_c} & 0\\ y_{\mathbf{x}_c} & y_{\mathbf{y}_c} & y_{\mathbf{z}_c} & 0\\ z_{\mathbf{x}_c} & z_{\mathbf{y}_c} & z_{\mathbf{z}_c} & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} a \\ b \\ c \end{pmatrix} = \begin{pmatrix} |\ & |\ & |\ & |\\\ \mathbf{x}_c & \mathbf{y}_c & \mathbf{z}_c & \mathbf{O}_c\\ |\ & |\ & |\ & |\ \\ 0 & 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} a \\ b \\ c \\ 1 \end{pmatrix} Ap=(xOc,yOc,zOc,1)+⎝⎛xxcyxczxc0xycyyczyc0xzcyzczzc00001⎠⎞⎝⎛abc⎠⎞=⎝⎛∣ xc∣ 0∣ yc∣ 0∣ zc∣ 0∣Oc∣ 1⎠⎞⎝⎛abc1⎠⎞
这里 M c → p M_{c\to p} Mc→p 就出来了。
M c → p = ( ∣ ∣ ∣ ∣ x c y c z c O c ∣ ∣ ∣ ∣ 0 0 0 1 ) M_{c\to p} = \begin{pmatrix} |\ & |\ & |\ & |\\\ \mathbf{x}_c & \mathbf{y}_c & \mathbf{z}_c & \mathbf{O}_c\\ |\ & |\ & |\ & |\ \\ 0 & 0 & 0 & 1 \end{pmatrix} Mc→p=⎝⎛∣ xc∣ 0∣ yc∣ 0∣ zc∣ 0∣Oc∣ 1⎠⎞
有了 M c → p M_{c\to p} Mc→p , M p → c M_{p\to c} Mp→c 就可以求到了,如果是正交矩阵,直接就是前者的转置矩阵。
Key point!!! 你看这个矩阵的形式,当我们得到这个矩阵的时候,其实可以直接把子坐标空间的原点和坐标轴方向,当然还需要归一化!!!
另一方面,如果只操作矢量,那么不需要平移操作,那么变换矩阵可以进一步简化为:
M c → p = ( ∣ ∣ ∣ x c y c z c ∣ ∣ ∣ ) M_{c\to p} = \begin{pmatrix} |\ & |\ & |\ & \\ \mathbf{x}_c & \mathbf{y}_c & \mathbf{z}_c \\ |\ & |\ & |\ & \end{pmatrix} Mc→p=⎝⎛∣ xc∣ ∣ yc∣ ∣ zc∣ ⎠⎞
到这我们应该可以理解坐标变换其中用数学公式来怎么表达了。接下来需要去了解法线变换的一些特殊性,以及TBN矩阵。
法线变换
对于点和大部分方向矢量,我们可以直接使用同一个 4 × 4 4\times 4 4×4 或者 3 × 3 3\times 3 3×3 的变换矩阵 M A → B M_{A\to B} MA→B ,使其从坐标空间 A A A 转换到坐标空间 B B B ,But! 这对于法线的变换就不适用了。

可以看到仅仅只是放缩了一下,其它顶点信息正确的变换,但是法线明显变换出现了问题,变换后法线不再垂直于平面了。
而切线是通过两个顶点之间的差值计算得到,我们可以直接用变换矩阵来变换。
T B = M A → B T A \mathbf{T}_B=M_{A\to B}\mathbf{T}_A TB=MA→BTA
这里的 T \mathbf{T} T 表示切线。
那么问题来了???如何得到变换后的法线向量呢???
已知条件: T A ⋅ N A = 0 \mathbf{T}_A \cdot \mathbf{N}_A=0 TA⋅NA=0
给定条件: 变换矩阵 M A → B M_{A\to B} MA→B
设置:法线变换矩阵 G G G
首先:
T B ⋅ N B = ( M A → B T A ) ⋅ ( G N A ) = ( M A → B T A ) ⋅ ( G N A ) = ( M A → B T A ) ⊤ ( G N A ) = T A ⊤ M A → B ⊤ G N A = T A ⊤ ( M A → B ⊤ G ) N A = 0 \begin{aligned} \mathbf{T}_B \cdot \mathbf{N}_B &= (M_{A\to B}\mathbf{T}_A )\cdot(G\mathbf{N}_A ) \\ & = (M_{A\to B}\mathbf{T}_A )\cdot(G\mathbf{N}_A ) \\ & = (M_{A\to B}\mathbf{T}_A)^{\top}(G\mathbf{N}_A) \\ & = \mathbf{T}_A^{\top}M_{A\to B}^{\top}G\mathbf{N}_A \\ & = \mathbf{T}_A^{\top}(M_{A\to B}^{\top}G)\mathbf{N}_A \\ & = 0 \end{aligned} TB⋅NB=(MA→BTA)⋅(GNA)=(MA→BTA)⋅(GNA)=(MA→BTA)⊤(GNA)=TA⊤MA→B⊤GNA=TA⊤(MA→B⊤G)NA=0
这里开始我不是很明白公式的第二个等号后的式子为什么能直接转变成第三个等号后的式子,后来思考了一下,这是两个向量在点乘,所以转置一下其实变成了矩阵相乘,最终会得到 0 0 0 ,这里因为 T A ⋅ N A = 0 \mathbf{T}_A \cdot \mathbf{N}_A = 0 TA⋅NA=0 ,所以只要 ( M A → B ⊤ G ) = I (M_{A\to B}^{\top}G) = \mathbf{I} (MA→B⊤G)=I 就可以了,即相乘为一个单位阵。
那么结果呼之欲出, G = ( M A → B ⊤ ) − 1 G=(M_{A\to B}^{\top})^{-1} G=(MA→B⊤)−1 ,即变换矩阵的逆转置矩阵,如果变换矩阵正交,即只做旋转变换,那么 G = ( M A → B ⊤ ) − 1 = ( M A → B ⊤ ) ⊤ = M A → B G = (M_{A\to B}^{\top})^{-1} = (M_{A\to B}^{\top})^{\top}=M_{A\to B} G=(MA→B⊤)−1=(MA→B⊤)⊤=MA→B 。
如果还包含统一缩放,那么可以使用统一缩放系数 k k k 来变换,即 G = ( M A → B ⊤ ) − 1 = 1 k ( M A → B ⊤ ) ⊤ = 1 k M A → B G = (M_{A\to B}^{\top})^{-1} = \frac{1}{k}(M_{A\to B}^{\top})^{\top}=\frac{1}{k}M_{A\to B} G=(MA→B⊤)−1=k1(MA→B⊤)⊤=k1MA→B 。
TBN矩阵的使用
在Shader中,我们计算光照模型通常还是会在世界空间下计算,所以在片元着色器中会将获得的切线空间坐标下的法线通过TBN矩阵转到世界空间中去。
首先获得模型世界空间坐标下法线的坐标 N \mathbf{N} N ,切线坐标 T \mathbf{T} T,注意! 这里的法线是通过模型的网格顶点自动计算出来的,与我们要使用的法线贴图的法线不是同一个概念,后者是加了一些扰动,使的模型不用改动网格就可以获得更真实的效果。
获得副切线 B = N × T \mathbf{B} = \mathbf{N} \times \mathbf{T} B=N×T ,通过叉乘得到。
那么 T B N = ( ∣ ∣ ∣ T B N ∣ ∣ ∣ ) \mathbf{TBN}=\begin{pmatrix} |\ & |\ & |\ \\ \mathbf{T} & \mathbf{B} & \mathbf{N} \\ |\ & |\ & |\ \end{pmatrix} TBN=⎝⎛∣ T∣ ∣ B∣ ∣ N∣ ⎠⎞
现在我们获得了切线空间下的三个坐标轴在世界空间下的表示,回想之前的坐标变换,我们已经获得了将切线空间坐标下的点变换到世界空间坐标下的矩阵!!!
那么到此为止,对于TBN矩阵的理解就告一段落了。
边栏推荐
- QGIS系列(1)-QGIS(server-apache) win10安装
- 【头歌】重生之我在py入门实训中(9):异常处理
- 力扣题解 动态规划(6)
- 力扣 236. 二叉树的最近公共祖先
- 判断是否为回文结构的三种方法
- Unity 菜单界面的简单介绍
- [first song] rebirth of me in py introductory training (2): formula programming
- arcgis for js api(2) 获取要素服务的id集合
- 【头歌】重生之我在py入门实训中(12):Matplotlib接口和常用图形
- Force buckle 110. Balanced binary tree
猜你喜欢

PS 2022 updated in June, what new functions have been added

C语言扫雷最新 递归展开 超详解(附源码)

ps 2022 六月更新,都新增了哪些功能
哈夫曼树的求法,代码实现及证明,图文解释

STM32-FSMC外扩内存SRAM
Acwing the number of square arrays of one question per day

Lightroom classic 2022 v11.4 Chinese version "latest resources"

Unity 实用小技巧(更新ing)

PZK学C语言之数据类型,进制转换,输入输出,运算符,分支语句ifelse

性感素数(Acwing每日一题)
随机推荐
遥感影像识别-训练策略
C语言-程序的编译
物联网操作系统
编程学习记录——第5课【分支和循环语句】
力扣第一周错题集
C language - linear sequence table
浅记一下十大排序
编程学习记录——第6课【函数】
根据SQL必知必会学习SQL(MYSQL)
【动态规划----钢条切割问题】
力扣每日一题(链表模拟)
PZK学C语言之初识指针
【头歌】重生之数据科学导论——回归进阶
Linked list palindrome judgment
Weidongshan digital photo frame project learning (II) displaying Chinese characters on LCD
2021-06-26
链表回文判断
性感素数(Acwing每日一题)
开源WebGIS-相关知识
一张图看懂指针