Problem with order-independent transparency

hi,

i’ve created an OIT renderer. all looks fine so far, it appears correctly onscreen. he problem appears when i zoom into the “cube of cubes”, that means when some vetices are clipped away. then a part of the scene just disappears.

the complete code can be found here:

the rendering part:

    // render
    //----------------------------------------
    CheckForGLErrors;

    // initialize fragment start index texture (each r32ui texel = 0xFFFFFFFF)
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, fragmentstartindexinitializerbuffer);
    glTextureSubImage2D(
        fragmentstartindextexture, 
        0, 0, 0, framebuffersize.x, framebuffersize.y,
        GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

    // initialize fragment index buffer
    uint atomic_fragment_index = 0;
    glNamedBufferSubData(fragmentindexbuffer, 0, sizeof(uint), &atomic_fragment_index);

    //glEnable(GL_DEPTH_TEST);
    //glEnable(GL_CULL_FACE);

    // first render pass: collect fragments into buffer
    glUseProgram(program_collect_fragments);
    glBindVertexArray(vertexarray);

    glUniformMatrix4fv(0, 1, false, value_ptr(projection));
    glUniform3fv(4, 1, value_ptr(light_intensity));
    glUniform3fv(5, 1, value_ptr(light_direction_eyespace));
    glUniform1f(6, light_ambient);

    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectbuffer);

    // one draw call, rendering all the draw commands contained in "indirectbuffer"
    glMultiDrawElementsIndirect(
        GL_TRIANGLES, 
        GL_UNSIGNED_INT, 
        nullptr, 
        instance_count, 
        sizeof(DrawElementsIndirectCommand));

    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);

    glBindVertexArray(0);
    glUseProgram(0);
    
    //glDisable(GL_DEPTH_TEST);
    //glDisable(GL_CULL_FACE);
    //----------------------------------------


    // second render pass:
    // render screen rectangle, present collected fragments in correct order
    //----------------------------------------
    glMemoryBarrier(GL_ALL_BARRIER_BITS); 

    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glUseProgram(program_present_fragments);
    glBindVertexArray(vertexarray);

    vec4 clear_color = { 0.2f, 0.3f, 0.4f, 0.0f };
    glUniform4fv(0, 1, value_ptr(clear_color));

    auto screen_rectangle = mesh_references.at(0);

    glDrawElementsBaseVertex(
        GL_TRIANGLES, 
        screen_rectangle.IndexCount,
        GL_UNSIGNED_INT, 
        reinterpret_cast<GLvoid*>(sizeof(GLuint) * screen_rectangle.BaseIndex),
        screen_rectangle.BaseVertex);

    glBindVertexArray(0);
    glUseProgram(0);
    //----------------------------------------

the shaders:

phase 1: collecting the fragments:
vertexshader

#version 460 core

// camera perspective
layout (location = 0) uniform mat4 projection = mat4(1);

// per-vertex attributes
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec3 in_normal;
layout (location = 2) in vec2 in_texcoord;

// per-instance attributes
layout (location = 3) in mat4 in_mv;

// interface vertex shader --> fragment shader
out VS_FS {
	smooth vec3 position;
	smooth vec3 normal;
	smooth vec2 texcoord;
	flat uint drawID;
} vs_out;

void main()
{
	// vertex position
	gl_Position = projection * in_mv * vec4(in_position, 1);

	// position and normal in eye-space
	vs_out.position = (in_mv * vec4(in_position, 1)).xyz;
	vs_out.normal = (in_mv * vec4(in_normal, 0)).xyz;

	// texture coordinate
	vs_out.texcoord = in_texcoord;

	// draw command index [ 0 ; instancecount )
	vs_out.drawID = gl_DrawID;
}

fragment shader:

#version 460 core

#extension GL_ARB_bindless_texture : enable

layout (early_fragment_tests) in;

struct Fragment {
	uint NextIndex;
	uint Color;
	float Depth;
	float _pad;
};

// material definition
struct Material {
	uint Kd;
	uint Ks;
	float Ns;
	float d;
	uint MapKd;
};

// array containing all materials
layout (std430, binding = 1) buffer MaterialBlock {
	Material materials[];
};

// array containing material index for current draw command (size = instancecount)
layout (std430, binding = 2) buffer MaterialIndicesBlock {
	uint material_indices[];
};

// array containing texture handles
layout (std430, binding = 3) buffer TextureHandleBlock {
	uvec2 texture_handles[];
};

// array containing fragments
layout (std430, binding = 4) buffer FragmentBlock {
	Fragment fragments[];
};

// fragment index to array of fragments
layout (binding = 1) uniform atomic_uint atomic_fragment_index;

// start index to array of fragments
layout (binding = 1, r32ui) uniform coherent uimage2D fragment_start_indices;

// light parameters
layout (location = 4) uniform vec3 light_intensity = vec3(0, 0, 0);
layout (location = 5) uniform vec3 light_direction = vec3(0, 0, 0);
layout (location = 6) uniform float light_ambient = 0.0f;

// interface vertex shader --> fragment shader
in VS_FS {
	smooth vec3 position;
	smooth vec3 normal;
	smooth vec2 texcoord;
	flat uint drawID;
} fs_in;

// fragment color
//layout (location = 0) out vec4 out_color;


