简体   繁体   中英

Marshaling arrays into constant buffer for pixel shader not working as expected

I'm trying to learn Direct3d programming using SharpDX and am working through various examples on the internet as well as Frank Luna's Introduction to 3D Game Programming with DirectX 11. Currently I'm trying to pass multiple light sources (Directional, Point and Spotlight) using arrays to my pixel shader. Now, as far as I understand how packing works in HLSL everything should be working but, of course, it's not and I just can't seem to find where the problem is.

In my HLSL shader code I have the following structure definitions for my lighting structures:

#define NUM_LIGHTS 1 //Max number of lights in scene

struct DirectionalLight {
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Direction;
    float pad;
};

struct PointLight {
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Position;
    float Range;
    float3 Attentuation;
    float pad;
};

struct SpotLight {
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Position;
    float Range;
    float3 Direction;
    float Spot;
    float3 Attentuation;
    float pad;
};

As you can see the padding variables are all there to ensure that everything is neatly packed into float4's and obeys 16-byte boundaries.

Then in my C# code I have the following corresponding structs':

[StructLayout(LayoutKind.Sequential, Size=64)]
    public struct DirectionalLight
    {
        public Color4 Ambient;
        public Color4 Diffuse;
        public Color4 Specular;
        public Vector3 Direction;
        public float pad;
    }

[StructLayout(LayoutKind.Sequential, Size=96)]
    public struct Spotlight
    {
        public Color4 Ambient;
        public Color4 Diffuse;
        public Color4 Specular;
        public Vector3 Position;
        public float Range;
        public Vector3 Direction;
        public float Spot;
        public Vector3 Attentuation;
        public float pad;
    }

[StructLayout(LayoutKind.Sequential, Size=80)]
    public struct PointLight
    {
        public Color4 Ambient;
        public Color4 Diffuse;
        public Color4 Specular;
        public Vector3 Position;
        public float Range;
        public Vector3 Attentuation;
        public float pad;
    }

In HLSL my constant buffer is setup as follows:

cbuffer cbPerFrame : register(b1) //register b0 used for cbuffer cbPerObject //(world, view, projection matrices)
{
    DirectionalLight gDirLight[NUM_LIGHTS];
    SpotLight gSpotLight[NUM_LIGHTS];
    PointLight gPointLight[NUM_LIGHTS];
    float3 cameraPosition;
    float fogStart;
    float fogEnd;
    float3 pad;
};

And again in C#:

[StructLayout(LayoutKind.Sequential, Size=272)]
    public struct ConstantBufferPerFrame
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public DirectionalLight[] DirectionalLight;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public Spotlight[] SpotLight;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public PointLight[] PointLight;
        public Vector3 CameraPosition;
        public float FogStart;
        public float FogEnd;
        public Vector3 pad;
    }

Note, for now I'm trying to keep it as simple as possible am only passing in 1 light of each type (that's why SizeConst=1)

Then, I create my constant buffer in C# as follows:

...
int size = Marshal.SizeOf(typeof(ConstantBufferPerFrame));

