将数组编组到常量缓冲区中,使像素着色器无法按预期工作
Marshaling arrays into constant buffer for pixel shader not working as expected
我正在尝试使用 SharpDX 学习 Direct3d 编程,并且正在研究互联网上的各种示例以及 Frank Luna 的 DirectX 11 3D 游戏编程简介。目前我正在尝试传递多个光源(定向, 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),只是阵列灯结构似乎有点不稳定。
我一定是遗漏了什么地方,谁能帮帮我?
您不能真正将 Marshal 属性与 DataStream 一起使用。 DataStream 不提供 Marshal/PInvoke 层,而是直接转储到内存中,中间没有 marshal 层。我会在结构中使用固定数组,或者如果 NUM_LIGHTS
必须以某种方式自适应
,则自定义将数组序列化为 DataStream/buffer
我正在尝试使用 SharpDX 学习 Direct3d 编程,并且正在研究互联网上的各种示例以及 Frank Luna 的 DirectX 11 3D 游戏编程简介。目前我正在尝试传递多个光源(定向, 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),只是阵列灯结构似乎有点不稳定。
我一定是遗漏了什么地方,谁能帮帮我?
您不能真正将 Marshal 属性与 DataStream 一起使用。 DataStream 不提供 Marshal/PInvoke 层,而是直接转储到内存中,中间没有 marshal 层。我会在结构中使用固定数组,或者如果 NUM_LIGHTS
必须以某种方式自适应