簡體   English   中英

將值列表傳遞給片段着色器

[英]Passing a list of values to fragment shader

我想將值列表發送到片段着色器中。 它可能是一個很大的列表(長數千個項目)單精度浮點數。 片段着色器需要對該列表進行隨機訪問,我想刷新每幀上CPU的值。

我正在考慮如何做到這一點的選擇:

  1. 作為數組類型的統一變量(“ uniform float x [10];”)。 但是這里似乎有限制,在我的GPU上發送數百個值非常慢,而且當我想在運行時更改它時,我必須在着色器中硬編碼上限。

  2. 作為高度為1且寬度為列表的紋理,然后使用glCopyTexSubImage2D刷新數據。

  3. 其他方法? 我最近還沒有跟上GL規范的所有變化,也許還有其他專門為此目的設計的方法?

當前有4種方法可以執行此操作:標准1D紋理,緩沖區紋理,統一緩沖區和着色器存儲緩沖區。

一維紋理

使用此方法,可以使用glTex(Sub)Image1D用數據填充一維紋理。 由於您的數據只是浮點數的數組,因此您的圖像格式應為GL_R32F 然后,您可以通過簡單的texelFetch調用在着色器中對其進行訪問。 texelFetch獲取texel坐標(因此命名),並關閉所有過濾。 因此,您得到的正是一個紋理像素。

注意: texelFetch是3.0+。 如果要使用以前的GL版本,則需要將尺寸傳遞給着色器並手動標准化紋理坐標。

這里的主要優點是兼容性和緊湊性。 這將適用於GL 2.1硬件(使用表示法)。 而且你不必使用GL_R32F格式; 您可以使用GL_R16F半浮標。 GL_R8如果您的數據對於標准化字節而言是合理的)。 尺寸對於整體性能可能意義重大。

主要缺點是尺寸限制。 您只能使用最大紋理大小的一維紋理。 在GL 3.x級硬件上,此值約為8,192,但保證不低於4,096。

統一緩沖區對象

這種工作方式是在着色器中聲明一個統一塊:

layout(std140) uniform MyBlock
{
  float myDataArray[size];
};

然后,您可以像在數組中一樣在着色器中訪問該數據。

返回C / C ++ / etc代碼,您將創建一個緩沖區對象,並用浮點數據填充它。 然后,您可以將該緩沖區對象與MyBlock統一塊關聯。 更多詳細信息可以在這里找到。

該技術的主要優點是速度和語義。 速度歸因於實現與紋理相比如何處理統一緩沖區。 紋理提取是全局內存訪問。 統一緩沖區訪問通常不是; 當着色器在渲染時初始化時,通常將統一緩沖區數據加載到着色器中。 從那里開始,它是本地訪問,這要快得多。

從語義上講,這更好,因為它不僅僅是平面數組。 對於您的特定需求,如果只需要一個float[] ,那就沒關系。 但是,如果您具有更復雜的數據結構,則語義可能很重要。 例如,考慮一個燈光陣列。 燈光具有位置和顏色。 如果使用紋理,則用於獲取特定光源的位置和顏色的代碼如下所示:

vec4 position = texelFetch(myDataArray, 2*index);
vec4 color = texelFetch(myDataArray, 2*index + 1);

使用統一緩沖區,它看起來就像其他統一訪問一樣。 您已經命名了可以稱為positioncolor 這樣所有的語義信息就在那里。 更容易了解正在發生的事情。

也有大小限制。 OpenGL要求實現為統一塊的最大大小提供至少16384個字節。 這意味着,對於浮點數組,您只能得到4,096個元素。 再次注意,這是實現所需的最低要求。 一些硬件可以提供更大的緩沖區。 例如,AMD在其DX10級硬件上提供65,536。

緩沖區紋理

這些是一種“超級1D紋理”。 它們有效地使您可以從紋理單元訪問緩沖區對象 盡管它們是一維的,但它們不是一維紋理。

您只能在GL 3.0或更高版本中使用它們。 而且,您只能通過texelFetch函數訪問它們。

這里的主要優點是尺寸。 緩沖區紋理通常可以非常龐大。 雖然規范通常是保守的,要求至少65,536個字節用於緩沖區紋理,但是大多數GL實現方案都允許它們的大小范圍為字節。 實際上,通常最大大小通常受可用GPU內存的限制,而不是硬件限制。

同樣,緩沖區紋理存儲在緩沖區對象中,而不是更不透明的紋理對象(如1D紋理)中存儲。 這意味着您可以使用一些緩沖區對象流技術來更新它們。

