gpt4 book ai didi

c# - 为什么在着色器中顺序很重要?

转载 作者:行者123 更新时间:2023-12-02 10:24:09 26 4
gpt4 key购买 nike

快速说明

这个问题带有C++标记,因为使用DirectX中的C++的开发人员比使用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;
}

如果我交换 b0b1寄存器,则渲染不再起作用(e1)。如果我不理会这些寄存器,并再次在 WorldWorldViewProjection之间交换顺序,则渲染不再起作用(e2)。但是,只需将 ViewBuffer移到 ObjectBuffer文件中的 HLSL上方,而不进行其他修改,就可以了。

现在,我希望寄存器的放置非常重要,并且第一个寄存器 b0需要该缓冲区中提供的三个属性,并且我知道 HLSL常量缓冲区必须位于16字节块中。但是,这使我有些疑问。

问题

鉴于 HLSL期望常量缓冲区位于16字节块中;
  • 为什么e2中的排序如此重要?
  • 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寄存器?
  • 最佳答案

    快速说明

    我目前正在对问题进行进一步的分析,以便可以给出更详细和准确的答案。我将更新问题和答案以反射(reflect)我发现更多细节时尽可能多的准确性。

    基本答案

    上面问题的确切问题(发布时尚不知道)是HLSL缓冲区与它们的C#表示形式不匹配。因此,变量的重新排序导致着色器失败。但是,我仍然不确定为什么类型相同。我在沿途中学到了其他一些东西,希望得到答案,因此决定将其发布在这里。

    为什么要订购

    经过一些进一步的研究和测试之后,对于类型完全相同的背后原因,我仍然不确定100%。总体而言,我认为这可能是由于cbuffer中预期的类型以及struct中类型的顺序所致。在这种情况下,如果您的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

    您必须意识到用于构造更复杂类型的基本类型。假设您有一个带有 Vector2属性和 X属性的 Y。如果这些是用 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);

    关于c# - 为什么在着色器中顺序很重要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53345125/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com