我正在尝试在 HLSL 计算着色器中解压 SNorm16 值。由于 SNorm16x4 = 总共 8 个字节,并且加载/存储函数只能读/写 4 个字节,我试图将两个 4 字节值打包为 1 个 8 字节值,将其解包为 4 个值,处理结果,然后打包它打包并存储为 1 个 8 字节的打包值。
代码是:
float2 UnpackFromSNorm16x2(uint v)
{
uint2 tempU = asuint(uint2(v, v >> 16) & 0xFFFF);
int2 tempI = int2(tempU.x - 32767, tempU.y - 32767);
return float2( tempI * float(1.0 / 32767.0));
}
int FloatToSNorm16(float v)
{
//According to D3D10 rules, the value "-1.0f" has two representations:
// 0x1000 and 0x10001
//This allows everyone to convert by just multiplying by 32767 instead
//of multiplying the negative values by 32768 and 32767 for positive.
return int(clamp(v >= 0.0f ? (v * 32767.0f + 0.5f) : (v * 32767.0f - 0.5f), -32768.0f, 32767.0f));
}
uint PackToSNorm16x2(float2 v)
{
int intX = int(FloatToSNorm16(v.x));
int intY = int(FloatToSNorm16(v.y));
uint2 uintXY = uint2(clamp(intX + 32767, 0, 65535), clamp(intY + 32767, 0, 65535));
uint x = (uintXY.x << 0) & 0x0000FFFF;
uint y = (uintXY.y << 16) & 0xFFFF0000;
return x | y;
}
uint2 inputTangentUInt = asuint(vertices.Load2(baseOffset + tangentOffset));
float4 qTangentUnpacked = float4(UnpackFromSNorm16x2(inputTangentUInt.x), UnpackFromSNorm16x2(inputTangentUInt.y));
//Do some work with qTangentUnpacked
uint2 qTangentPacked = uint2(PackTwoSNORM16(qTangentUnpacked.xy), PackTwoSNORM16(qTangentUnpacked.zw));
vertices.Store2(baseOffset + tangentOffset, asuint(qTangentPacked));
但是最终结果是错误的,看起来有些数据丢失了。 我做错了什么?
有一组用于执行此类转换的 HLSL 例程,称为
D3DX_DXGIFormatConvert.inl
。它记录在 Microsoft Learn 上,并用于在旧版 DirectX SDK 中提供。
最新版本位于 GitHub
请参阅此博文了解更多信息。