我已经根据“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 ;
Run Code Online (Sandbox Code Playgroud)
更新:
我已经尝试了此处提出的解决方案,但它仍然不起作用。以下是列表构建和解析过程的完整片段着色器:
列出构建过程:
#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;
}
Run Code Online (Sandbox Code Playgroud)
列表解析:
#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;
}
Run Code Online (Sandbox Code Playgroud)
在不知道代码实际工作原理的情况下,您可以按照链接的 DX11 演示的方式进行操作,因为 OpenGL 提供了所需的相同功能。
因此,在仅存储所有渲染片段的第一个着色器中,您还可以存储每个片段的示例覆盖蒙版(当然还有颜色和深度)。这是作为片段着色器输入变量给出的int gl_SampleMaskIn[],对于每个具有 id 的样本32*i+j,如果片段覆盖该样本,则设置位j(glSampleMaskIn[i]因为您可能不会使用 >32xMSAA,您通常可以只使用glSampleMaskIn[0]并且只需要存储单个作为int覆盖范围)面具)。
...
fragment.color = inColor;
fragment.depth = gl_FragCoord.z;
fragment.coverage = gl_SampleMaskIn[0];
...
Run Code Online (Sandbox Code Playgroud)
然后,最终的排序和渲染着色器针对每个样本运行,而不仅仅是每个片段。这是通过使用输入变量 隐式实现的int gl_SampleID,该变量为我们提供了当前样本的 ID。因此,我们在此着色器中所做的(除了非 MSAA 版本之外)是,排序步骤仅考虑样本,如果当前样本实际上已被覆盖,则仅将片段添加到最终(要排序的)片段列表中通过这个片段:
是什么样的(注意,从你的小片段和 DX 链接推断出的伪代码):
while(fragment.next != 0xFFFFFFFF)
{
fragment_list[count++] = vec2(fragment.depth, fragment.color);
fragment = fragments[fragment.next];
}
Run Code Online (Sandbox Code Playgroud)
就是现在
while(fragment.next != 0xFFFFFFFF)
{
if(fragment.coverage & (1 << gl_SampleID))
fragment_list[count++] = vec2(fragment.depth, fragment.color);
fragment = fragments[fragment.next];
}
Run Code Online (Sandbox Code Playgroud)
或类似的规定。
编辑:对于更新的代码,您必须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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2825 次 |
| 最近记录: |