新聞中心

NVIDIA OptiX 著色器綁定表優化實現高效光線追蹤 NEWS DETAIL

當前位置:首頁 > 新聞中心
資訊分類 · 新聞中心 發佈時間 · 2025-01-09

NVIDIA OptiX 是通過 CUDA 實現 GPU 加速光線追蹤的 API,通常用於渲染包含各種物體和材質的場景。在 OptiX 啓動期間,當光線與幾何基元相交時,系統會執行命中著色器。著色器綁定表 (Shader Binding Table, SBT) 回答了為給定的交集執行哪個著色器的問題。SBT 還可用於將輸入資料映射到著色運算。

本文介紹了在應用中佈局 Shader Binding Table(SBT)的幾種不同方法,以及著色器訪問其資料的不同方法。通過盡可能減少 SBT 和著色資料,您可以節省內存、提高性能並簡化 SBT 本身的管理。

著色器綁定表設計模式

光線追蹤應用通常會為每個網格對象儲存兩種主要類型的資料:幾何資訊 (例如著色法線) 和材質參數 (例如漫反射或粗糙度參數)。材質著色器會訪問這些資料,以執行計算,例如當前交叉點的光照。

以下各節將介紹使用 SBT 和實現資料查找的兩種方法。第一種方法可直接利用 SBT 來儲存著色器程序和查找資料。它易於實施,但顯存使用效率低下。第二種方法對這種基本方法進行迭代,以實現更高效的佈局,減少 SBT 記錄和著色資料中的冗余,從而提高效率。

這兩種設計針對的是場景層次結構的常見情況,其中許多實例共享一組材質。出於說明目的,我們假設所有幾何圖形都使用 OptiX 內置三角形相交,並且應用程序使用單一光線類型。這些約束易於放鬆,我們將在下文中詳細討論。

樸素方法:每個實例的 Geometry 和 material properties

將著色器和資料映射到實例的最簡單方法是在每個實例 (每種光線類型) 的單獨 SBT 記錄中明確儲存對所有著色器和資料的引用。著色器本身在 SBT 記錄的標題部分中引用,而對幾何資料和著色參數的引用儲存在用戶定義的資料塊中。可以直接儲存小參數。

例如,路徑追蹤渲染器支援簡單的漫反射和光澤材質,並通過每個頂點法線執行平滑著色。請注意,諸如頂點屬性之類的重量級資料儲存在單獨的全局內存分配中,並且只有對資料的引用直接儲存在資料塊中。圖 1 顯示了每個組件的關聯資料。

Diagram showing three fundamental data blocks. From left to right: Per-Vertex, Per-Glossy Material, and Per-Diffuse Material.

圖 1、示例設置中的基本資料塊


以下結構將在 SBT 記錄中容納此內容:

struct ShadingParams {
    Float3* normals
    Float3 reflectance
    Float roughness
}

請注意,反射率參數可以在漫反射和光澤材質之間共享,但即使不需要,也必須儲存粗糙度參數。這是這種方法的缺點。data 部分的大小必須至少等同於所有材質中設置的最大參數的佔用空間。

我們來看看應用於假設場景的佈局:

  • 100000 個實例

  • 50000 個唯一三角形網格 (GAS)

  • 兩個材質著色器 (如前所述,光澤著色器和漫反射著色器)

  • 10000 個唯一材質參數集

由於某些對象可能共享相同的材質描述,因此材質的數量通常小於實例的數量。

Diagram showing simple data layout with inlined SBT data segments.


圖 2、在 SBT 資料片段中內聯資料的簡單佈局


此方案所需的儲存空間為實例數量乘以 SBT 記錄的大小。在本例中,SBT 資料部分大小為 24 字節;但是,16 字節對齊會導致四捨五入高達 32 億。根據 API 的要求,報文頭部分始終為 32 字節。這樣一來,著色資料總開銷約為 6 MB,相當於 SBT 命中組列表的總大小。

這種方法佔用大量內存,會多次儲存相同的 SBT 標頭和資料部分。這種內存腫現象不僅佔用寶貴的 GPU 儲存空間,而且可能會由於內存訪問不一致而導致 GPU 端性能下降,並導致填充和維護超大 SBT 陣列的主機端用度。但是,對於簡單的應用和場景設置,這種技術仍然是一個合理的選擇。

另外,請注意,通過將所有著色和幾何資料放入全局分配中,並只需在記錄中儲存資料指針,資料部分始終可以保持 16 字節的最小非零大小。但是,這會產生額外指針和額外內存間接的儲存成本。

優化方法:通過遠離每個實例儲存來減少冗余

在本節中,我們將通過優化 SBT 和資料佈局來緩解這些問題。減少 SBT 和所有著色資料的內存佔用的關鍵是利用冗余。

首先,完全刪除 SBT 記錄的 data 部分。而是將著色資料儲存在全局內存中,並使用 OptiX 層次結構的相關知識進行訪問。在每個 GAS 的單個 SBT 記錄的情況下,這種映射是微不足道的。著色參數移動到全局內存中的數組中,場景中存在的材質參數和幾何資料的每個獨特組合都對應一個條目。

此數組中的 ShadingParams 條目數量每個實例最多一個,但實際上可能要小得多,因為給定網格的多個實例通常具有相同的材質參數。然後,場景中的每個實例都將其實例 ID 設置為著色參數的數組索引。

圖 3 顯示了資料的當前外觀。所有著色資料均移動到一個單獨的全局數組中。陣列的大小由幾何圖形和材質參數的獨特組合數量決定。

Diagram of global memory array with ShadingParams elements.

圖 3、用於著色參數的全局數組,與 SBT 分離


您可以在設備代碼中訪問當前的著色資料:

ShadingParams& params = shading_data_array[optixGetInstanceId()]

請注意,實例 ID 不需要是唯一的。如果多個實例共享相同的著色和幾何資料,它們可以共享用於索引到參數列表的相同實例 ID。

接下來,解決幾何參數的冗余儲存問題。當給定的 GAS 被實例化多次時,其著色法線數組的引用會被儲存多次——樸素佈局中的每個實例和當前方法中設置的唯一幾何/著色參數分別儲存一次。理想情況下,每個 GAS 只應儲存一次法線,因為它們不會因實例而異。

可以使用 OptiX 8.1 中引入的新 optixGetGASPointerFromHandle 函數 輕鬆 實現對每個 GAS 資料的儲存。此函數檢索與給定 GAS 關聯的二進制加速結構資料的地址。因此,在 GAS 構建時分配內存時,應用程序可以在此加速結構資料前加上任意用戶資料塊。設備函數可以通過調用 optixGetGASPointerFromHandle 並減去用戶資料段的大小來檢索用戶資料。

現在,資料的組織方式如下:

MaterialParams {
    float3 reflectance
    float roughness
}
GeometryParams {
    float3* normals
}

圖 4 顯示了此新方案的內存佈局。現在,幾何著色參數與其各自的幾何網格相關聯。全局著色資料陣列的大小現在僅取決於唯一材質參數的數量。

Diagram of geometry shading parameters associated with respective geometry meshes.

圖 4、現在,幾何著色參數與其各自的幾何網格相關聯


這不僅可以消除每個唯一 GeometryParams 的多個儲存,還可以減少材質參數中的冗余,因為參數的唯一性不再需要一組唯一的材質和幾何參數。

最終優化消除了 SBT 標頭中材質程序的冗余儲存。只有兩組獨特的材質程序,一組用於光澤,另一組用於漫反射,但這些程序會多次儲存,每個實例一次。我們無需為每個實例儲存單個 SBT 條目,而是可以為每種材質類型儲存單個 SBT 記錄 – 在本例中,為兩種。現在,每個實例的 SBT 偏移量都設置為 0 或 1,具體取決於其是否為漫反射或光澤。

圖 5 顯示最終資料佈局。請注意,幾何圖形和材質參數佈局與圖 4 相同。

Reduced SBT showing Header (32 bytes) and Opaque Shader Handle.

圖 5、SBT 僅取決於場景中唯一著色器的數量

著色資料的最終資料用法如下:

num-GASs*sizeof(GeometryParams) + num-unique-material-instances*sizeof(MaterialParams) + num material-shaders*OPTIX_SBT_RECORD_HEADER_SIZE

這遠低於 1 兆字節。請注意,儲存大小與場景中實例的數量完全無關。對於包含大量實例和著色器且每個都有數十或數百個參數的複雜真實場景而言,這可能是一場巨大的勝利。

擴展替代著色設置 

OptiX 內聯函數提供對場景狀態的訪問,與受限示例相比,可實現更複雜的資料查找。這些狀態查詢函數包括:

  • optixGetSBTDataPointer:當前基元的 SBT 記錄資料塊的地址。這就是使用[Equation 1]通過不透明記錄報文頭的大小偏移量計算出的地址。

  • optixGetInstanceId:應用在創建當前實例 (OptixInstance::instanceId) 時分配的 ID。此值不需要跨實例唯一,可以在[0 – 2^28) 範圍內任意選擇。

  • optixGetInstanceIndex: 返回實例加速結構中基於零的索引。

  • optixGetPrimitiveIndex:當前相交三角形、球體、曲線或自定義幾何圖形的基元索引 。有關詳情,請參閱 OptiX 編程指南 

  • optixGetSbtGASIndex: 當前 GAS 中的 SBT 偏移量 (由構建輸入的 sbtIndexOffsetBuffer 指定)。

  • optixGetGASPointerFromHandle:此選項用於檢索設備內存中幾何加速結構開頭的地址 。然後,應用程序可以在 GAS 內存之前按幾何圖形分配資料塊。

著色設置示例 

本節提供了一些著色設置示例,以及處理這些設置的方式。

多種幾何類型 

改進後的佈局可以輕鬆適應多種不同類型的幾何圖形,但 SBT 條目的數量現在取決於幾何圖形類型和材質著色器的組合數量,因為命中組通過相交程序封裝幾何圖形,並通過命中程序封裝材質。對於三種幾何類型和兩種材質,最多可以有六個 SBT 命中組條目。

多種光線類型 

光線類型的數量也是 SBT 命中組條目數量的乘數。即使是真實示例,光線類型、幾何類型和材質類型的乘積通常也在 10 到 100 之間。

單個氣體內含多種材質 

有多種方法可以處理此情況,但一種簡單的方法是創建輔助查找表,以重定向到著色器參數索引。此表可以通過實例 ID 和 SBT GAS 索引進行索引。

總結 

NVIDIA OptiX 庫提供了將著色器和資料綁定到光線追蹤應用的靈活機制。使用著色器綁定表在實例級別儲存和檢索資料是一種簡單的方法,適用於簡單的應用。但是,避免冗余綁定和資料儲存的更優化的方法可以提高性能並避免內存浮腫。