vec4 ShadeFragment()
{
	// get material index
	uint material_index = material_indices[fs_in.drawID];

	// get material data
	vec3 Kd = unpackUnorm4x8(materials[material_index].Kd).rgb;
	vec3 Ks = unpackUnorm4x8(materials[material_index].Ks).rgb;
	float Ns = clamp(materials[material_index].Ns, 1, 1000);
	float d = clamp(materials[material_index].d, 0, 1);

	// get texture data
	sampler2D MapKd = sampler2D(texture_handles[ materials[material_index].MapKd ]);
	Kd = Kd * texture(MapKd, fs_in.texcoord).rgb;

	// directions (eye-space)
	vec3 N = normalize(fs_in.normal);
	vec3 L = normalize(-light_direction);
	vec3 R = reflect(-L, N);
	vec3 V = normalize(-fs_in.position);

	// light data
	vec3 Ia = light_intensity * clamp(light_ambient, 0, 1);
	vec3 Id = light_intensity * max(0, dot(N, L));
	vec3 Is = light_intensity * max(0, pow(max(0, dot(R, V)), Ns));

	// color
	vec3 color = (Ia + Id) * Kd + Is * Ks;

	return vec4(color, d);
}


void main()
{
	// fragment color
	vec4 color = ShadeFragment();

	// fragment index
	uint fragment_index = atomicCounterAdd(atomic_fragment_index, 1);

	// create fragment
	Fragment fragment;
	fragment.NextIndex = imageAtomicExchange(fragment_start_indices, ivec2(gl_FragCoord.xy), fragment_index);
	fragment.Color = packUnorm4x8(color);
	fragment.Depth = gl_FragCoord.z;
	fragment._pad = 0; // unused

	// save fragment in fragment buffer
	fragments[fragment_index] = fragment;
}

phase 2: sorting and blending the fragments
(screen-wide rectangle is rendered)

vertexshader:

#version 460 core

// per-vertex attributes
layout (location = 0) in vec3 in_position;

void main()
{
	// vertex position
	gl_Position = vec4(in_position, 1);
}

fragmentshader:

#version 460 core

struct Fragment {
	uint NextIndex;
	uint Color;
	float Depth;
	float _pad;
};


#define MAX_FRAGMENTS 30

Fragment local_fragments[MAX_FRAGMENTS];


// array containing fragments
layout (std430, binding = 4) buffer FragmentBlock {
	Fragment fragments[];
};

// fragment index to array of fragments
//layout (binding = 1) uniform atomic_uint atomic_fragment_index;

// start index to array of fragments
layout (binding = 1, r32ui) uniform coherent uimage2D fragment_start_indices;

// clear color
layout (location = 0) uniform vec4 clear_color = vec4(0, 0, 0, 0);

// fragment color
layout (location = 0) out vec4 out_color;


uint BuildFragmentArray()
{
	uint fragment_count = 0;
	
	uint fragment_index = imageLoad(fragment_start_indices, ivec2(gl_FragCoord.xy)).r;

	while (fragment_count < MAX_FRAGMENTS && fragment_index < fragments.length())
	{
		Fragment fragment = fragments[fragment_index];
		local_fragments[fragment_count] = fragment;
		fragment_index = fragment.NextIndex;
		fragment_count++;
	}

	return fragment_count;
}


void SortFragmentsByDepth(uint fragment_count)
{
	for (uint i = 0; i < fragment_count - 1; i++)
	{
		for (uint j = i + 1; j < fragment_count; j++)
		{
			if (local_fragments[i].Depth < local_fragments[j].Depth)
			{
				Fragment swap = local_fragments[i];
				local_fragments[i] = local_fragments[j];
				local_fragments[j] = swap;
			}
		}
	}
}


vec4 BlendFragments(uint fragment_count)
{
	vec4 destination_color = clear_color;

	for (uint i = 0; i < fragment_count; i++)
	{
		vec4 source_color = unpackUnorm4x8(local_fragments[i].Color);
		destination_color = mix(destination_color, source_color, source_color.a);
	}

	return destination_color;
}


void main()
{
	// get local fragments
	uint fragment_count = BuildFragmentArray();
	if (fragment_count > 0)
	{
		// sort local fragments
		SortFragmentsByDepth(fragment_count);

		// calculate color
		out_color = BlendFragments(fragment_count);
	}
	else
		// no fragments here, set clear color
		out_color = clear_color;
}

what i can say is:
if i ask how many fragments have been collected at these spots that are not being rendered correctly, it says “0 ragments”. either the “collect shader” isnt doing its job correctly or the “present shader” doesnt get the fragments somehow … :thinking:

Have you examined the value of the atomic counter after the first pass? If the value drops suddenly, then the first pass isn’t collecting the fragments. If it doesn’t, the second pass isn’t seeing the collected fragments. Try adding a mode which writes a colour based upon the fragment count (from BuildFragmentArray). In particular, check whether the MAX_FRAGMENTS limit is being reached.

i changed the last fragmentshader so that it displays the number of fragments under the cursor (i bound an additional SSBO “debugdata”)

but … thank you VERY MUCH. i think i got the problem: the fragment buffer is 5 times the pixelcount of the framebuffer. closing into the 11 x 11 x 11 cubes means that approximately the full screen is used, indicating that i might have not allocated enough memory for the collected fragments …

i will check that. thanks again :+1:

EDIT: checked it … it was the problem, so i think it works as it should, i’ll improve the code a bit later.

:+1: