DirectX 12 – Normal Mapping 法线映射

19.2 法线映射

法线映射贴图也是一张贴图,但是它的每一个texel并不会存储RGB数据,而是在RGB中分别存储x轴,y轴,z轴的坐标数据。这些坐标值定义了一个法线向量;因此法线映射贴图在其每一个像素中存储了一个法线向量。下图将一张法线贴图“实例化”。

为了对应上图,假设法线映射贴图是24位格式的图片,也就是说它为RGB每一个成员预留了1个字节的空间,因此每一个成员的范围为[0,255]。(我们也可以使用32位格式的贴图,这样的话α通道可能被弃用或者存储其他数据,比如说heightmap或者specular map。此外,我们也可以使用浮点数格式的贴图,但这也意味着我们不需要对向量进行压缩,但是需要更多的存储空间。)

正如上图所示,大多数向量都与z轴对齐。也就是说,z轴的坐标值最大。因此,大多数法线映射贴图看上去都是蓝色的。这是因为z轴的坐标存储在蓝色通道(RGB中的B)。

所以该如何将一个单位向量压缩为以上格式呢(RGB总共占24位)?对于一个单位向量来说,每一个坐标轴上值的范围都是[-1,1]。如果范围是[0,1],那么将其乘以255之后,范围变为[0-255]。那么如果x的范围是[-1,1],那么将x转换为f(x)且范围是[0,255]的等式为:

所以,为了将一个单位向量存储在24位格式的图像中,我们只需将f(x)应用于RGB的每一个成员即可。

接下来,我们需要做的就是在读取法线映射贴图中的法线向量后如何将其还原为范围[-1,1]的浮点数单位向量:

也就是说,如果x是一个范围[0,255]的整型数,那么以上等式可以将其转换为范围[-1,1]的浮点数。

由于PS软件提供了插件能够将图像转换为法线映射贴图,我们并不需要自己进行压缩处理。但是,当我们在像素着色器中采样一个法线映射贴图时:

// HLSL
float3 normalT = gNormalMap.Sample(gTriLinearSam, pin.Tex);

通过以上函数得到的向量normalT已经拥有了标准化的成员(0≤r,g,b≤1)。因此,函数已经帮我们完成了一部分解压的处理(其只是将每个成员都除以255,因此我们得到的rgb成员的范围是[0,1])。而我们将自行将范围[0,1]转换为[-1,1]:

在代码中,我们会将以下函数应用与颜色向量的RGB成员:

// HLSL
normalT = 2.f * normalT - 1.f;

在HLSL代码中,以上代码中的1.f会被转换为向量(1.f,1.f,1.f)。

如果你想要使用一个压缩的贴图格式来存储法线映射贴图,那么使用BC7(DXGI_FORMAT_BC7_UNORM)能提供最好的精确度,其能够大大减少压缩后的法线映射贴图所造成的精确度错误。

留下评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据