获取 OIT 像素着色器内的 MSAA 样本编号

Get the MSAA sample number inside pixelshader for OIT

我尝试自己实施独立于顺序的透明度。就像没有一件事就完成了......正如您在下图中看到的那样,带有 MSAA 的 OIT 有点错误。我认为这是因为样本。因为在每个三角形边缘有 4 个样本(并且仅在三角形边缘)。

使用和不使用 MSAA 的 Alphablending 和 OIT

这里还有 HLSL 中的着色器代码:

创建列表

RWByteAddressBuffer tRWFragmentList : register(u1);

void main(PS_INPUT input)
{
    float2 position = (input.Pos.xy - float2(0.5,0.5)) / input.Pos.w;

    uint nXPosition = position.x;
    uint nYPosition = position.y;

    uint vScreenAddress = nScreenWidth * nYPosition + nXPosition;

    float3 Normal = normalize((float3)input.Normal);
    float3 Position = (float3)input.Pos;

    float4 Color = createphong(input);
    //float4 Color = (float4)input.Diffuse;

    // Get counter value and increment
    uint nNewFragmentAddress = 0;
    tRWFragmentList.InterlockedAdd(0, 44, nNewFragmentAddress);

    if (nNewFragmentAddress < 1000*1000*500)
    {
        uint pixel = 4 + nScreenWidth * nScreenHeight * 4 + nNewFragmentAddress;

        tRWFragmentList.Store(pixel + 4, asuint(Position.x));
        tRWFragmentList.Store(pixel + 8, asuint(Position.y));
        tRWFragmentList.Store(pixel + 12, asuint(Position.z));
        tRWFragmentList.Store(pixel + 16, asuint(Normal.x));
        tRWFragmentList.Store(pixel + 20, asuint(Normal.y));
        tRWFragmentList.Store(pixel + 24, asuint(Normal.z));
        tRWFragmentList.Store(pixel + 28, asuint(Color.r));
        tRWFragmentList.Store(pixel + 32, asuint(Color.g));
        tRWFragmentList.Store(pixel + 36, asuint(Color.b));
        tRWFragmentList.Store(pixel + 40, asuint(Color.a));

        uint output = 0;
        tRWFragmentList.InterlockedExchange(vScreenAddress * 4 + 4, pixel, output);
        tRWFragmentList.Store(pixel, output);
    }
}

对列表进行排序

RWByteAddressBuffer tRWFragmentList : register(u1);

float4 main(PS_INPUT input) : SV_Target
{
    float2 position = (input.Pos.xy - float2(0.5,0.5)) / input.Pos.w;

    uint nXPosition = position.x;
    uint nYPosition = position.y;

    uint vScreenAddress = 4+(nScreenWidth * nYPosition + nXPosition) * 4;

    if (tRWFragmentList.Load(vScreenAddress) != 0)
    {
        uint i = vScreenAddress;
        uint j = vScreenAddress;
        float zMin = 0;
        uint zMinPrev = i;

        do
        {
            i = j;
            zMin = asfloat(tRWFragmentList.Load(tRWFragmentList.Load(i) + 12));
            zMinPrev = i;
            do
            {
                if (asfloat(tRWFragmentList.Load(tRWFragmentList.Load(i) + 12)) > zMin)
                {
                    zMin = asfloat(tRWFragmentList.Load(tRWFragmentList.Load(i) + 12));
                    zMinPrev = i;
                }
                i = tRWFragmentList.Load(i);
            }
            while (tRWFragmentList.Load(i) > 0);

            //check swap
            if (zMinPrev != j)
            {
                uint trwJ = tRWFragmentList.Load(j);
                uint trwtrwMin = tRWFragmentList.Load(tRWFragmentList.Load(zMinPrev));
                uint trwMin = tRWFragmentList.Load(zMinPrev);
                tRWFragmentList.Store(j,trwMin);
                tRWFragmentList.Store(zMinPrev,trwtrwMin);
                tRWFragmentList.Store(trwMin,trwJ);
            }

            j = tRWFragmentList.Load(j);
        }
        while (tRWFragmentList.Load(j) > 0);
    }
    return float4(1, 0, 1, 1);
}

渲染完成图

RWByteAddressBuffer tRWFragmentList  : register(u1);

float4 main(PS_INPUT input) : SV_Target
{
    float2 position = (input.Pos.xy - float2(0.5,0.5)) / input.Pos.w;

    uint nXPosition = position.x;
    uint nYPosition = position.y;

    uint vScreenAddress = nScreenWidth * nYPosition + nXPosition;

    float3 Color = float3(0.5, 0.5, 0.5);
    uint nScreenAdress = vScreenAddress*4+4;

    while (tRWFragmentList.Load(nScreenAdress) != 0)
    {
        nScreenAdress = tRWFragmentList.Load(nScreenAdress);

        float4 NewColor = float4(asfloat(tRWFragmentList.Load(nScreenAdress + 28)),
                                 asfloat(tRWFragmentList.Load(nScreenAdress + 32)),
                                 asfloat(tRWFragmentList.Load(nScreenAdress + 36)),
                                 asfloat(tRWFragmentList.Load(nScreenAdress + 40)));

        float fZValue = asfloat(tRWFragmentList.Load(nScreenAdress + 12));
        Color = NewColor.a * NewColor.rgb + (1 - NewColor.a) * Color.rgb;               
    }
    tRWFragmentList.Store(vScreenAddress * 4 + 4, 0);
    if (nXPosition == 0 && nYPosition)
    {
        tRWFragmentList.Store(0, 0);
    }
    return float4(Color.r, Color.g, Color.b, 1);
}

我的想法是把样本数写在列表里面,最后渲染完图的时候,我比较列表节点,如果它们靠在一起我想检查样本数并计算平均值颜色。 但是我不知道如何得到实际的样本数...

顺便说一句:有人有更好的主意来修复这个错误吗?不需要快速计算,我不实时渲染。

您必须使用 sv_coverage 读取触摸片段的像素着色器中的遮罩。

有了它,您可以根据覆盖率累积 N 个值(N 为 MSAA Nx),然后取平均值并输出,从而解决透明度(和 msaa 一次性)。

如果你想在msaa表面输出而不是在解析之前,你必须使用计算着色器才能进行一次累加然后分别写入N个值。

除了实际的网格渲染之外,我会计算所有内容,对于这种处理,它比像素着色器更方便