在制作纹理资产查看器时总结的一些思路和逻辑。

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;
}