[英]Why does order matter in shaders?
這個問題帶有C++
標記,因為使用C++
DirectX
開發人員比使用C#
開發人員更多。 我不認為這個問題與任何一種語言都直接相關,而是與所使用的類型(據我所知完全相同)或DirectX
本身及其如何編譯着色器有關。 如果使用C++
知道更好,更具描述性的答案,那么我寧願選擇它而不是自己的答案。 我了解兩種語言,但主要使用C#
。
在HLSL
着色器中,設置我的常量緩沖區時遇到了一個非常奇怪的問題。 有問題的原始常量緩沖區設置如下:
cbuffer ObjectBuffer : register(b0) {
float4x4 WorldViewProjection;
float4x4 World;
float4x4 WorldInverseTranspose;
}
cbuffer ViewBuffer : register(b1) {
DirectionalLight Light;
float3 CameraPosition;
float3 CameraUp;
float2 RenderTargetSize;
}
如果我在周圍交換b0
和b1
寄存器,則渲染不再起作用( e1 )。 如果我不理會這些寄存器,並再次在World
和WorldViewProjection
之間交換順序,則渲染不再起作用( e2 )。 但是,只需將ViewBuffer
ObjectBuffer
HLSL
文件中的ViewBuffer
上方,而不進行其他修改,就可以正常工作。
現在,我希望寄存器的放置非常重要,並且第一個寄存器b0
需要該緩沖區中給定的三個屬性,並且我知道HLSL
常量緩沖區必須位於16個字節的塊中。 但是,這使我有些疑問。
鑒於HLSL
期望常量緩沖區為16字節塊;
float4x4
類型是否與Matrix
類型相同,而后者基本上是一個數組數組?
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ TOTAL ] = 64 bytes
由於float
本身是4個字節,因此這意味着float4
是16個字節,因此float4x4
是64個字節。 那么,如果大小保持不變,為什么順序很重要呢?
ObjectBuffer
分配給b0
而不是其他b
寄存器? 我目前正在對問題進行進一步的分析,以便可以給出更詳細和准確的答案。 我將更新問題和答案,以反映我發現更多細節時盡可能多的准確性。
上面問題的確切問題(發布HLSL
不知道)是HLSL
緩沖區不匹配其C#
表示形式。 因此,變量的重新排序導致着色器失敗。 但是,我仍然不確定為什么類型相同。 我在沿途中學到了其他一些問題,希望將其發布在這里。
經過一些進一步的研究和測試之后,對於類型完全相同的背后原因,我仍然不確定100%。 總的來說,我認為這可能是由於cbuffer
預期的類型以及struct
類型的順序cbuffer
。 在這種情況下,如果您的cbuffer
期望bool
,然后是float
,則重新排列會導致問題。
cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}
我進行了一些研究工作來測試類型的字節大小的差異,但這似乎並沒有太大改變,但是我將在此處發布常見基本類型的發現:
1 Byte : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal
您確實必須意識到用於構造更復雜類型的基本類型。 假設您有一個帶有X
屬性和Y
屬性的Vector2
。 如果這些內容由float
類型表示,則在下一個屬性之前需要8字節的填充,除非您有其他幫助達到16字節的填充。 但是,如果它們由double
類型或decimal
類型表示,則大小會有所不同,您需要注意這一點。
我能夠解決注冊問題; 設置緩沖區時,這也對應於C#
端。 設置緩沖區時,需要為這些緩沖區分配索引,並且HLSL
應該使用相同的索引。
// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);
// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);
由於緩沖區已分配給正確的寄存器,因此上面的代碼將按預期工作。 但是,例如,如果將攝像機的緩沖區更改為8,則必須將cbuffer
分配給寄存器b8
才能正常工作。 出於確切的原因,下面的代碼不起作用。
cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.