3

我已经根据“OpenGL 编程指南”第 8 版中的演示实现了 OIT。(红皮书)。现在我需要添加 MSAA。只需启用 MSAA 就会破坏透明度,因为分层像素被解析 x 次等于样本级别。我已经阅读了这篇关于如何使用 DirectX 完成的文章,他们说像素着色器应该按样本而不是按像素运行。它是如何在 OpenGL 中完成的。

我不会在这里介绍整个实现,而是在其中出现分层像素的最终分辨率的片段着色器块:

vec4 final_color = vec4(0,0,0,0);
for (i = 0; i < fragment_count; i++)
{
    /// Retrieving the next fragment from the stack:
    vec4 modulator = unpackUnorm4x8(fragment_list[i].y) ;
    /// Perform alpha blending:
    final_color =   mix(final_color, modulator, modulator.a);
}

color = final_color ;

更新:

我已经尝试过这里提出的解决方案,但它仍然不起作用。这是列表构建和解析通道的完整片段着色器:

列出构建通行证:

#version 420 core
layout (early_fragment_tests) in;
layout (binding = 0, r32ui) uniform uimage2D head_pointer_image;
layout (binding = 1, rgba32ui) uniform writeonly uimageBuffer list_buffer;
layout (binding = 0, offset = 0) uniform atomic_uint list_counter;
layout (location = 0) out vec4 color;//dummy output

in vec3 frag_position;
in vec3 frag_normal;
in vec4 surface_color;
in int gl_SampleMaskIn[];
uniform vec3 light_position = vec3(40.0, 20.0, 100.0);

void main(void)
{
    uint index;
    uint old_head;
    uvec4 item;
    vec4 frag_color;
    index = atomicCounterIncrement(list_counter);
    old_head = imageAtomicExchange(head_pointer_image, ivec2(gl_FragCoord.xy), uint(index));

    vec4 modulator =surface_color;
    item.x = old_head;
    item.y = packUnorm4x8(modulator);
    item.z = floatBitsToUint(gl_FragCoord.z);
    item.w = int(gl_SampleMaskIn[0]);
    imageStore(list_buffer, int(index), item);
    frag_color = modulator;
    color = frag_color;
}

列表解析:

#version 420 core
// The per-pixel image containing the head pointers
layout (binding = 0, r32ui) uniform uimage2D head_pointer_image;
// Buffer containing linked lists of fragments
layout (binding = 1, rgba32ui) uniform uimageBuffer list_buffer;
// This is the output color
layout (location = 0) out vec4 color;
// This is the maximum number of overlapping fragments allowed
#define MAX_FRAGMENTS 40

// Temporary array used for sorting fragments
uvec4 fragment_list[MAX_FRAGMENTS];

void main(void)
{
    uint current_index;
    uint fragment_count = 0;
    current_index = imageLoad(head_pointer_image, ivec2(gl_FragCoord).xy).x;

    while (current_index != 0 && fragment_count < MAX_FRAGMENTS )
    {   
        uvec4 fragment = imageLoad(list_buffer, int(current_index));
        int coverage = int(fragment.w);
        //if((coverage &(1 << gl_SampleID))!=0) {

            fragment_list[fragment_count] = fragment;
            current_index = fragment.x;

        //}

        fragment_count++;
    }

    uint i, j;

    if (fragment_count > 1)
    {
        for (i = 0; i < fragment_count - 1; i++)
        {
            for (j = i + 1; j < fragment_count; j++)
            {
                uvec4 fragment1 = fragment_list[i];
                uvec4 fragment2 = fragment_list[j];

                float depth1 = uintBitsToFloat(fragment1.z);
                float depth2 = uintBitsToFloat(fragment2.z);

                if (depth1 < depth2)
                {
                    fragment_list[i] = fragment2;
                    fragment_list[j] = fragment1;
                }
            }
        }
    }

    vec4 final_color = vec4(0,0,0,0);

    for (i = 0; i < fragment_count; i++)
    {  
        vec4 modulator = unpackUnorm4x8(fragment_list[i].y);
        final_color =  mix(final_color, modulator, modulator.a);      
    }

    color = final_color;
}
4

2 回答 2

3

在不知道您的代码实际如何工作的情况下,您可以使用与链接的 DX11 演示相同的方式进行操作,因为 OpenGL 提供了所需的相同功能。

因此,在仅存储所有渲染片段的第一个着色器中,您还存储了每个片段的样本覆盖掩码(当然还有颜色和深度)。这是作为片段着色器输入变量给出的int gl_SampleMaskIn[],对于每个带有 id 的样本,如果片段覆盖该样本,则设置32*i+j位(因为您可能不会使用 >32xMSAA,您通常可以只使用并且只需要存储一个作为覆盖范围面具)。jglSampleMaskIn[i]glSampleMaskIn[0]int

...
fragment.color = inColor;
fragment.depth = gl_FragCoord.z;
fragment.coverage = gl_SampleMaskIn[0];
...

然后为每个样本运行最终的排序和渲染着色器,而不仅仅是为每个片段运行。这是通过使用输入变量隐式实现的int gl_SampleID,它为我们提供了当前样本的 ID。所以我们在这个着色器中所做的(除了非 MSAA 版本)是排序步骤只考虑样本,如果当前样本实际被覆盖,则仅将一个片段添加到最终(待排序)片段列表中通过这个片段:

是什么样的(注意,从您的小片段和 DX-link 推断出的伪代码):

while(fragment.next != 0xFFFFFFFF)
{
    fragment_list[count++] = vec2(fragment.depth, fragment.color);
    fragment = fragments[fragment.next];
}

就是现在

while(fragment.next != 0xFFFFFFFF)
{
    if(fragment.coverage & (1 << gl_SampleID))
        fragment_list[count++] = vec2(fragment.depth, fragment.color);
    fragment = fragments[fragment.next];
}

或类似的规定。

编辑:对于您更新的代码,您必须fragment_count仅在if(covered)块内递增,因为如果未覆盖示例,我们不想将片段添加到列表中。始终增加它可能会导致您在边缘看到伪影,这是 MSAA(以及覆盖范围)发挥作用的区域。

另一方面,列表指针必须current_index = fragment.x在每次循环迭代中被转发(),而不仅仅是在样本被覆盖的情况下,否则它可能会导致无限循环,就像你的情况一样。所以你的代码应该是这样的:

while (current_index != 0 && fragment_count < MAX_FRAGMENTS )
{
    uvec4 fragment = imageLoad(list_buffer, int(current_index));
    uint coverage = fragment.w;
    if((coverage &(1 << gl_SampleID))!=0)
        fragment_list[fragment_count++] = fragment;
    current_index = fragment.x;
}
于 2013-06-05T09:46:12.237 回答
1

OpenGL 4.3 Spec在 7.1 中声明了关于gl_SampleID内置变量:

在片段着色器中对该变量的任何静态使用都会导致对每个样本评估整个着色器。

(在 ARB_sample_shading 中已经是这种情况,对于gl_SamplePosition使用限定符声明的自定义变量也是如此sample

因此它是非常自动的,因为无论如何您都可能需要 SampleID。

于 2013-06-05T08:54:51.093 回答