Маршалинг массивов в постоянный буфер для пиксельного шейдера не работает должным образом - PullRequest
0 голосов
/ 01 июля 2018

Я пытаюсь научиться программированию Direct3d с использованием SharpDX и работаю с различными примерами в Интернете, а также с введением Фрэнка Луны в программирование 3D-игр с помощью DirectX 11. В настоящее время я пытаюсь пропустить несколько источников света (Directional, Point и Spotlight) используя массивы для моего пиксельного шейдера. Теперь, насколько я понимаю, как работает упаковка в HLSL, все должно работать, но, конечно, это не так, и я просто не могу понять, в чем проблема.

В моем коде шейдера HLSL у меня есть следующие определения структуры для моих структур освещения:

#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;
};

Как видите, все переменные-заполнители предназначены для того, чтобы все было аккуратно упаковано в float4 и соответствовало 16-байтовым границам.

Тогда в моем коде C # у меня есть следующие соответствующие структуры ':

[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;
    }

В HLSL мой постоянный буфер настроен следующим образом:

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;
};

И снова в 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;
    }

Заметьте, сейчас я стараюсь сделать это как можно проще, пропускаю только по 1 свету каждого типа (поэтому SizeConst = 1)

Затем я создаю свой постоянный буфер в C # следующим образом:

...
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);
...

И я пишу в буфер следующим образом:

...

 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);
...

Теперь все это выглядит так, как будто оно должно работать, однако, когда я запускаю свою программу, постоянный буфер совершенно неверен.

Например, если объект C # ConstantBuffer содержит следующие значения:

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}
}

В графическом анализаторе я получаю следующий вывод компилятора:

// 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]
//
// }

Однако постоянный буфер имеет следующие значения:

#,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"

Как вы можете видеть, значения CameraPosition и Fog End / Start были правильно распределены (или это так? Когда я смотрю на него снова, я вижу, что позиции смещены, я ожидал, что переменные CameraPosition в числах с плавающей запятой 6,7 а 8 и 2 тумана плавают в позициях 9 и 10), это только массивы легких структур, которые кажутся немного странными.

Должно быть, я что-то упустил, может кто-нибудь мне помочь?

1 Ответ

0 голосов
/ 02 июля 2018

Вы не можете сделать это, используя атрибуты Marshal с DataStream. DataStream не предоставляет слой Marshal / PInvoke, но создает дамп непосредственно в память без слоя маршала посередине. Я бы вместо этого использовал фиксированный массив в структуре или настраивал бы сериализацию массива в DataStream / буфер, если NUM_LIGHTS должен быть адаптивным в некотором роде

...