_constantBufferPerFrame = new SharpDX.Direct3D11.Buffer(device, size,
                ResourceUsage.Dynamic, BindFlags.ConstantBuffer, 
                CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
...
_context.PixelShader.SetConstantBuffer(1, _constantBufferPerFrame);
...

And I write to the buffer as follows:

...

 DataStream mappedResource;
            context.MapSubresource(_constantBufferPerFrame, 0, MapMode.WriteDiscard, MapFlags.None, out mappedResource);

 mappedResource.Write(_perFrameBuffer);

 context.UnmapSubresource(_constantBufferPerFrame, 0);

 context.PixelShader.SetShaderResource(0, texture);

 context.DrawIndexed(indexCount, 0, 0);
...

Now all this looks like it should be working, however when I run my program, the constant buffer is completely wrong.

For example, if the C# ConstantBuffer object contains the following values:

ConstantBuffer
{
  DirectionalLight:
  {
     Ambient: {Alpha:1, Red:100, Green:100, Blue:100},
     Diffuse: {Alpha:1, Red:0, Green:0, Blue:0},
     Specular: {Alpha:1, Red:0, Green:0, Blue:0},
     Direction: {X:0, Y:0, Z:0}
  },
  PointLight:
  {
    Ambient: {Alpha:1, Red:10.7, Green:0.7, Blue:0.7},
    Diffuse: {Alpha:1, Red:0, Green:0, Blue:0},
    Specular: {Alpha:1, Red:0, Green:0, Blue:0},
    Position: {X:201.0751 Y:6.075078 Z:2145},
    Range: 100
    Attentuation: {X:0 Y:1 Z:0}
    Pad: 0
  },
  Spotlight:
  {
    Ambient: {Alpha:1, Red:10.7, Green:0.7, Blue:0.7},
    Diffuse: {Alpha:1, Red:0, Green:0, Blue:0},
    Specular: {Alpha:1, Red:0, Green:0, Blue:0},
    Direction: {X:0, Y:0, Z:0}
    Position: {X:0, Y:0, Z:0}
    Range: 0,
    Spot: 0,
    Attentuation: {x:0, Y:0, Z:0},
    Pad: 0
  },
  CameraPosition: {X:195, Y:16, Z:2145},
  FogStart: 50,
  FogEnd: 1000,
  Pad: {X:0, Y:0, Z:0}
}

In the Graphic Analyzer I get the following Compiler Output:

// cbuffer cbPerFrame
// {
//
//   struct DirectionalLight
//   {
//       
//       float4 Ambient;                // Offset:    0
//       float4 Diffuse;                // Offset:   16
//       float4 Specular;               // Offset:   32
//       float3 Direction;              // Offset:   48
//       float pad;                     // Offset:   60
//
//   } gDirLight;                       // Offset:    0 Size:    64
//   
//   struct SpotLight
//   {
//       
//       float4 Ambient;                // Offset:   64
//       float4 Diffuse;                // Offset:   80
//       float4 Specular;               // Offset:   96
//       float3 Position;               // Offset:  112
//       float Range;                   // Offset:  124
//       float3 Direction;              // Offset:  128
//       float Spot;                    // Offset:  140
//       float3 Attentuation;           // Offset:  144
//       float pad;                     // Offset:  156
//
//   } gSpotLight;                      // Offset:   64 Size:    96
//   
//   struct PointLight
//   {
//       
//       float4 Ambient;                // Offset:  160
//       float4 Diffuse;                // Offset:  176
//       float4 Specular;               // Offset:  192
//       float3 Position;               // Offset:  208
//       float Range;                   // Offset:  220
//       float3 Attentuation;           // Offset:  224
//       float pad;                     // Offset:  236
//
//   } gPointLight;                     // Offset:  160 Size:    80
//   float3 cameraPosition;             // Offset:  240 Size:    12 [unused]
//   float fogStart;                    // Offset:  252 Size:     4 [unused]
//   float fogEnd;                      // Offset:  256 Size:     4 [unused]
//   float3 pad;                        // Offset:  260 Size:    12 [unused]
//
// }

However, the constant buffer has the following values:

#,float
"0","-8.0022389e-09"
"1","+9.9492191e-43"
"2","-8.0024094e-09"
"3","+9.9492191e-43"
"4","-8.002317e-09"
"5","+9.9492191e-43"
"6","+50"
"7","+1000"
"8","+195"
"9","+16"
"10","+2145"
"11","+0"
"12","+0"
"13","+0"
"14","+0"
"15","+0"
"16","+0"
"17","+0"
"18","+0"
"19","+0"
"20","+0"
"21","+0"
"22","+0"
"23","+0"
"24","+0"
"25","+0"
"26","+0"
"27","+0"
"28","+0"
"29","+0"
"30","+0"
"31","+0"
"32","+0"
"33","+0"
"34","+0"
"35","+0"
"36","+0"
"37","+0"
"38","+0"
"39","+0"
"40","+0"
"41","+0"
"42","+0"
"43","+0"
"44","+0"
"45","+0"
"46","+0"
"47","+0"
"48","+0"
"49","+0"
"50","+0"
"51","+0"
"52","+0"
"53","+0"
"54","+0"
"55","+0"
"56","+0"
"57","+0"
"58","+0"
"59","+0"
"60","+0"
"61","+0"
"62","+0"
"63","+0"
"64","+0"
"65","+0"
"66","+0"
"67","+0"

As you can see, the CameraPosition and Fog End/Start values were marshaled correctly (or is it? when I look at it again I see that the positions are swopped, I would have expected the CameraPosition variables in floats 6,7 and 8 and the 2 fog floats in position 9 and 10), it's only the array light structures that seems to be a bit wonky.

I must be missing something somewhere, can anyone perhaps help me?

You can't really do that using Marshal attributes with DataStream. DataStream doesn't provide a Marshal/PInvoke layer, but dumps directly into the memory without a marshal layer in the middle. I would use fixed array in the struct instead or would custom serialize the array to the DataStream/buffer if the NUM_LIGHTS has to be adaptive in someway

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM