DirectX - 顶点缓冲区中使用半精度浮点数代替单精度浮点数

4

最近我决定压缩我的顶点数据以使渲染更加高效,我找到了一个解决方案 - 使用half(具体来说是half4和half2)而不是float来存储我的顶点数据。我的顶点结构如下:

[StructLayout(LayoutKind.Sequential)]  
public struct MyVertex  
{  
    public Half4 Position; //8 bytes
    public Half4 Normal; //8 bytes
    public Half2 UVW; //4 bytes
    public Half4 TextureID; //8 bytes  
    public Half4 BlendFactor; //8 bytes
    public const int SizeInBytes = (2 * 4) * 4 + (2 * 2);  
}  

这是我的顶点声明:

MyVertexDecl = new VertexDeclaration(device,
            new VertexElement[6]
        {
            new VertexElement(0, 0, DeclarationType.HalfFour, DeclarationMethod.Default, DeclarationUsage.Position, 0),
            new VertexElement(0, 8, DeclarationType.HalfFour, DeclarationMethod.Default, DeclarationUsage.Normal, 0),
            new VertexElement(0, 16, DeclarationType.HalfTwo, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
            new VertexElement(0, 20, DeclarationType.HalfFour, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 1),
            new VertexElement(0, 28, DeclarationType.HalfFour, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 2),
            VertexElement.VertexDeclarationEnd

        });

当我直接将数据作为MyVertex数组发送到DrawUserPrimitives时,一切都正常。但是,当我决定将所有数据放入VBO中以避免过多复制操作时,我的所有网格都变成了乱码。我将值写为ushorts(SlimDX halfs每个组件都有RawValue字段)。

这是PIX关于我的网格的显示:

crazy numbers

下面是在指定布局后缓冲区查看器中相同部分的显示(仅显示一列,因为它们很宽,希望您能理解):

there is a nice numbers you have there

可以看出,我的halfs被错误地视为floats。在着色器中将float4更改为half4也没有帮助,似乎问题出在其他地方。有没有另一种方法可以告诉GPU将我的顶点缓冲区用作halfs缓冲区而不是floats缓冲区?

更新:

这是我的绘图代码(简化):

public void Render(GraphicsDevice device, ICamera camera, MyModel model)
    {
        //there are some SetValue calls
        model.Effect.CommitChanges();
        foreach (D3D9.VertexBuffer buf in model.Meshes)
        {
            device.SetStreamSource(0, buf, 0, MyVertex.SizeInBytes);
            device.DrawPrimitives(D3D9.PrimitiveType.TriangleList, 0, mesh.VertexCount);
        }
    }

以下是该方法的调用方式(主要部分):

_eff.BeginPass(0);
GraphicsDevice.VertexDeclaration = vDecl;
renderer.Render(GraphicsDevice, camera, world);
_eff.EndPass();

我正在使用C#通过SlimDX来使用D3D9。


1
你能展示一下填充缓冲区的代码片段吗? - Gnietschow
@Gnietschow 当然,这是链接 - http://pastebin.com/YzcnUc8r - RaZeR RawByte
1
嗯,我没有看到任何错误。你的绘图代码怎么样?愚蠢的问题:在绘制调用之前,你记得设置顶点声明了吗? - Gnietschow
@Gnietschow 听起来我在某个地方犯了一个愚蠢的错误。我已经更新了我的帖子,并附上了一部分绘图代码。谢谢你的帮助! - RaZeR RawByte
@Gnietschow 有什么想法吗?赏金将在22小时后结束,我想授予给你 :) - RaZeR RawByte
1
非常好:),但不幸的是我不知道为什么它不起作用。最后一个猜测:在您的填充代码中,您使用了data.indices.Count(),应该是顶点的数量吧?并且您解锁了缓冲区吗? - Gnietschow
2个回答

1

我解决了。我忘记锁定我的缓冲区并调用我的VertexDeclaration初始化方法。对于一个有5年经验的程序员来说真是可惜...谢谢,现在它可以工作了 :)


0

通常的图形卡管道都被优化为使用浮点数计算高带宽内容。

我认为DirectX在低级别上无法处理顶点缓冲区中的半精度数据。

我通过Device :: CreateVertexBuffer和D3DFVF的文档来支持我的猜测:

CreateVertexBuffer:格式通过参数FVF传递。也许将其设置为非FVF缓冲区时,可以使用自定义着色器渲染缓冲区(但我不知道您的框架是否会尝试这样做,因为它可能会更慢)。如果它不是零:

D3DFVF:所有可用的顶点格式都只有浮点数。


1
他正在使用顶点声明而不是FVF,因此应该可以使用halfs,例如在文档http://msdn.microsoft.com/en-us/library/windows/desktop/bb172533%28v=vs.85%29.aspx中有一个声明类型`D3DDECLTYPE_FLOAT16_4`。 - Gnietschow
@Gnietschow 是的,你说得对 - 我无法让它与我的顶点缓冲区一起工作。我的GPU支持D3DTDELTYPE_FLOAT_16_4,但看起来他不知道如何解释我的缓冲区,或者我只是把参数发送到了错误的位置,或者我用错误的值填充了我的缓冲区...从PIX输出中可以看出,GPU只将我的缓冲区解释为浮点数,忽略了我的decltype。如果需要,我可以向您展示更多代码。任何帮助都将不胜感激 :) - RaZeR RawByte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接