在制作纹理资产查看器时总结的一些思路和逻辑。
GAMMA
最终显示的结果是要看最终渲染到的RT的纹理格式,该引擎交换链的纹理使用的是线性格式,所以纹理也全是以线性的方式来显示,这样就会和其他的图片浏览器显示结果不一致而产生困惑,所以纹理浏览器添加了Gamma的显示开关,默认启用,用户可以选择性关闭来查看纹理的线性数据。
通道开关
对RGBA四个通道设有启用和禁用的开关,可以方便的查看每个通道内的数据,其中B和A通道的逻辑会有些不太一样。
RGB三个通道都可以单独的进行开关,A通道是比较特殊的一个通道,在四个通道都启用时没有问题,但如果仅启用A通道时,RGB通道都为0无法渲染出内容,所以要在仅启用A通道时将A的数据赋值给RGB,然后将A通道置为1。
if (!(_Flags & FLAGS_CHANNEL_R) && !(_Flags & FLAGS_CHANNEL_G) && !(_Flags & FLAGS_CHANNEL_B) && _Flags & FLAGS_CHANNEL_A) { output.xyz = imgColor.w; output.w = 1; }
这样在仅启用A通道的情况下就可以以灰度来显示。
另外一个特殊通道就是B通道,在一些特殊压缩的格式下这个通道可能没有数据,例如BC5,BC5使用较高精度的双灰度通道经常用作法线纹理的压缩格式,而法线纹理B通道始终为1可以舍弃。所以纹理应该将是否为法线纹理的信息传入Shader,我这里使用了FLAGS_NORMALMAP位运算方式传入。在纹理类型为法线时,B通道强制为1,来解决纹理压缩通道信息丢失的问题。
float4 output = float4(0,0,0,1); if (_Flags & FLAGS_NORMALMAP) output.b = 1; float4 imgColor = _Image.Sample(ColorSampler, v2f.TexCoord0); if (_Flags & FLAGS_CHANNEL_R) output.x = imgColor.x; if (_Flags & FLAGS_CHANNEL_G) output.y = imgColor.y; if ((_Flags & FLAGS_CHANNEL_B) && !(_Flags & FLAGS_NORMALMAP)) output.z = imgColor.z; if (_Flags & FLAGS_CHANNEL_A) output.w = imgColor.w;
透明背景
透明背景一般有两种表现方式,一种是黑色背景,另一种就是棋盘格图案,这里采用程序化方法直接在shader中绘制棋盘格,最后使用a通道数值对前景和背景进行lerp操作。
if (_Flags & FLAGS_EnableCheckerBackground) { float2 channels = saturate(fmod(floor(v2f.Position.xy / _GridSize), 2)); float crossArea = 1-saturate(channels.x + channels.y); float blackArea = channels.x * channels.y; float4 checker = lerp(_CheckerColorA, _CheckerColorB, crossArea + blackArea); output = lerp(checker, output, output.a); } else { output = float4(lerp(0, output.xyz, output.w), 1); }
完整的HLSL
#include "PostProcessing.inc.hlsl" #define FLAGS_GAMMA 0x01 #define FLAGS_EnableCheckerBackground 0x02 #define FLAGS_CHANNEL_R 0x04 #define FLAGS_CHANNEL_G 0x08 #define FLAGS_CHANNEL_B 0x10 #define FLAGS_CHANNEL_A 0x20 #define FLAGS_NORMALMAP 0x40 cbuffer Properties : register(b0, space3) { float4 _CheckerColorA; float4 _CheckerColorB; float _GridSize; int _Flags; } Texture2D _Image : register(t1, space3); SamplerState _ImageSampler : register(s1, space3); float4 main(PPVSOutput v2f) : SV_TARGET { float4 output = float4(0,0,0,1); if (_Flags & FLAGS_NORMALMAP) output.b = 1; float4 imgColor = _Image.Sample(ColorSampler, v2f.TexCoord0); if (_Flags & FLAGS_CHANNEL_R) output.x = imgColor.x; if (_Flags & FLAGS_CHANNEL_G) output.y = imgColor.y; if ((_Flags & FLAGS_CHANNEL_B) && !(_Flags & FLAGS_NORMALMAP)) output.z = imgColor.z; if (_Flags & FLAGS_CHANNEL_A) output.w = imgColor.w; if (!(_Flags & FLAGS_CHANNEL_R) && !(_Flags & FLAGS_CHANNEL_G) && !(_Flags & FLAGS_CHANNEL_B) && _Flags & FLAGS_CHANNEL_A) { output.xyz = imgColor.w; output.w = 1; } if (_Flags & FLAGS_EnableCheckerBackground) { float2 channels = saturate(fmod(floor(v2f.Position.xy / _GridSize), 2)); float crossArea = 1-saturate(channels.x + channels.y); float blackArea = channels.x * channels.y; float4 checker = lerp(_CheckerColorA, _CheckerColorB, crossArea + blackArea); output = lerp(checker, output, output.a); } else { output = float4(lerp(0, output.xyz, output.w), 1); } if (_Flags & FLAGS_GAMMA) { output = float4(pow(output.xyz, 1/2.2), output.w); } return output; }