目前正在使用 SlimDX 的 Direct3D 11 绑定,并且在使用我的着色器时遇到了很大的困难,其来源如下:
/* * * * * * * * *
* PARAM STRUCTS *
* * * * * * * * */
struct VSPointIn
{
float3 pos : POSITION;
float4 color : COLOR;
};
struct GSParticleIn
{
float3 pos : POSITION;
float4 color : COLOR;
float radius : RADIUS;
};
struct PSParticleIn
{
float4 pos : SV_Position;
float2 tex : TEXCOORD0;
float3 eyePos : TEXCOORD1;
float radius : TEXCOORD2;
float4 color : COLOR;
};
struct PSParticleOut
{
float4 color : SV_Target;
float depth : SV_Depth;
};
/* * * * * * * * * * *
* CONSTANT BUFFERS *
* * * * * * * * * * */
cbuffer MatrixBuffer : register(cb1)
{
float4x4 InvView;
float4x4 ModelView;
float4x4 Projection;
float4x4 ModelViewProjection;
};
cbuffer ImmutableBuffer
{
float3 Positions[4] =
{
float3(-1, 1, 0),
float3(1, 1, 0),
float3(-1, -1, 0),
float3(1, -1, 0)
};
float2 Texcoords[4] =
{
float2(0, 0),
float2(1, 0),
float2(0, 1),
float2(1, 1)
};
float3 LightDirection = float3(-0.5, -0.5, -2.0);
}
/* * * * * * * * *
* STATE OBJECTS *
* * * * * * * * */
BlendState AdditiveBlending
{
AlphaToCoverageEnable = FALSE;
BlendEnable[0] = TRUE;
SrcBlend = SRC_ALPHA;
DestBlend = ONE;
BlendOp = ADD;
SrcBlendAlpha = ZERO;
DestBlendAlpha = ZERO;
BlendOpAlpha = ADD;
RenderTargetWriteMask[0] = 0x0F;
};
BlendState NoBlending
{
AlphaToCoverageEnable = FALSE;
BlendEnable[0] = FALSE;
};
DepthStencilState EnableDepth
{
DepthEnable = TRUE;
DepthWriteMask = ALL;
DepthFunc = LESS_EQUAL;
};
DepthStencilState DisableDepth
{
DepthEnable = FALSE;
DepthWriteMask = ZERO;
};
/* * * * * * * * * * *
* SHADER FUNCTIONS *
* * * * * * * * * * */
GSParticleIn VSParticleMain(VSPointIn input)
{
GSParticleIn output;
output.pos = input.pos;
output.color = input.color;
output.radius = 5.0;
return output;
}
[maxvertexcount(4)]
void GSParticleMain(point GSParticleIn input[1], inout TriangleStream<PSParticleIn> SpriteStream)
{
PSParticleIn output = (PSParticleIn)0;
[unroll]
for(int i = 0; i < 4; i++)
{
float3 position = Positions[i] * input[0].radius;
position = mul(position, (float3x3)InvView) + input[0].pos;
output.pos = mul(float4(position, 1.0), ModelViewProjection);
output.eyePos = mul(float4(position, 1.0), ModelView).xyz;
output.color = input[0].color;
output.tex = Texcoords[i];
output.radius = input[0].radius;
SpriteStream.Append(output);
}
SpriteStream.RestartStrip();
}
PSParticleOut PSParticleMain(PSParticleIn input)
{
PSParticleOut output;
float3 norm;
norm.xy = (input.tex * 2.0) - 1.0;
float sqrad = dot(norm.xy, norm.xy);
if(sqrad > 1.0) discard;
norm.z = -sqrt(1.0 - sqrad);
float4 pixelpos = float4(input.eyePos + (norm * input.radius), 1.0);
float4 clspace = mul(pixelpos, Projection);
float diffuse = max(0.0, dot(norm, LightDirection));
output.depth = clspace.z / clspace.w;
output.color = diffuse * input.color;
return output;
}
/* * * * * * * * * * * * * *
* TECHNIQUE DECLARATIONS *
* * * * * * * * * * * * * */
technique11 RenderParticles
{
pass p0
{
SetVertexShader(CompileShader(vs_5_0, VSParticleMain()));
SetGeometryShader(CompileShader(gs_5_0, GSParticleMain()));
SetPixelShader(CompileShader(ps_5_0, PSParticleMain()));
SetBlendState(NoBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);
SetDepthStencilState(EnableDepth, 0);
}
}
着色器的目的是对彩色顶点的点列表进行操作,使用几何着色器将每个顶点扩展为广告牌“冒名顶替者”球体,然后在写入调整后的深度(眼睛空间)的同时将最终场景写入渲染目标每个像素到深度缓冲区的“球体”法线的深度偏移。目前,此配置确实可以正确渲染场景,如下图所示。
http://i.imgur.com/2SQ6228.png
但是,深度缓冲区永远不会被写入,并且当在图形调试器中检查时,它仍然是纯白色 (1.0)。由于我认为我的代码中的错误可能在于矩阵的构造或传递给着色器的方式,以下表示示例绘制调用期间我的矩阵常量缓冲区的内容,以供参考:
Inverse View
0 [0x00000000-0x0000000f] | { +0.9993909 +6.102026e-009 +0.03489951 +2.132527e-013}
1 [0x00000010-0x0000001f] | { +7.1054282e-015 +1 -1.7484578e-007 +2.348344e-012}
2 [0x00000020-0x0000002f] | { -0.034899514 +1.7473927e-007 +0.99939084 -200.00002}
3 [0x00000030-0x0000003f] | { +0 +0 +0 +1}
Model View
4 [0x00000040-0x0000004f] | { +0.99939078 +7.1054269e-015 -0.03489951 -6.9799023}
5 [0x00000050-0x0000005f] | { +6.1020251e-009 +0.99999994 +1.7473926e-007 +3.4947851e-005}
6 [0x00000060-0x0000006f] | { +0.034899514 -1.7484578e-007 +0.99939084 +199.87817}
7 [0x00000070-0x0000007f] | { +0 +0 +0 +1}
Projection
8 [0x00000080-0x0000008f] | { +2.0606079 +0 +0 +0}
9 [0x00000090-0x0000009f] | { +0 +2.7474773 +0 +0}
10 [0x000000a0-0x000000af] | { +0 +0 +1.0001 -0.10001}
11 [0x000000b0-0x000000bf] | { +0 +0 +1 +0}
Model View Projection
12 [0x000000c0-0x000000cf] | { +2.0593526 +1.4641498e-014 -0.071914211 -14.382842}
13 [0x000000d0-0x000000df] | { +1.6765176e-008 +2.7474771 +4.8009213e-007 +9.6018426e-005}
14 [0x000000e0-0x000000ef] | { +0.034903005 -1.7486327e-007 +0.9994908 +199.79816}
15 [0x000000f0-0x000000ff] | { +0.034899514 -1.7484578e-007 +0.99939084 +199.87817}
我知道的
通过使用 Visual Studio 2012 的图形调试器,我能够确认深度缓冲区已正确绑定,并且可以由像素着色器正确绘制。我通过简单地将 output.depth 分配给 [0, 1] 范围内的某个随机浮点数来确认这一点。
我的视图矩阵是由以下代码段中的 SlimDX.Matrix.LookAtLH() 函数生成的:
private Matrix CreateView() { Matrix rotMatrix; var up = Vector3.UnitY; var look = LookTarget; var rot = Rotation * ((float)Math.PI / 180.0f); Matrix.RotationYawPitchRoll(rot.Y, rot.X, rot.Z, out rotMatrix); Vector3.TransformCoordinate(ref look, ref rotMatrix, out look); Vector3.TransformCoordinate(ref up, ref rotMatrix, out up); return Matrix.LookAtLH(Position, Position + look, up); }
我的投影矩阵由以下代码段中的 SlimDX.Matrix.PerspectiveFovLH() 生成:
public Camera(Viewport viewport) { var aspect = viewport.Width / viewport.Height; var fov = 40.0f * ((float)Math.PI / 180.0f); projectionMatrix = Matrix.PerspectiveFovLH(fov, aspect, 0.1f, 1000.0f); }
我的模型矩阵只是单位矩阵,无论它出现在哪里都会被省略。
使用图形调试器,我能够逐步通过像素着色器到计算剪辑空间深度的点。在着色器尝试将 clipspace.z 除以 clipspace.w 之前,我的着色器局部变量如下所示:
http://i.imgur.com/XOGniyd.png
这是针对一些任意选择的像素,尽管每个像素都会为 clspace 向量产生相似的结果。如您所见,clspace.z 和 clspace.w 在大小上总是如此相似,以至于除法总是导致 ~1。
我试过的
在将矩阵上传到设备之前对其进行转置。什么都没做,只是造成了我认为是我的投影被反转的结果的失真,这向我表明,使用 SlimDX 的效果框架不需要预先转置矩阵。(这个怀疑后来得到证实,SlimDX 的效果框架会自动转置设置为效果变量的矩阵。)
在编译我的效果时,通过添加列/行主要矩阵打包标志来隐式转置矩阵。反转向量和矩阵在着色器中出现的任何位置的乘法顺序。这些都是试图解决我认为是列/行主要问题的错误尝试。
使用右手投影和视图矩阵,而不是左手。没有效果,但轴的预期反转。
使用许多不同的近距和远距投影参数。尽管没有解决深度问题,但它们对近距和远距剪裁距离产生了预期的影响。
我真的用尽了所有可用的资源来试图找出导致这个问题的原因。我已经浏览了几乎所有可能的矩阵排列、操作数顺序和状态设置,我可以理解并参考了无数的 MSDN 文章和 SO 问题,但似乎没有任何内容适用。非常感谢您对这个问题的帮助!