How to do it...

With all of the buffers set up, we need two render passes. Before the first pass, we want to clear our buffers to default values (that is, empty lists), and to reset our atomic counter buffer to zero:

glBindBuffer(GL_PIXEL_UNPACK_BUFFER, clearBuf); 
glBindTexture(GL_TEXTURE_2D, headPtrTex); 
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, 
        GL_RED_INTEGER, GL_UNSIGNED_INT, NULL); 
GLuint zero = 0; 
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, counterBuffer); 
glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), &zero);

In the first pass, we'll render the full scene geometry. Generally, we should render all the opaque geometry first and store the results in a texture. However, we'll skip that step for this example to keep things simple and focused. Instead, we'll render only transparent geometry. When rendering the transparent geometry, we need to make sure to put the depth buffer in read-only mode (use glDepthMask). In the fragment shader, we add each fragment to the appropriate linked list:

layout (early_fragment_tests) in; 
 
#define MAX_FRAGMENTS 75 
 
in vec3 Position; 
in vec3 Normal; 
 
struct NodeType { 
 vec4 color; 
 float depth; 
 uint next; 
}; 
 
layout(binding=0, r32ui) uniform uimage2D headPointers; 
layout(binding=0, offset=0) uniform atomic_uint 
                     nextNodeCounter; 
layout(binding=0, std430) buffer linkedLists { 
 NodeType nodes[]; 
}; 
uniform uint MaxNodes; 
 
subroutine void RenderPassType(); 
subroutine uniform RenderPassType RenderPass; 
 
... 
 
subroutine(RenderPassType) 
void pass1() 
{ 
 // Get the index of the next empty slot in the buffer 
 uint nodeIdx = atomicCounterIncrement(nextNodeCounter); 
 
 // Is there space left in the buffer? 
 if( nodeIdx < MaxNodes ) { 
  // Update the head pointer image 
  uint prevHead = imageAtomicExchange(headPointers, 
              ivec2(gl_FragCoord.xy), nodeIdx); 
 
  // Set the color and depth of this new node to the color 
  // and depth of the fragment. The next pointer points to the 
  // previous head of the list. 
  nodes[nodeIdx].color = vec4(shadeFragment(), Kd.a); 
  nodes[nodeIdx].depth = gl_FragCoord.z; 
  nodes[nodeIdx].next = prevHead; 
 } 
} 

Before rendering the second pass, we need to be sure that all of the data has been written to our buffers. In order to ensure that is indeed the case, we can use a memory barrier:

glMemoryBarrier( GL_ALL_BARRIER_BITS ); 

In the second pass, we don't render the scene geometry, just a single, screen-filling quad in order to invoke the fragment shader for each screen pixel. In the fragment shader, we start by copying the linked list for the fragment into a temporary array:

struct NodeType frags[MAX_FRAGMENTS]; 
int count = 0; 
 
// Get the index of the head of the list 
uint n = imageLoad(headPointers, ivec2(gl_FragCoord.xy)).r; 
 
// Copy the linked list for this fragment into an array 
while( n != 0xffffffff && count < MAX_FRAGMENTS) { 
 frags[count] = nodes[n]; 
 n = frags[count].next; 
 count++; 
} 

Then, we sort the fragments using insertion sort:

// Sort the array by depth (largest to smallest). 
for( uint i = 1; i < count; i++ ) 
{ 
 struct NodeType toInsert = frags[i]; 
 uint j = i; 
 while( j > 0 && toInsert.depth > frags[j-1].depth ) { 
  frags[j] = frags[j-1]; 
  j--; 
 } 
 frags[j] = toInsert; 
}

Finally, we blend the fragments manually, and send the result to the output variable:

// Traverse the array, and blend the colors. 
vec4 color = vec4(0.5, 0.5, 0.5, 1.0); // Background color 
for( int i = 0; i < count; i++ ) { 
 color = mix( color, frags[i].color, frags[i].color.a); 
} 
  
// Output the final color 
FragColor = color; 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.12.240