前言
在前面的文章中我們經常提到知道某個三角形三個頂點的屬性,然後就可以求出三角形内部某一點對應的屬性。例如深度緩存的時候,高洛德着色的時候等等,但是我們一直沒有說具體應該如何計算,本文就來介紹一下這一部分内容。
想要計算三角形内部某一點對應的屬性,也就是我們一直說的三角形的插值,就需要用到重心坐标的概念。
直線的重心坐标
在講三角形的重心坐标前,我們先來看一看直線上的重心坐标是怎麼定義的。
求直線上任意一點
設我們有兩個點 A
和 B
,它們可以連成一條直線。那麼該直線上的任意一點 P
必然滿足:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
k為一個常數,其值也很好求,取任意軸三個點值進行計算即可:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
然後我們可得:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
因為
即
。
而
通過向量的減法,我們可以了解為
,同樣的
,那麼就可得到
,然後可以得到
,最終簡化可得:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
而
,
,
分别代表的就是 P,A,B三個點的坐标,是以可得
P = (1 - k)A + kB
因為 P = ((1-k)+k)P 是以上面式子可以變為 (1-k)A+kB-((1-k)+k)P=0,化簡為 (1-k)(A-P)+k(B-P),即:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
幾何意義
前面我們得到
,即AP的長度
為 AB的長度
的k倍。而 BP的長度
又等于
,是以
的長度為
的 1-k 倍。是以可得:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
因為長度是沒有正負的,然而實際上k可能為負數,就會導緻上面的公式不對。我們先來看看下面三種情況:
0 <= k <= 1
此時P在AB之間,如下圖
可得
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
k < 0
k < 0 ,也就是說
方向和
相反,此時P在AB之外,離A更近一些,如下圖
可得
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
k > 0
和前者相反,如下圖
可得
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
總結
可以發現我們隻要取絕對值,就可以滿足上面的各種情況了,是以
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
直線上的重心坐标
通過上面的推導,我們就知道直線AB上的任意一點P,都可以由一個k來計算出來的。當然了,也可以通過P來推出k的值,怎麼求上面已經說明過了。
若我們設 j = 1 - k,那麼
P = jA + kB
j + k = 1
這樣的話我們P就可以使用 (j, k) 的方式來表示,這種表示方式就是我們的重心坐标。同時需要注意,因為重心坐标是根據某一條直線的AB兩點所定義的,是以不同的直線各自會有各自的重心坐标。
為什麼叫重心坐标
在生活中想必大家都看過或挑過擔子吧,人們在擔子兩邊挂上重物,然後用肩膀扛起擔子。如果擔子兩邊的物體差不多重,我們要保持擔子的平衡隻需要把肩膀撐在擔子的中間即可。但如果擔子有一頭特别的重,我們需要把肩膀盡可能的往重的那頭靠近,才能保持住擔子的平衡。
我們假設有線段AB(也就是擔子),在A下面挂了品質為
的物體,在B下面挂了品質為
的物體,如下圖:
若
,那麼重心P(肩膀的支撐點)應該在哪?自然線段的中心點了,如下圖:
若
呢 ,那麼P點的位置又應該在哪呢?通過常識我們應該知道,此時P點位置離A點更近一些,如下圖:
那麼如果
> 0,
= 0 呢?那不就變得擔子一邊沒有重物,我們隻需要扛有重物的那一邊了,即P在A點上,如下圖:
既然可以等于0了,那麼能不能等于負數呢?即
> 0,
< 0,P會在哪呢?之前說
和
代表的品質,那麼我們怎麼了解負數的品質呢,我們知道挑擔子的時候品質會造成向下的重力,那麼負數的品質我們可以了解成在B點有一個向上的力,等于有人在擔子的一邊幫你搭把手,那就變成不是挑擔子了,而是兩個人擡東西了。兩個人要用棍子擡一個東西,那麼東西自然是在兩個人中間了,是以P會在A的左邊,如下圖:
或
某個值小于0的情況,也就把我們的P的位置從線段AB拓展到直線AB上了。當然了,我們不能
< 0,
< 0,這樣擔子就飛起來了。
上面的例子說明了隻要确定了
和
的值,也就确定了 重心P 的位置,怎麼樣是不是和前面所說的很像?事實上,隻要保證
, 我們的
就是前面所說的 j 的值,而
就是 k 的值,是以稱這樣的坐标為重心坐标。
三角形上的重心坐标
定義
與直線上任意一點滿足 P = jA + kB 一樣,在三角形ABC所在平面上的任意一點P同樣滿足
P = iA + jB + kC
i + j + k = 1
那麼P用 (i, j, k) 的方式表示,就是在三角形上的重心坐标。同樣的,因為重心坐标是由三角形的三個頂點所定義的,是以不同的三角形有各自的重心坐标。
同樣的,若點P要在三角形内部或邊上,需要滿足 i >= 0,j >= 0,k >= 0,否則點P在三角形所在平面外。
同樣由于 P = (i+j+k)P = iP+jP+kP,是以我們可得到 iA+jB+kC-iP-jP-kP = 0 即:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
和直線比喻成擔子類似,對于三角形也是一樣的,我們可以假設有個三角形,它本身是沒有品質的,但是我們在它的三個頂點位置分别懸挂了不同品質的物體,如果我們能找到其中一個點,能夠使三角形保持平衡,那麼這個點就是三角形的重心,如下圖。
三角形和直線的關系
我們将三角形ABC的某個頂點(例如C)和三角形内任意一點P連線,并衍生到三角形的某條邊上,設交點為D,如下圖:
那麼D點在AB上的位置,我們不就可以用直線的重心坐标表示麼,我們設:D = xA + yB,其中 x + y = 1
知道D點坐标後,那麼P點在CD的坐标我們又可以用直線的重心坐标表示,我們設:P = wC + zD,其中 w + z = 1
把D帶入得:P = wC + z(xA + yB) = zxA + zyB + wC,而 zx + zy + w = z(x + y) + w = z + w = 1
那麼設 i = zx,j = zy,k = w,不就證明了 P = iA + jB + kC 成立。
解i,j,k的值
接下來,我們來看看i,j,k三個值怎麼計算,因為 i+j+k=1,是以k=1-i-j,也就是隻要求兩個未知數i和j即可。那麼我們隻需要找到兩個方程組,解二進制一次方程式即可。
因為 P = iA + jB + kC 是以
,從中我們就可以取得兩個方程式:
注:取x,y的話,也友善在二維空間中了解,當然也可以去x,z或y,z去計算。
解得
去 i ,得
此時方程式中隻有一個 j 是變量,我們繼續解,得
解得
解得
去分母,解得
即可求得 j 的值:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
同理可解的 i 的值:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
至于k的值,1-i-j 即可。
幾何意義
我們将點P和三個頂點分别連線,可以得到三個新的三角形,如下圖:
我們設三角形的總面積為 s,三角形PBC的面積為 a,三角形PAB的面積為 c,三角形PCA的面積為 b,那麼可得:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景 ![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景 ![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
也就是說重心坐标和每個頂點所相對的三角形(例如A對應的是PBC)的面積比有關。
我們來簡單的推導一下:
前面我們知道
而
的值,不就是
的x值麼,我們标記為
,其他項也同理,那麼我們可以得到
不知道大家對上面的這種
式子熟不熟悉,它正是二維向量叉乘的模(不熟悉的可以看下叉乘相關知識)。是以我們可以得到
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
設夾角CBP為 α ,那麼分子
,同理分母就是三角形ABC的面積,是以
成立,其他也同理。
三角形的重心
我們知道三角形的重心點為三條中線的交點,如下圖:
中線即是将三角形分成面積相等的兩部分,例如
。
我們來看下O點的重心坐标是多少,根據前面,我們知道我們可以通過三個三角形 AOB,AOC,BOC的面積比來求得重心坐标。那麼我們就來看看這三個三角形的面積比。
首先因為
而他們的高相等,是以 BD = DC,可得
,那麼
。同理我們可得
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
是以三角形的重心坐标即為
。同樣的,關于重心O點的坐标我們可以通過下面公式計算:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
實際應用場景
前面哔哔賴賴了一大堆,我們知道可以通過重心坐标來計算三角形内某點的坐标,即
P = iA + jB + kC
ABCP代表的都是位置資訊,我們可以通過位置資訊求出重心坐标。而重心坐标牛逼就牛逼在,我們可以把ABCD的資訊用别的任何資訊來代替,例如顔色,法線,uv,深度等,然後套用上面的公式即可求出P點對應的屬性。
例如我們A點紅色(1,0,0),B點綠色(0,1,0),C點藍色(0,0,1),那麼三角形内任何點的顔色就等于它的重心坐标,例如重心點顔色為
。
我們可以簡單的用Unity寫個demo驗證一下
首先用下面腳本繪制一個三角形,三角形内部的顔色Unity已經為我們插值好了
[ExecuteInEditMode]
public class Triangle : Graphic
{
Vector2 positionA = new Vector2(70, 40);
Vector2 positionB = new Vector2(100, 100);
Vector2 positionC = new Vector2(40, 70);
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
vh.AddVert(positionA, Color.red, Vector2.zero);
vh.AddVert(positionB, Color.green, Vector2.zero);
vh.AddVert(positionC, Color.blue, Vector2.zero);
vh.AddTriangle(0, 1, 2);
}
}
效果如下(注意color space要使用gamma的):
然後怎麼驗證呢,我們可以創個小Image,然後通過它的坐标和三個頂點的坐标,我們就可以計算出小Image所在點對應的重心坐标,知道重心坐标和三個頂點顔色後,就可以計算出對應顔色,指派給小Image,然後對比下顔色即可。代碼如下:
using UnityEngine;
using UnityEngine.UI;
public class NewBehaviourScript : MonoBehaviour
{
public Image self;
Vector2 a = new Vector2(70, 40);//red
Vector2 b = new Vector2(100, 100);//green
Vector2 c = new Vector2(40, 70);//blue
void Update()
{
Vector2 p = transform.position;
float i = (-(p.x - b.x) * (c.y - b.y) + (p.y - b.y)*(c.x - b.x)) /
(-(a.x - b.x)*(c.y - b.y) + (a.y - b.y)*(c.x - b.x));
float j = (-(p.x - c.x) * (a.y - c.y) + (p.y - c.y)*(a.x - c.x)) /
(-(b.x - c.x)*(a.y - c.y) + (b.y - c.y)*(a.x - c.x));
float k = 1 - i - j;
Debug.Log($"({i}, {j}, {k})");
self.color = new Color(i, j, k);
}
}
效果如下:
可以看出在三角形内部時,我們計算得到的顔色和Unity做好的插值是一樣的。
至于除了顔色外的其他屬性插值,原理也都是一樣的,隻要了解重心坐标了即可。也就是說我們隻需要先通過四個點的位置資訊算出重心坐标,然後就可以通過重心坐标來計算其他屬性的插值。
重心坐标與投影
前面一套套下來,我們可能會有個疑惑,重心坐标的計算和z軸沒有關系麼?
注:其實更準确的說法是隻和x,y,z中其中任意兩項有關,具體可以看求i,j,k時,我們取的二進制一次方程式是哪兩個軸,當然通常情況下,就是取的x和y。
不考慮z,等于把空間中的三角形去掉z,即投影到平面xy上,即原本的A點
,B點
,C點
變成了A'點
,B'點
,C'點
。原本空間中三角形内的P點
,同樣投影變成了 P' 點
。那麼投影前後,P和P' 的重心坐标一樣麼?答案是一樣的!
公式沒考慮z其實就已經告訴我們答案了,那麼幾何上,我們怎麼了解呢,我們可以看個最簡單的例子,就是重心。
我們假設下圖中的三角形是空間中的三角形,也就是ABC的z軸值不同,重心點O的重心坐标自然是
那麼我們看看投影後還是不是
就可以了。
很簡單,我們先來看邊BC的投影,如下圖,我們在yz平面看邊BC的投影
可以發現投影後 D' 依舊是 B' 和 C' 的中心點(相似三角形原理),也就是說投影後的直線 A'D' 是三角形 A'B'C'的中線。其他中線同理,是以投影後 O' 的還是三角形A'B'C'的重心,其重心坐标還是
。
但是!有一種投影不行,就是透視投影,依舊是上面的三角形的邊BC,我們來看看透視投影會發生什麼,如下圖:
很明顯我們就可以看出來,此時 D' 不再是 B' 和 C' 的中心點。當然了,數學不能光用眼睛看,我們需要推導
為了友善計算,我們設O點為原點,O到投影螢幕的距離為 l (如上圖所示),根據相似三角形可以得到:
,即:
。同理可得
,
由于D是BC的中心點,根據直線的重心坐标我們可以知道
,
,帶入可得:
,而投影後B'C'的中點的y值應該是
,可以發現和
并不相等。但是有個前提,那就是
,否則
,上面的式子依舊相等。用圖來看的話更直覺,如下圖(依舊是相似三角形):
emmm,好像直接看點D的重心坐标是否改變比看重心的更友善。
而且三角形重心坐标的這個變化同樣适用于直線的重心坐标,事實上我們的舉例就等于在看直線的重心坐标變化。
是以可以得出結論,在空間中的三角形或直線内的某個點,在投影變換前後,其的重心坐标可能會發生改變。是以有些計算,例如深度,一定要在投影變換前做,否則得到的結果可能是不對的。
矯正
但是前面那樣太麻煩了,我們可不可以直接在知道變換前的重心坐标推出變換後的重心坐标,或者反過來呢?當然可以。
我們先從直線的重心坐标投影矯正開始,直接使用之前的圖,如下:
此時D不再是BC的中心點了,而是當做BC中的任意一點。根據直線的重心坐标我們可以設: D = iB+(1-i)C,D的重心坐标即為(i, 1-i)。直線BC通過透視投影後得到直線B'C',點D變為D',我們知道透視投影後,重心坐标的值會變,是以我們設:D' = jB'+(1-j)C',D'的重心坐标即為(j, 1-j)。
那麼我們隻需要知道 i 和 j 的相對關系,不就可以在隻知道 i 或 j 中一個的情況下推出另個的值了麼?例如,我們假設 i = 2j,那麼投影前的重心坐标 (0.6, 0.4)在投影後自然變成的了(0.3, 0.7),或者說投影後的重心坐标為(0.1, 0.9),那麼投影前就是(0.2, 0.8)。這樣即使碰見投影變換,我們也可以通過變換後的重心坐标去推導出原本的重心坐标,不用再通過逆變換去求原本的重心坐标了。
當然前面 i = 2j 是我們瞎雞兒說的,我們來看看真正的值是多少。
根據直線的重心坐标,我們可以很容易求得 j 的值:
(其實 i 的值同樣可以直接算出來,然後和 j 一除就知道了,但是我們這邊要推導出一個更簡單的公式)。
通過相似三角形可以得到:
,
,
,帶入 j 中可得:
。
我們先來看分子項,即
,我們知道
,
,帶入分子中,得到:
化簡可得:
其中的
不就是分母中的那部分嘛,即可抵消掉,j 就可以簡化為:
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
從這個式子也可以看出,z值相同時,則 i = j,重心坐标不變。也說明了之前一大串的
的值其實就是 i 。
做了個簡單的驗證,如下圖,D點的重心坐标确實正好從 (1/3, 2/3) 變成了 (1/6, 5/6)。
接下來我們來講講三角形的重心坐标投影矯正
前面我們得到
,那麼三角形内任意一點P,我們可以看作是AD上的任意一點,我們設 P = wA+(1-w)D,投影後w會變為z,套用上面的公式,我們可以得到
![]()
圖形學基礎知識:重心坐标(Barycentric Coordinates)前言直線的重心坐标三角形上的重心坐标實際應用場景
因為P = wA+(1-w)D,D = iB+(1-i)C,是以 P = wA+(1-w)(iB+(1-i)C),即:
P = wA+(i-iw)B+(1-i-w+iw)C
也就是P的重心坐标為(w, i-iw, 1-i-w+iw) 後面兩項看着很複雜,我們先不管,寫成 (w, ?, ?),那麼我們就知道投影後重心坐标會變為
。
那後面兩個怎麼算呢?既然我們可以把D看成是BC上的一點,P是AD上的點,那麼是不是可以再把D看成AB或AC上一點,P則是CD或BD上一點來推導上面的公式。
這樣我們又會得到
和
是以得出結論,假設在透視投影前,P在三角形ABC的重心坐标為(i, j, k),那麼投影後該坐标會變為
,而
。