Получить номер образца MSAA внутри пиксельного шейдера для OIT

Я пытаюсь реализовать независимую прозрачность заказов самостоятельно. Это как закончено без единой вещи... Как вы можете видеть на картинке ниже, OIT с MSAA является своего рода неправильным. Я думаю, что это из-за образцов. потому что на каждом ребре треугольника есть 4 образца (и только на краях треугольника).

Alphablending и OIT с и без MSAA

Alphablending и OIT с и без 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);
}

Моя идея состоит в том, чтобы записать номер образца в список, и в конце, когда я отображаю конечную картинку, я сравниваю узлы списка, и если они должны быть рядом, я хочу проверить номер образца и вычислить средний цвет. Но я не знаю, как получить актуальный номер образца...

Кстати: у кого-нибудь есть лучший идеал, чтобы исправить эту ошибку? Это не должен быть быстрый расчет, я не рендеринг в режиме реального времени.

1 ответ

Решение

Вы должны использовать sv_coverage прочитать маску в пиксельном шейдере затронутых фрагментов.

С его помощью вы разрешаете прозрачность (и msaa за один раз) путем накопления в N значений ( N как MSAA Nx) в соответствии с покрытием, затем усреднением и выводом.

Если вы хотите вывести на поверхность msaa вместо разрешения, вам нужно использовать вычислительный шейдер, чтобы иметь возможность выполнить накопление, а затем записать N значений отдельно.

Я бы пошел вычислить для всего, кроме фактического рендеринга сетки, это более удобно, чем пиксельный шейдер для такого рода обработки

Другие вопросы по тегам