[DirectX 10.1+] 一种定制化的alpha-to-coverage实现

alpha-to-coverage是我个人非常钟爱的一种渲染技术,因为它可以用比较低的编程成本以及GPU成本实现类似顺序无关的透明(OIT)渲染。

最典型的使用alpha-to-coverage的例子就是《最终幻想13》三部曲了。

在PS3/Xbox 360上,由于只有MSAA 2x,它的颗粒感是很明显的,但PC版上,由于有了MSAA 8x,因此有了明显的效果。

具体的实现方式可以从DirectX SDK的例子 TransparencyAA就可以了解一二。(顺便说一下,这个例子本来是为了推广TransparencyAA的,但这种渲染技巧对毛发或者叶子边缘的蓬松感的表现没有什么好处,因为TransparencyAA本质上还是一种alpha test的改进形式。)

不过缺省的alpha-to-coverage是一种完全没有随机性的过程,因此它会在alpha值变化的地方产生类似地形图那样的条纹:

为了解决这个问题,humus.name网站的大牛(点击打开链接)提出了使用伪随机函数在pixel shader里改写SV_Coverage,从而改进alpha-to-coverage效果的尝试。但大牛没有留下参考用的shader代码。

因此我就做了点小尝试,去实现大牛提出的效果。实验结果截图和参考shader会贴在后面。


效果比第一张图的要好多了。这个是MSAA 4x下的截图,所以颗粒感还是有一些,但只要不放大了看还是很可以接受的。

Shader实现如下:


// 根据屏幕坐标得到一个伪随机值。由于屏幕坐标是固定的,因此伪随机值不会导致闪烁
float rand_1_05(in float2 uv)
{
    float2 noise = (frac(sin(dot(uv, float2(12.9898, 78.233)*2.0)) * 43758.5453));
        return abs(noise.x + noise.y) * 0.5;
}

struct PsOutput
{
    float4 v4Color        : SV_Target;
    uint uCoverageMask    : SV_Coverage;
};

PsOutput PsCustomAlphaToCoverage(PsInput I)
{
    PsOutput output;
    // Lookup alpha texture
    float4 v4AlphaSample = g_AlphaTexture.Sample( g_AlphaTextureSampler, I.v2TexCoord );
    
    // Modulate by diffuse lighting value
    output.v4Color = v4AlphaSample * I.v4Diffuse;

    // 如果是普通的alpha to coverage,在这里就可以返回,也不需要输出SV_Coverage。
    // 另外记得定制的alpha to coverage不能打开Blend state里的AlphaToCoverageEnabled选项。
   
    // 根据alpha算出可以写多少个MSAA sample
    float alpha = clamp(output.v4Color.a * MSAA_SAMPLES, 0.0f, MSAA_SAMPLES);

    // 如果alpha值太低,则直接不输出任何MSAA sample
    if (alpha < 0.2f)
        output.uCoverageMask = 0;
    // 如果alpha值太高,则输出全部MSAA sample
    else if (alpha > MSAA_SAMPLES - 0.2f)
    {
        output.uCoverageMask = (uint(1) << MSAA_SAMPLES) - 1;
    }
    else
    {
        // 其余情况则根据alpha的范围决定。
        // base和base+1为需要二择一的两组coverage mask。fraction为选择base+1的概率。
        float fraction = frac(alpha);
        uint base = floor(alpha);

        float randValue = rand_1_05(I.v4Pos.xy);

        // 根据概率决定该屏幕像素的coverage mask选择的是base还是base+1。
        if (randValue < fraction)
            base++;
        output.uCoverageMask = (uint(1) << base) - 1;
    }
    return output;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值