函數核心目標
實現屏幕空間內三角形的光柵化,將三角形覆蓋的像素點顏色填充到幀緩沖區,同時處理深度測試(Z-Buffer)。這是渲染管線中幾何階段到像素階段的關鍵步驟
包圍盒計算(Bounding Box)?**
- ?功能:確定三角形在屏幕空間的最小包圍區域,減少無效像素遍歷。
- ?實現邏輯:
- 遍歷三角形的三個頂點,取最小和最大的x、y坐標值,形成矩形包圍盒。
?遍歷包圍盒內像素**
- ?功能:對包圍盒內的每個像素進行“是否在三角形內”的測試。
- ?實現邏輯:
- 對每個像素點
(x, y)
,調用insideTriangle
函數判斷其是否在三角形內部
- 對每個像素點
若在三角形內,則進行深度插值和顏色填充
點是否在三角形內的判斷**
- ?算法:通過叉乘符號一致性測試(Cross Product Sign Test)
(4) 重心坐標與深度插值**
- ?功能:通過重心坐標插值計算當前像素的深度值(Z值),用于深度測試
?深度測試與顏色填充**
- ?深度測試:比較當前像素的插值深度與深度緩沖區的值。若更小(更近),則更新深度緩沖區并填充顏色。
- ?顏色設置:使用三角形顏色插值(作業2)或紋理采樣(作業3)
,調用set_pixel
函數寫入幀緩沖區。
?關鍵函數調用鏈
- ?**
draw()
**:處理模型-視圖-投影變換(MVP),將頂點轉換到屏幕空間。 - ?**
rasterize_triangle()
**:執行光柵化邏輯。 - ?**
set_pixel()
**:將最終顏色寫入幀緩沖區的指定位置。
注意事項
- ?性能優化:包圍盒可結合整數坐標計算,減少浮點運算誤差。
- ?抗鋸齒:作業2為基礎實現,實際渲染中可能需要超采樣(MSAA)處理鋸齒問題。
該函數用于在離散像素網格中繪制一條顏色為白色(RGB 255,255,255)的直線,基于經典的Bresenham直線算法
其核心邏輯是通過整數運算逐像素逼近理想直線,避免浮點運算以提高效率。算法根據斜率大小分為兩種處理模式:
- ?當斜率絕對值≤1?(水平方向變化更大)時,以
x
軸為主步進方向。 - ?當斜率絕對值>1?(垂直方向變化更大)時,以
y
軸為主步進方向
t
?是什么?
-
?**
t
?是三角形對象**:在光柵化器中,t
?是?Triangle
?類的實例,表示一個需要被繪制的三角形。這個類通常包含以下信息:- 三個頂點的位置(
v[0]
,?v[1]
,?v[2]
) - 頂點顏色、紋理坐標等屬性(取決于具體實現)
- 三個頂點的位置(
-
?來源:在?
rasterize_triangle(const Triangle& t)
?函數中,t
?是函數的參數,表示當前正在處理的三角形。
t.v[0]
、t.v[1]
、t.v[2]
?是什么?
-
?頂點數組:
t.v
?是存儲三角形三個頂點的數組,每個頂點是一個 ?三維向量?(例如?Eigen::Vector3f
),包含以下信息:- ?x():頂點的 x 坐標(屏幕空間或投影空間)
- ?y():頂點的 y 坐標
- ?z():頂點的深度值(用于深度緩沖)
-
?**
pos_buffer
(頂點位置緩沖區)?**- 類型:
rst::pos_buf_id
- 作用:存儲三角形頂點的模型空間坐標。通過
pos_buffer.pos_id
從全局位置緩沖區中獲取頂點數據,每個頂點為三維坐標(如Eigen::Vector3f
)。
- 類型:
-
?**
ind_buffer
(索引緩沖區)?**- 類型:
rst::ind_buf_id
- 作用:定義頂點如何組合成三角形。通過
ind_buffer.ind_id
獲取索引數組,每組3個索引對應一個三角形的三個頂點(如i[0]
,?i[1]
,?i[2]
)。
- 類型:
-
?**
type
(圖元類型)?**- 類型:
rst::Primitive
- 作用:指定渲染的圖元類型。當前代碼僅支持
Triangle
類型,其他類型會拋出異常。這是為了適配圖形管線中三角形光柵化的特定需求
- 類型:
auto v = t.toVector4();
?這一操作的作用是將三角形頂點從三維坐標轉換為四維齊次坐標,其核心目的是為了支持透視投影下的正確深度插值和透視校正屬性插值
在圖形管線中,頂點經過MVP矩陣變換后會處于齊次裁剪空間?(Homogeneous Clip Space),此時坐標是四維的(x, y, z, w)。通過調用?t.toVector4()
?可以獲取頂點在齊次空間中的完整信息
深度插值
-
?問題背景
在透視投影中,物體“近大遠小”的特性導致屏幕空間的均勻步長對應視圖空間中的非線性步長。若直接對屏幕空間坐標線性插值,視圖空間的深度值(z
)會失真
傳統的GPU渲染流水線(管線)是基于光柵化的一套流程,之所以要強調傳統,是為了將之區別于基于光線追蹤(ray trace)的流水線和基于體素化的流水線。在光柵管線中,最基本的2個著色器是頂點著色器和像素著色器,在下圖中,除了2個著色器可編程,中間三個時鐘節點都是固定的,只能配置不可編程。
新的形狀保持了一些特性:平行線仍然是平行的,各處密度均勻,原點不變。如果原點位置變化的話那就得加上平移,線性矩陣變成仿射矩陣
那什么是線性插值呢?即均勻地插值,比如線段的中點的插值一定是兩端之和處以2,這個例子是一維的插值,多維也是類似。下圖中列舉了頂點色和頂點法線的線性插值
關于“密度”可以這樣理解:在原始三角形上均勻的撒一些散點,待它被投影到屏幕三角形上之后,這些點是否仍然分布均勻?想象一下,很顯然在正交投影的情況下,是均勻的,但透視投影中,距離相機近的部位散點更稀疏,遠處的散點更密集。
所以我們要找到插值和插值點之間真正的函數關系,所以我引入了下面的視錐側剖圖:其中O點是攝像機,L是近截面,ax+bz=c是三角形。我們抽象一個虛擬的插值點t,范圍是0~1,t從(P1,-e)出發,勻速運動至(p2,-e),t的值也勻速地從0增長至1。圖中可以看出,近截面上的均勻散點反投影到三角形上時變得不均勻了,此外還能得出,插值點的x坐標P與t線性相關。
仿射矩陣是線性變換與平移變換的結合形式,其核心作用是將原本分離的線性操作(如旋轉、縮放)和平移操作統一在一個矩陣框架下處理
FOV(視場角)的定義
FOV(Field of View)?,即視場角,用于描述光學設備(如攝像頭、鏡頭、人眼等)能夠捕捉到的最大可見范圍,通常以角度(°)為單位.其本質是一個幾何概念,類似于人眼的視野范圍,但受設備硬件(如傳感器尺寸、焦距)和光學設計的限制
-
未校正的插值
假設一個長條形紋理貼在一個傾斜的平面上,若直接線性插值,近處的紋理會被壓縮,遠處的會被拉伸,導致紋理扭曲(如棋盤格變成梯形)。 -
?校正后的效果
通過透視校正,紋理在視圖空間中保持均勻分布,屏幕空間中的非線性變化被抵消,紋理顯示正確(如棋盤格保持正方形)
此階段通過透視投影矩陣將頂點從視圖坐標空間?(觀察坐標系)變換到裁剪空間?(Clip Space)。該矩陣的作用包括:
- ?近大遠小:模擬人眼的視覺特征,使遠處物體縮小,近處物體放大。
- ?視錐體壓縮:將視圖空間中的視錐體(由近/遠裁剪平面和視角定義的棱臺)映射到規則觀察體(Canonical View Volume),即邊長為2的立方體(范圍[-1,1])。
- ?深度非線性處理:通過矩陣運算將視圖空間的Z值轉換為裁剪空間的W分量,為后續透視除法做準備
透視除法(齊次除法)?
裁剪空間的頂點坐標需進行齊次除法?(即各分量除以W分量),得到歸一化設備坐標(NDC)?:
- ?公式:
(Xndc, Yndc, Zndc) = (Xclip/Wclip, Yclip/Wclip, Zclip/Wclip)
- ?深度非線性:NDC的Z軸范圍[-1,1],但視圖空間的Z值與NDC的Z值呈非線性關系,
- 模型空間 → 視圖空間:通過視圖矩陣(View Matrix)轉換,處理攝像機位置與朝向。
- ?視圖空間 → 裁剪空間:應用透視投影矩陣,完成視錐體壓縮。
- ?裁剪空間 → NDC空間:齊次除法實現非線性映射。
- ?NDC空間 → 屏幕空間:視口變換適配顯示設備。
三角形有很多特性,非常適合作為渲染的最小單位,如:
-
各頂點/各邊在同一平面上
-
內部的點很好定義
-
三角形內的頂點之間插值容易實現(質心插值)
齊次坐標通過引入第四維 ?w,,w不是一個值,而是一個維度,如x,y,z的表示一樣
支持透視投影與深度感知
在透視投影中,?w 分量存儲深度信息(如相機到物體的距離)?。通過投影矩陣修改 ?w?的值(例如?w_clip = -z_eye
),后續透視除法(x/w, y/w, z/w
)能實現“近大遠小”的視覺效果
例如,遠處的物體因 ?w 值較大,其投影后的坐標會被壓縮,符合人眼透視規律
?
頂點之間的插值指通過三角形三個頂點的已知屬性(如顏色、紋理坐標、法線等),利用數學方法(如重心坐標)計算三角形內部任意點的屬性值的過程
例如,已知頂點顏色為紅、綠、藍,插值后三角形內部會呈現平滑的漸變混合效果
為什么需要插值?
-
?屬性傳遞需求
三角形頂點僅存儲少量屬性(如位置、法線),但渲染時需要為每個內部像素(或片元)賦予屬性值。插值通過頂點數據的加權混合,將離散頂點屬性擴散到整個三角形表面
示例:?若頂點存儲紋理坐標,內部像素的紋理坐標需通過插值計算,才能正確映射貼圖
幾何連續性的保證
三角形是平面多邊形中最簡單的形式,其線性插值特性(如重心坐標的非負性和歸一化)能保證屬性在三角形內部平滑過渡,避免突變
?
+1.0
將NDC坐標范圍從[-1,1]映射到[0,2]0.5*width
將坐標縮放到屏幕實際像素尺寸,例如width=800時,x=1.0會被映射到800像素位置
- 將NDC的z值[-1,1]映射到深度緩沖區范圍[0.1,50]
- 線性變換公式:zbuffer?=(zndc?×24.95)+25.05,確保深度值適配渲染管線的深度測試范圍
head<3>
的含義
- ?語法作用:
head<3>
是Eigen庫的向量操作方法,表示取前3個分量 - ?數值意義:當應用于
Eigen::Vector4f
(四維向量)時,vec.head<3>()
會返回一個三維向量(Eigen::Vector3f
) - ?應用場景:
- 頂點坐標
v[i]
經過MVP變換和視口變換后是四維向量(x, y, z, w) - 但三角形頂點只需要三維坐標(x, y, z),因此需要截取前三個分量
- 頂點坐標
?
投影矩陣的約定
代碼中f1=(50-0.1)/2.0
和f2=(50+0.1)/2.0
的推導源于投影矩陣的參數設置。這里的50和0.1分別對應視錐體的遠(far plane)和近(near plane)平面距離,映射公式z' = z*f1 + f2
實際是線性變換:
zbuffer?=2zndc?+1??(far?near)+near
將NDC的[-1,1]線性映射到[near, far]的實際深度范圍
在齊次除法中,除以vec.w()
是因為經過模型-視圖-投影(MVP)矩陣變換后,頂點的齊次坐標的w
分量可能不再為1,尤其是當使用透視投影時。以下是關鍵點解析:
-
?投影矩陣的作用:
- ?透視投影矩陣會修改頂點的
w
分量,通常將其設置為頂點的原始z
值(或相關值)。例如,OpenGL的透視投影矩陣會將頂點變換到裁剪空間,此時w
分量變為-z
。 - 例如,應用如下投影矩陣后的頂點齊次坐標為:
(a*x, b*y, c*z + d, -z)
,此時w = -z
而非1。
- ?透視投影矩陣會修改頂點的
-
?齊次除法的必要性:
- 齊次除法(
vec /= vec.w()
)將裁剪空間坐標轉換為歸一化設備坐標(NDC)?,范圍為[-1, 1]3
。 - 此步驟通過除以
w
實現透視校正,使遠處的物體看起來更小(透視效果)。
- 齊次除法(
-
?代碼流程分析:
- ?**
mvp * to_vec4(buf[i[...]], 1.0f)
**:頂點初始化為(x, y, z, 1)
,但經過投影矩陣后w
被改變。 - ?除以
vec.w()
:確保正確投影到NDC,無論w
是否被修改(如透視投影時)。
- ?**
-
?正交投影的特殊情況:
- 若使用正交投影,投影矩陣通常保持
w = 1
,此時齊次除法無實際影響,但仍需統一處理。
- 若使用正交投影,投影矩陣通常保持
總結:即使頂點初始w
為1,投影矩陣會修改w
,因此必須進行齊次除法才能正確投影到屏幕空間。這是實現透視效果的關鍵步驟。