就像一維紋理一樣,這里的主要缺點是性能。 緩沖區紋理可能不會比一維紋理慢,但它們也不會像UBO一樣快。 如果您只是從它們中拉出一個浮標,則不必擔心。 但是,如果要從中提取大量數據,請考慮使用UBO。

着色器存儲緩沖區對象

OpenGL 4.3提供了另一種處理方式: 着色器存儲緩沖區 它們很像統一緩沖區。 您可以使用幾乎與統一塊相同的語法來指定它們。 原則上的區別在於您可以寫信給他們。 顯然,這對您的需求沒有用,但是還有其他差異。

從概念上講,着色器存儲緩沖區是緩沖區紋理的另一種形式。 因此,着色器存儲緩沖區的大小限制比為均勻的緩沖器大很多 最大UBO大小的OpenGL最小值為16KB。 最大SSBO大小的OpenGL最小值為16MB 因此,如果您擁有硬件,它們是UBO的有趣替代品。

只需確保將它們聲明為readonly ,因為您沒有寫信給他們。

相對於UBO,此處的潛在缺點再次是性能。 SSBO通過緩沖區紋理像圖像加載/存儲操作一樣工作。 基本上,它是imageBuffer圖像類型周圍的(非常好)語法糖。 這樣,從這些讀取可能會以從readonly imageBuffer讀取的速度執行讀取。

目前尚不清楚通過圖像加載/通過緩沖圖像存儲讀取是否比緩沖紋理更快或更慢。

另一個潛在的問題是,您必須遵守非同步內存訪問的規則。 這些很復雜,很容易使您絆倒。

這聽起來像是紋理緩沖區對象的一個很好的用例。 這些與常規紋理沒有太大關系,基本上可以讓您以簡單的線性數組的形式在着色器中訪問緩沖區對象的內存。 它們類似於1D紋理,但不進行過濾,只能由整數索引訪問,這聽起來像您將其稱為值列表時需要執行的操作。 而且它們還支持比1D紋理更大的尺寸。 為了進行更新,您可以使用標准的緩沖區對象方法( glBufferDataglMapBuffer ,...)。

但另一方面,我認為它們需要使用GL3 / DX10硬件,甚至已經成為OpenGL 3.1的核心。 如果您的硬件/驅動程序不支持它,那么您的第二種解決方案將是選擇的方法,而是使用1D紋理而不是寬度x 1 2D紋理)。 在這種情況下,您還可以使用非平面2D紋理和一些索引魔術來支持大於最大紋理大小的列表。

但是,我認為紋理緩沖區是您問題的完美選擇。 為了獲得更准確的了解,您還可以查看相應的擴展規范

編輯:為了回應Nicol關於統一緩沖區對象的評論,您還可以在此處查找兩者的一些比較。 我仍然傾向於使用TBO,但是不能真正地說明原因,僅是因為我認為它在概念上更合適。 但是,也許尼克爾可以提供答案,使人們對該事件有更多的了解。

一種方法是使用您提到的統一數組。 另一種方法是使用一維“紋理”。 查找GL_TEXTURE_1D和glTexImage1D。 我個人更喜歡這種方式,因為您不需要像您所說的那樣在着色器代碼中對數組的大小進行硬編碼,並且opengl已經具有內置功能,可以在GPU上上載/訪問1D數據。

我想說的可能不是數字1 ..着色器統一的寄存器數量有限,具體取決於卡。 您可以查詢GL_MAX_FRAGMENT_UNIFORM_COMPONENTS以了解您的限額。 在較新的卡上,它可以運行到成千上萬個,例如,Quadro FX 5500顯然具有2048。 (http://www.nvnews.net/vbulletin/showthread.php?t=85925)。 這取決於您希望它在什么硬件上運行,以及您可能還想發送到着色器的其他哪些制服。

根據您的要求,可以使2號正常工作。 很抱歉,這里的含糊不清,希望其他人可以為您提供更精確的答案,但是您必須明確說明在較舊的着色器模型卡中調用了多少個紋理。 這也取決於您要對每個片段進行多少次紋理讀取,您可能不想再次嘗試根據着色器模型和性能要求來讀取每個片段1000個元素。 您可以將值打包到紋理的RGBA中,每個紋理調用可以讀取4次,但是由於需要隨機訪問,這可能對您沒有幫助。

我不確定第3位,但是我建議也許看看UAV(無序訪問視圖),盡管我認為這僅是DirectX,而沒有相當的openGL。 我認為openGL有n​​Vidia擴展,但是您再次將自己限制在非常嚴格的最低規格。

將1000的數據項傳遞到片段着色器不太可能是解決問題的最佳解決方案。.也許,如果您提供了有關要實現的目標的更多詳細信息,您可能會得到替代建議?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM