• <xmp id="om0om">
  • <table id="om0om"><noscript id="om0om"></noscript></table>
  • 內容創建/渲染

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

    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 庫提供了將著色器和數據綁定到光線追蹤應用的靈活機制。使用著色器綁定表在實例級別存儲和檢索數據是一種簡單的方法,適用于簡單的應用。但是,避免冗余綁定和數據存儲的更優化的方法可以提高性能并避免內存浮腫。

    首先, 下載 NVIDIA OptiX SDK 并查看 文檔 以了解更多詳情。

    ?

    0

    標簽

    人人超碰97caoporen国产