八、法线贴图及其编解码操作的函数
法线贴图存储的信息是对模型顶点法线的扰动方向向量,利用此扰动方向向量,在光照计算时对顶点原有的法线进行扰动,从而使法线方向排列有序的平滑表面产生法线方向杂乱无序从而导致表面凹凸不平的效果。
要在Unity3D中导入和使用法线贴图,要设置为Normal map类型,是因为在不同平台上,Unity3D可以利用该平台硬件加速的纹理格式对导入的法线贴图进行压缩,同时也因为法线贴图和普通纹理贴图在采样和解码时的方式也有所不同。
8.1 UnpackNormal函数
如果UNITY_NO_DXT5nm宏启用了,表示引擎使用了DXT5nm压缩格式或者BC5压缩格式的法线贴图纹理,则调用了UnpackNormalmapRGorAG函数去解码,如下:
inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(UNITY_NO_DXT5nm)
return packednormal.xyz * 2 - 1;
#else
return UnpackNormalmapRGorAG(packednormal);
#endif
}
DXT是一种纹理压缩格式,以前称为S3TC。当前很多图形硬件已经支持这种格式,即在显存中依然保持着压缩格式,从而减少显存占用量。目前有DXT1~5这5种编码格式。
DXT系列压缩格式被很多格式的文件所使用,如DDS文件格式就使用了DXT系列压缩格式。要使用DXT格式压缩图像,要求图像大小至少是4X4文素,而且图像高宽的文素个数是2的整数次幂,如32X32,64X128等。
8.2 UnpackNormalmapRGorAG函数
该函数能同时处理DXT5nm和DXT格式的法线贴图,并正确地把法线贴图扰动向量从纹素中解码出来,如下:
// Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1)
// Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5
fixed3 UnpackNormalmapRGorAG(fixed4 packednormal)
{
// This do the trick
packednormal.x *= packednormal.w;
fixed3 normal;
normal.xy = packednormal.xy * 2 - 1;
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return normal;
}
8.3 UnpackNormalDXT5nm函数
该函数用来解码DXT5nm格式的法线贴图,其算法思想和UnpackNormalmapRGorAG一致。
inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{
fixed3 normal;
normal.xy = packednormal.wy * 2 - 1;
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return normal;
}
九、线性化深度值的工具函数
9.1 Linear01Depth函数
//把从深度纹理中取得的顶点深度值z变换到观察空间中,然后映射到[0,1]区间内
//_ZBufferParams的x分量为1减去视截体远截面与近截面值的商,_ZBufferParams的y分量为视截体远截面与近截面值的商
// Z buffer to linear 0..1 depth
inline float Linear01Depth( float z )
{
return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}
片元的深度往往是非线性的,即从近截面到远截面之间的深度值精度分布不均匀。但在有些场合中需要线性化的深度值,如在观察空间中要利用深度值计算时,便要把深度纹理贴图中获取到的深度值重新映射到一个线性区域。
9.2 LinearEyeDepth函数
//把从深度纹理中取得的顶点深度值z变换到观察空间中
//_ZBufferParams的x分量为x分量除以视截体远截面的值,w分量为y分量除以视截体远截面的值
// Z buffer to linear depth
inline float LinearEyeDepth( float z )
{
return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
}
9.3 封装了操作深度纹理的工具宏
// Depth render texture helpers
#define DECODE_EYEDEPTH(i) LinearEyeDepth(i)
//取得顶点从世界空间变换到观察空间后的z值,并取其相反数
#define COMPUTE_EYEDEPTH(o) o = -UnityObjectToViewPos( v.vertex ).z
//取得顶点从世界空间变换到观察空间后的z值,取得相反数后将值映射到[0,1]范围内
#define COMPUTE_DEPTH_01 -(UnityObjectToViewPos( v.vertex ).z * _ProjectionParams.w)
//把顶点法线从世界空间变换到观察空间
#define COMPUTE_VIEW_NORMAL normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal))