Best practices for vertex packing


I am exploring the use of compacted vertex formats that may be modified with a geometry shaders. For geometry shaders data must be 4 bytes aligned.

My current vertex structure was :

typedef struct _Vert
{
    XMFLOAT3 Pos;
    XMFLOAT3 Normal;
    XMFLOAT4 Color;
    XMFLOAT2 Tex;
    UINT ID;
}Vert, *LPVert;

with the following

const D3D11_INPUT_ELEMENT_DESC PNCTILayout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },    
    { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 40, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "BUFFERID", 0, DXGI_FORMAT_R32_UINT, 0, 48, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

As color in R8G8B8A8_UNORM format can be natively decoded to float4 in the vertex shader I first changed XMFLOAT4 Color to UINT (or XMCOLOR) Color for creating the vertex on the CPU. Then I changed the UV coordinates from XMFLOAT2 to XMHALF2 with the use of XMConvertFloat2toHalf when creating the vertex data for my mesh, giving the following:

typedef struct _Vert
{
    XMFLOAT3 Pos;
    XMFLOAT3 Normal;
    XMCOLOR Color;
    XMHALF2 Tex;
    UINT    ID16;
}Vert, *LPVert;

const D3D11_INPUT_ELEMENT_DESC PNCTILayout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32_UINT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "BUFFERID", 0, DXGI_FORMAT_R32_UINT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

The TEXCOORD is not R16G16_FLOAT because it does not work for geometry shaders where each component must be 4 bytes. The hack is to use a uint and pack in the CPU the two components in 0xFFFF and 0x0000FFFF bits, what XMHALF2 do. In the shaders we need to unpack the UV UINT to two floats :

float2 UnpackUV(uint packed)
{
    uint2 h = uint2(packed & 0xFFFF, packed >> 16);
        return f16tof32(h);
}

It works fine but what is the real practice for games for PCs? Compaction is a regular strategy for textures but I don’t see too much about vertex. In particular if I want to also pack position or normal, what should work the best regarding percision for application in regular 3D world settings where the world is not aligned to any grid (no voxels)?



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *