获取OIT中的像素标记内的MSAA样本编号

时间:2016-07-28 12:02:05

标签: hlsl direct3d11 msaa

我尝试自己实现订单独立透明度。这就像完成没有一件事......正如你在下面的图片中看到的那样,带有MSAA的OIT是某种错误的。我认为这是因为样本。因为在每个三角形边缘有4个样本(并且只在三角形边缘)。

使用和不使用MSAA的Alphablending和OIT

Alphablending and OIT with and without MSAA

这里也是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);
}

我的想法是在列表中编写样本编号,最后在渲染结束图时,我比较列表节点,如果它们要靠近,我想检查样本编号并计算平均颜色。 但我不知道如何获得aktual样本号...

BTW:有人有更好的理想来修复这个bug吗?它不需要是快速计算,我不会实时渲染。

1 个答案:

答案 0 :(得分:1)

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

有了它,你可以根据覆盖范围累积成N个值(N为MSAA Nx)来解决透明度(和一次性msaa),然后是平均值和输出。

如果要在解析之前输入msaa表面,则必须使用计算着色器才能进行累积,然后单独写入N值。

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