2. b) Transparency, optimised

First all opaque objects are rendered as usual, then all transparent objects using a fragment collecting program with depth testing enabled. That way, unnecessary invisible fragments (hidden behind opaque objects) are not collected, with means we dont waste memory for them. Finally, a screen-wide rectangle is rendered, without depth testing but with blending enabled, all the transparent fragments collected previously are sorted, blended and rendered to the scene.

Main.cpp

#define GLEW_STATIC

#include <GL/glew.h>

#include <GLFW/glfw3.h>

#include <iostream>

#include <vector>

#include <map>

#include "Graphics.h"

using namespace std;

using namespace glm;

struct DebugUserParams {

bool ShowNotifications = true;

bool ShowSameMessagesOnlyOnce = true;

map<GLuint, bool> CachedMessageIDs;

} debuguserparams;

bool turn_camera = true;

Camera camera;

void error_callback(int error, const char* description)

{

cout << "GLFW Error: " << description << endl;

cin.get();

}

void drop_callback(GLFWwindow* window, int pathcount, const char** paths)

{

for (unsigned int i = 0; i < pathcount; i++)

cout << "dropped file: " << paths[i] << endl;

}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)

{

Graphics::ResizeFramebuffer(ivec2(width, height));

}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)

{

if (action == GLFW_PRESS)

{

if (key == GLFW_KEY_T)

{

turn_camera = !turn_camera;

cout << "camera turn: " << (turn_camera ? "on" : "off") << endl;

}

}

}

void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)

{

static double xpos_prev = xpos;

static double ypos_prev = ypos;

double dx = xpos - xpos_prev;

double dy = ypos - ypos_prev;

xpos_prev = xpos;

ypos_prev = ypos;

if (turn_camera)

{

camera.Theta = camera.Theta + 0.01f * float(dx);

camera.Phi = clamp(camera.Phi + 0.01f * float(dy), -1.5f, +1.5f);

}

}

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)

{

if (action == GLFW_PRESS)

{

if (button == GLFW_MOUSE_BUTTON_RIGHT)

{

}

}

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)

{

camera.Distance = clamp(camera.Distance + 0.3f * float(yoffset), 1.0f, 20.0f);

}

/* debug */

void APIENTRY OpenGLDebugCallback(

GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userparam)

{

/* debug user params */

DebugUserParams* params = (DebugUserParams*)userparam;

if (params->CachedMessageIDs.find(id) != params->CachedMessageIDs.end())

if (params->ShowSameMessagesOnlyOnce)

return;

params->CachedMessageIDs[id] = true;

/* filter out unnecessary warnings */

if (!params->ShowNotifications)

if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)

return;

/* source */

string str_source;

if (source == GL_DEBUG_SOURCE_API) str_source = "API";

if (source == GL_DEBUG_SOURCE_WINDOW_SYSTEM) str_source = "Window System";

if (source == GL_DEBUG_SOURCE_SHADER_COMPILER) str_source = "Shader Compiler";

if (source == GL_DEBUG_SOURCE_THIRD_PARTY) str_source = "Third Party";

if (source == GL_DEBUG_SOURCE_APPLICATION) str_source = "Application";

if (source == GL_DEBUG_SOURCE_OTHER) str_source = "Other";

/* type */

string str_type;

if (type == GL_DEBUG_TYPE_ERROR) str_type = "Error";

if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) str_type = "Deprecated Behavior";

if (type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR) str_type = "Undefined Behavior";

if (type == GL_DEBUG_TYPE_PORTABILITY) str_type = "Portability";

if (type == GL_DEBUG_TYPE_PERFORMANCE) str_type = "Performance";

if (type == GL_DEBUG_TYPE_MARKER) str_type = "Marker";

if (type == GL_DEBUG_TYPE_PUSH_GROUP) str_type = "Push Group";

if (type == GL_DEBUG_TYPE_POP_GROUP) str_type = "Pop Group";

if (type == GL_DEBUG_TYPE_OTHER) str_type = "Other";

/* severity */

string str_severity;

if (severity == GL_DEBUG_SEVERITY_HIGH) str_severity = "High";

if (severity == GL_DEBUG_SEVERITY_MEDIUM) str_severity = "Medium";

if (severity == GL_DEBUG_SEVERITY_LOW) str_severity = "Low";

if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) str_severity = "Notification";

/* print message */

cout << "OpenGL Debug Message:" << endl;

cout << "----------------------------------" << endl;

cout << "ID: \t\t" << id << endl;

cout << "Source: \t" << str_source << endl;

cout << "Type: \t\t" << str_type << endl;

cout << "Severity: \t" << str_severity << endl;

cout << "Message: \t" << message << endl;

cout << "----------------------------------" << endl << endl;

}

/************************************************************************************/

int main(int argc, char* argv[])

{

bool fullscreen = false;

bool debugcontext = true;

bool compatibilityprofilecontext = false;

bool vsync = true;

/* Initialize GLFW */

if (!glfwInit())

return 1;

/* Create a window and its OpenGL context */

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);

glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);

glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, debugcontext ? GLFW_TRUE : GLFW_FALSE);

glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, compatibilityprofilecontext ? GLFW_TRUE : GLFW_FALSE);

glfwWindowHint(GLFW_OPENGL_PROFILE, compatibilityprofilecontext ? GLFW_OPENGL_COMPAT_PROFILE : GLFW_OPENGL_CORE_PROFILE);

GLFWmonitor* monitor = fullscreen ? glfwGetPrimaryMonitor() : NULL;

GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", monitor, NULL);

if (!window)

{

glfwTerminate();

return 1;

}

/* Make the window's context current */

glfwMakeContextCurrent(window);

/* Set V-sync */

glfwSwapInterval(vsync ? 1 : 0);

/* Set callback functions */

glfwSetErrorCallback(error_callback);

glfwSetDropCallback(window, drop_callback);

glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

glfwSetKeyCallback(window, key_callback);

glfwSetCursorPosCallback(window, cursor_position_callback);

glfwSetMouseButtonCallback(window, mouse_button_callback);

glfwSetScrollCallback(window, scroll_callback);

/* Initialize GLEW */

if (glewInit() != GLEW_OK)

{

glfwTerminate();

return 2;

}

/* Register debug callback if debug context is available */

GLint contextflags = 0;

glGetIntegerv(GL_CONTEXT_FLAGS, &contextflags);

if (contextflags & GL_CONTEXT_FLAG_DEBUG_BIT)

{

glEnable(GL_DEBUG_OUTPUT);

glDebugMessageCallback(OpenGLDebugCallback, &debuguserparams);

}

/* Initialize graphics */

int width = 0, height = 0;

glfwGetFramebufferSize(window, &width, &height);

if (!Graphics::Initialize(ivec2(width, height)))

{

glfwTerminate();

cin.get();

return 3;

}

// setup example materials

//----------------------------------------

Material chrome;

chrome.SetKd(vec3(0.400f, 0.400f, 0.400f));

chrome.SetKs(vec3(0.774f, 0.774f, 0.774f));

chrome.Ns = 76.8f;

chrome.d = 0.7f;

Material gold;

gold.SetKd(vec3(0.751f, 0.606f, 0.226f));

gold.SetKs(vec3(0.628f, 0.556f, 0.366f));

gold.Ns = 51.2f;

gold.d = 0.8f;

Material dog;

dog.SetKs(vec3(1, 1, 1));

dog.Ns = 128.0f;

dog.d = 1.0f;

dog.MapKd = Graphics::LoadTexture("tibo_blinked.jpg");

//----------------------------------------

// setup example meshes

//----------------------------------------

Mesh cube;

cube.Vertices = {

// +x

{ { +0.5f, -0.5f, -0.5f }, { +1, 0, 0 }, { 1, 1 } },

{ { +0.5f, +0.5f, -0.5f }, { +1, 0, 0 }, { 0, 1 } },

{ { +0.5f, +0.5f, +0.5f }, { +1, 0, 0 }, { 0, 0 } },

{ { +0.5f, -0.5f, +0.5f }, { +1, 0, 0 }, { 1, 0 } },

// -x

{ { -0.5f, -0.5f, -0.5f }, { -1, 0, 0 }, { 1, 1 } },

{ { -0.5f, +0.5f, -0.5f }, { -1, 0, 0 }, { 0, 1 } },

{ { -0.5f, +0.5f, +0.5f }, { -1, 0, 0 }, { 0, 0 } },

{ { -0.5f, -0.5f, +0.5f }, { -1, 0, 0 }, { 1, 0 } },

// +y

{ { -0.5f, +0.5f, -0.5f }, { 0, +1, 0 }, { 1, 1 } },

{ { +0.5f, +0.5f, -0.5f }, { 0, +1, 0 }, { 0, 1 } },

{ { +0.5f, +0.5f, +0.5f }, { 0, +1, 0 }, { 0, 0 } },

{ { -0.5f, +0.5f, +0.5f }, { 0, +1, 0 }, { 1, 0 } },

// -y

{ { -0.5f, -0.5f, -0.5f }, { 0, -1, 0 }, { 1, 1 } },

{ { +0.5f, -0.5f, -0.5f }, { 0, -1, 0 }, { 0, 1 } },

{ { +0.5f, -0.5f, +0.5f }, { 0, -1, 0 }, { 0, 0 } },

{ { -0.5f, -0.5f, +0.5f }, { 0, -1, 0 }, { 1, 0 } },

// +z

{ { -0.5f, -0.5f, +0.5f }, { 0, 0, +1 }, { 1, 1 } },

{ { +0.5f, -0.5f, +0.5f }, { 0, 0, +1 }, { 0, 1 } },

{ { +0.5f, +0.5f, +0.5f }, { 0, 0, +1 }, { 0, 0 } },

{ { -0.5f, +0.5f, +0.5f }, { 0, 0, +1 }, { 1, 0 } },

// -z

{ { -0.5f, -0.5f, -0.5f }, { 0, 0, -1 }, { 1, 1 } },

{ { +0.5f, -0.5f, -0.5f }, { 0, 0, -1 }, { 0, 1 } },

{ { +0.5f, +0.5f, -0.5f }, { 0, 0, -1 }, { 0, 0 } },

{ { -0.5f, +0.5f, -0.5f }, { 0, 0, -1 }, { 1, 0 } },

};

cube.Faces = {

// +x

{ 0, 1, 2 },

{ 0, 2, 3 },

// -x

{ 4, 6, 5 },

{ 4, 7, 6 },

// +y

{ 8, 10, 9 },

{ 8, 11, 10 },

// -y

{ 12, 13, 14 },

{ 12, 14, 15 },

// +z

{ 16, 17, 18 },

{ 16, 18, 19 },

// -z

{ 20, 22, 21 },

{ 20, 23, 22 },

};

//----------------------------------------

// send materials and meshes to graphics

//----------------------------------------

vector<int> material_refs = {

Graphics::AddMaterial(chrome),

Graphics::AddMaterial(gold),

Graphics::AddMaterial(dog),

};

int mesh_ref_cube = Graphics::AddMesh(cube);

//----------------------------------------

// setup array of example objects

//----------------------------------------

int N_2 = 5;

std::vector<Object> objects;

for (int x = -N_2; x <= N_2; x++)

{

for (int y = -N_2; y <= N_2; y++)

{

for (int z = -N_2; z <= N_2; z++)

{

// create dummy object in scene

Object object;

// transform object to world-space

object.Transform =

translate(vec3(x * 1.5f, y * 1.5f, z * 1.5f));

// set material index that should be used for mesh

object.Material_Ref = material_refs.at(objects.size() % material_refs.size());

// set mesh index that should be drawn for object

object.Mesh_Ref = mesh_ref_cube;

// put object into scene

objects.push_back(object);

}

}

}

//----------------------------------------

/* Loop until the user closes the window */

//----------------------------------------

while (!glfwWindowShouldClose(window))

{

/* draw objects */

Graphics::Render(camera, objects);

/* Swap front and back buffers */

glfwSwapBuffers(window);

/* Poll for and process events */

glfwPollEvents();

}

//----------------------------------------

/* Clean up graphics */

Graphics::CleanUp();

glfwTerminate();

return 0;

}

Graphics.h

#pragma once

#define GLEW_STATIC

#include <GL/glew.h>

#define GLM_ENABLE_EXPERIMENTAL

#include <glm/gtc/type_ptr.hpp>

#include <glm/gtc/matrix_transform.hpp>

#include <glm/gtx/transform.hpp>

#include <string>

#include <vector>

#include "Shader.h"

struct Material

{

unsigned int Kd = 0;

unsigned int Ks = 0;

float Ns = 0.0f;

float d = 0.0f;

unsigned int MapKd = 0;

void SetKd(const glm::vec3& kd) {

Kd = glm::packUnorm4x8(glm::vec4(kd.x, kd.y, kd.z, 0));

}

void SetKs(const glm::vec3& ks) {

Ks = glm::packUnorm4x8(glm::vec4(ks.x, ks.y, ks.z, 0));

}

};

struct Vertex

{

glm::vec3 Position = { 0, 0, 0 };

glm::vec3 Normal = { 0, 0, 0 };

glm::vec2 Texcoord = { 0, 0 };

};

struct Mesh

{

std::vector<Vertex> Vertices;

std::vector<glm::uvec3> Faces;

};

struct Object

{

glm::mat4 Transform = glm::mat4(1);

unsigned int Mesh_Ref = 0;

unsigned int Material_Ref = 0;

};

struct Camera

{

float Distance = 15.0f;

float Theta = 0.0f;

float Phi = 0.3f;

float FieldOfView = 1.57f;

float ZNear = 0.1f;

float ZFar = 100.0f;

glm::vec3 Position() const {

return glm::vec3(

Distance * cos(Phi) * cos(Theta),

Distance * sin(Phi),

Distance * cos(Phi) * sin(Theta));

}

glm::mat4 View() const {

return glm::lookAt(Position(), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));

}

glm::mat4 Projection(float aspect_ratio) const {

return glm::perspective(FieldOfView, aspect_ratio, ZNear, ZFar);

}

};

namespace Graphics

{

bool Initialize(const glm::ivec2 framebuffer_size);

void CleanUp();

void Render(const Camera& camera, const std::vector<Object>& objects);

void ResizeFramebuffer(const glm::ivec2 framebuffer_size);

int AddMaterial(const Material& material);

int AddMesh(const Mesh& mesh);

int LoadTexture(const std::string& filename);

}

Graphics.cpp

#include "Graphics.h"

#include <SOIL.h>

#include <iostream>

#define CheckForGLErrors CheckForGLErrors__(__FILE__, __LINE__)

#define NULL_TEXTURE_NAME "NULL_TEXTURE_NAME"

#define THRESHOLD_OPACITY 0.95f

using namespace std;

using namespace glm;

/* argument type buffered in GL_DRAW_INDIRECT_BUFFER */

struct DrawElementsIndirectCommand

{

GLuint Count = 0;

GLuint InstanceCount = 0;

GLuint FirstIndex = 0;

GLuint BaseVertex = 0;

GLuint BaseInstance = 0;

};

/* sub-struct containing the values for a specific mesh */

struct MeshRef

{

GLuint BaseVertex = 0;

GLuint BaseIndex = 0;

GLuint IndexCount = 0;

};

GLuint program_opaque = 0;

GLuint program_transparent_collect = 0;

GLuint program_transparent_present = 0;

GLuint vertexshader = 0;

GLuint fragmentshader = 0;

GLuint vertexarray = 0;

GLuint vertexbuffer = 0;

GLuint indexbuffer = 0;

GLuint instancebuffer = 0;

GLuint indirectbuffer = 0;

GLuint materialbuffer = 0;

GLuint materialindicesbuffer = 0;

GLuint texturehandlebuffer = 0;

GLuint frametimequery = 0;

GLuint fragmentbuffer = 0;

GLuint fragmentindexbuffer = 0;

GLuint fragmentstartindextexture = 0;

GLuint fragmentstartindexinitializerbuffer = 0;

vector<Vertex> vertices;

vector<GLuint> indices;

vector<MeshRef> mesh_references;

vector<Material> materials;

vector<string> texture_filenames;

vector<GLuint64> texture_handles;

vector<GLuint> textures;

bool reload_mesh_buffers = true;

bool reload_material_buffers = true;

ivec2 framebuffersize = ivec2(0, 0);

void CheckForGLErrors__(const char* file, unsigned int line)

{

string errorstring;

for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)

{

if (error == GL_INVALID_ENUM)

errorstring += " GL_INVALID_ENUM";

if (error == GL_INVALID_VALUE)

errorstring += " GL_INVALID_VALUE";

if (error == GL_INVALID_OPERATION)

errorstring += " GL_INVALID_OPERATION";

if (error == GL_STACK_OVERFLOW)

errorstring += " GL_STACK_OVERFLOW";

if (error == GL_STACK_UNDERFLOW)

errorstring += " GL_STACK_UNDERFLOW";

if (error == GL_OUT_OF_MEMORY)

errorstring += " GL_OUT_OF_MEMORY";

if (error == GL_INVALID_FRAMEBUFFER_OPERATION)

errorstring += " GL_INVALID_FRAMEBUFFER_OPERATION";

if (error == GL_CONTEXT_LOST)

errorstring += " GL_CONTEXT_LOST";

}

if (!errorstring.empty())

{

cout

<< (char)7

<< "OpenGL Error: " << endl

<< "\tLine: \t" << line << endl

<< "\tFile: \t" << file << endl

<< "\tErrors: \t" << errorstring << endl << endl;

cin.get();

}

}

void ReloadMaterialBuffer()

{

if (!reload_material_buffers)

return;

reload_material_buffers = false;

// fill material buffer

glNamedBufferData(

materialbuffer,

materials.size() * sizeof(Material),

materials.data(),

GL_DYNAMIC_DRAW);

// fill texture handle buffer

glNamedBufferData(

texturehandlebuffer,

texture_handles.size() * sizeof(GLuint64),

texture_handles.data(),

GL_DYNAMIC_DRAW);

}

void ReloadMeshBuffers()

{

if (!reload_mesh_buffers)

return;

reload_mesh_buffers = false;

// fill vertex buffer

glNamedBufferData(

vertexbuffer,

vertices.size() * sizeof(Vertex),

vertices.data(),

GL_DYNAMIC_DRAW);

// fill index buffer

glNamedBufferData(

indexbuffer,

indices.size() * sizeof(GLuint),

indices.data(),

GL_DYNAMIC_DRAW);

}

bool Graphics::Initialize(const glm::ivec2 framebuffer_size)

{

// check if GL_ARB_bindless_texture is supported

//----------------------------------------

bool bindless_texture_supported = false;

GLint numberofextensions = 0;

glGetIntegerv(GL_NUM_EXTENSIONS, &numberofextensions);

for (int i = 0; i < numberofextensions; i++)

{

string extension = (const char*)glGetStringi(GL_EXTENSIONS, i);

if (extension == "GL_ARB_bindless_texture")

{

bindless_texture_supported = true;

break;

}

}

if (!bindless_texture_supported)

{

cout << "error: GL_ARB_bindless_texture not supported" << endl;

return false;

}

//----------------------------------------

// create GL objects

//----------------------------------------

program_opaque = glCreateProgram();

program_transparent_collect = glCreateProgram();

program_transparent_present = glCreateProgram();

vertexshader = glCreateShader(GL_VERTEX_SHADER);

fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);

glCreateVertexArrays(1, &vertexarray);

glCreateBuffers(1, &vertexbuffer);

glCreateBuffers(1, &indexbuffer);

glCreateBuffers(1, &instancebuffer);

glCreateBuffers(1, &indirectbuffer);

glCreateBuffers(1, &materialbuffer);

glCreateBuffers(1, &materialindicesbuffer);

glCreateBuffers(1, &texturehandlebuffer);

glCreateQueries(GL_TIME_ELAPSED, 1, &frametimequery);

glCreateBuffers(1, &fragmentbuffer);

glCreateBuffers(1, &fragmentindexbuffer);

glCreateBuffers(1, &fragmentstartindexinitializerbuffer);

//----------------------------------------

// setup program opaque

//----------------------------------------

if (!CompileShader(vertexshader, LoadTextFile("opaque.vs.glsl")))

return false;

if (!CompileShader(fragmentshader, LoadTextFile("opaque.fs.glsl")))

return false;

if (!LinkProgram(program_opaque, { vertexshader, fragmentshader }))

return false;

//----------------------------------------

// setup program collect fragments

//----------------------------------------

if (!CompileShader(vertexshader, LoadTextFile("transparent_collect.vs.glsl")))

return false;

if (!CompileShader(fragmentshader, LoadTextFile("transparent_collect.fs.glsl")))

return false;

if (!LinkProgram(program_transparent_collect, { vertexshader, fragmentshader }))

return false;

//----------------------------------------

// setup program present fragments

//----------------------------------------

if (!CompileShader(vertexshader, LoadTextFile("transparent_present.vs.glsl")))

return false;

if (!CompileShader(fragmentshader, LoadTextFile("transparent_present.fs.glsl")))

return false;

if (!LinkProgram(program_transparent_present, { vertexshader, fragmentshader }))

return false;

//----------------------------------------

// setup vertexarray

//----------------------------------------

// index buffer

glVertexArrayElementBuffer(vertexarray, indexbuffer);

// per-vertex attributes

glVertexArrayAttribFormat(vertexarray, 0, 3, GL_FLOAT, GL_FALSE, 0);

glVertexArrayAttribFormat(vertexarray, 1, 3, GL_FLOAT, GL_FALSE, 0);

glVertexArrayAttribFormat(vertexarray, 2, 2, GL_FLOAT, GL_FALSE, 0);

glVertexArrayVertexBuffer(vertexarray, 0, vertexbuffer, offsetof(Vertex, Position), sizeof(Vertex));

glVertexArrayVertexBuffer(vertexarray, 1, vertexbuffer, offsetof(Vertex, Normal), sizeof(Vertex));

glVertexArrayVertexBuffer(vertexarray, 2, vertexbuffer, offsetof(Vertex, Texcoord), sizeof(Vertex));

glEnableVertexArrayAttrib(vertexarray, 0);

glEnableVertexArrayAttrib(vertexarray, 1);

glEnableVertexArrayAttrib(vertexarray, 2);

// per-instance attributes

glVertexArrayAttribFormat(vertexarray, 3, 4, GL_FLOAT, GL_FALSE, 0);

glVertexArrayAttribFormat(vertexarray, 4, 4, GL_FLOAT, GL_FALSE, 0);

glVertexArrayAttribFormat(vertexarray, 5, 4, GL_FLOAT, GL_FALSE, 0);

glVertexArrayAttribFormat(vertexarray, 6, 4, GL_FLOAT, GL_FALSE, 0);

glVertexArrayVertexBuffer(vertexarray, 3, instancebuffer, sizeof(vec4) * 0, sizeof(mat4));

glVertexArrayVertexBuffer(vertexarray, 4, instancebuffer, sizeof(vec4) * 1, sizeof(mat4));

glVertexArrayVertexBuffer(vertexarray, 5, instancebuffer, sizeof(vec4) * 2, sizeof(mat4));

glVertexArrayVertexBuffer(vertexarray, 6, instancebuffer, sizeof(vec4) * 3, sizeof(mat4));

glEnableVertexArrayAttrib(vertexarray, 3);

glEnableVertexArrayAttrib(vertexarray, 4);

glEnableVertexArrayAttrib(vertexarray, 5);

glEnableVertexArrayAttrib(vertexarray, 6);

glVertexArrayBindingDivisor(vertexarray, 3, 1);

glVertexArrayBindingDivisor(vertexarray, 4, 1);

glVertexArrayBindingDivisor(vertexarray, 5, 1);

glVertexArrayBindingDivisor(vertexarray, 6, 1);

//----------------------------------------

// SSBO bindings

//----------------------------------------

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, materialbuffer);

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, materialindicesbuffer);

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, texturehandlebuffer);

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, fragmentbuffer);

glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

//----------------------------------------

// atomic counter bindings

//----------------------------------------

// allocate fragmentindexbuffer

glNamedBufferData(

fragmentindexbuffer,

sizeof(uint),

nullptr,

GL_DYNAMIC_DRAW);

glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 1, fragmentindexbuffer);

glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);

//----------------------------------------

// set viewport

//----------------------------------------

ResizeFramebuffer(framebuffer_size);

//----------------------------------------

// load null texture first

//----------------------------------------

LoadTexture(NULL_TEXTURE_NAME);

// add screen_rectangle mesh

//----------------------------------------

Mesh screen_rectangle;

screen_rectangle.Vertices = {

{ { -1, -1, +0 }, { 0, 0, 1 }, { 0, 0 } },

{ { +1, -1, +0 }, { 0, 0, 1 }, { 0, 0 } },

{ { +1, +1, +0 }, { 0, 0, 1 }, { 0, 0 } },

{ { -1, +1, +0 }, { 0, 0, 1 }, { 0, 0 } },

};

screen_rectangle.Faces = {

{ 0, 1, 2 },

{ 0, 2, 3 },

};

/*0 = */AddMesh(screen_rectangle);

//----------------------------------------

CheckForGLErrors;

return true;

}

void Graphics::CleanUp()

{

// destroy GL objects

//----------------------------------------

glDeleteProgram(program_opaque);

glDeleteProgram(program_transparent_collect);

glDeleteProgram(program_transparent_present);

glDeleteShader(vertexshader);

glDeleteShader(fragmentshader);

glDeleteVertexArrays(1, &vertexarray);

glDeleteBuffers(1, &vertexbuffer);

glDeleteBuffers(1, &indexbuffer);

glDeleteBuffers(1, &instancebuffer);

glDeleteBuffers(1, &indirectbuffer);

glDeleteBuffers(1, &materialbuffer);

glDeleteBuffers(1, &materialindicesbuffer);

glDeleteBuffers(1, &texturehandlebuffer);

glDeleteQueries(1, &frametimequery);

glDeleteBuffers(1, &fragmentbuffer);

glDeleteBuffers(1, &fragmentindexbuffer);

glDeleteTextures(1, &fragmentstartindextexture);

glDeleteBuffers(1, &fragmentstartindexinitializerbuffer);

//----------------------------------------

// delete dynamically created textures

//----------------------------------------

for (int i = 0; i < textures.size(); i++)

glMakeTextureHandleNonResidentARB(texture_handles.at(i));

glDeleteTextures(textures.size(), textures.data());

texture_filenames.clear();

texture_handles.clear();

textures.clear();

//----------------------------------------

CheckForGLErrors;

}

void Graphics::Render(const Camera& camera, const std::vector<Object>& objects)

{

//static bool measure_frametime = true;

//if (measure_frametime)

// glBeginQuery(GL_TIME_ELAPSED, frametimequery);

// reload buffers if necessary

//----------------------------------------

// make sure all materials are stored in material buffer

ReloadMaterialBuffer();

// make sure all meshes are stored in vertex buffer and index buffer

ReloadMeshBuffers();

//----------------------------------------

// camera matrices

//----------------------------------------

float aspect_ratio = 1.33f;

mat4 view = camera.View();

mat4 projection = camera.Projection(aspect_ratio);

//----------------------------------------

// calculate modelview matrices and draw commands

//----------------------------------------

// model-view transforms for instance buffer

vector<mat4> mvs;

// draw command parameters for indirect buffer

vector<DrawElementsIndirectCommand> cmds;

// indices for material indices buffer (SSBO)

vector<GLuint> materialindices;

for (int i = 0; i < objects.size(); i++)

{

const auto& object = objects.at(i);

// modelview

mat4 mv = view * object.Transform;

// cmd

DrawElementsIndirectCommand cmd;

cmd.Count = mesh_references.at(object.Mesh_Ref).IndexCount;

cmd.InstanceCount = 1;

cmd.FirstIndex = mesh_references.at(object.Mesh_Ref).BaseIndex;

cmd.BaseVertex = mesh_references.at(object.Mesh_Ref).BaseVertex;

cmd.BaseInstance = i;

// first render pass draws only opaque objects

bool is_opaque = materials.at(object.Material_Ref).d >= THRESHOLD_OPACITY;

if (!is_opaque)

cmd.InstanceCount = 0;

mvs.push_back(mv);

cmds.push_back(cmd);

materialindices.push_back(object.Material_Ref);

}

int instance_count = cmds.size();

// fill instance buffer with modelview matrices

glNamedBufferData(

instancebuffer,

mvs.size() * sizeof(mat4),

mvs.data(),

GL_DYNAMIC_DRAW);

// fill indirect buffer with draw command parameters

glNamedBufferData(

indirectbuffer,

cmds.size() * sizeof(DrawElementsIndirectCommand),

cmds.data(),

GL_DYNAMIC_DRAW);

// fill material indices buffer

glNamedBufferData(

materialindicesbuffer,

materialindices.size() * sizeof(GLuint),

materialindices.data(),

GL_DYNAMIC_DRAW);

CheckForGLErrors;

//----------------------------------------

// light parameters

//----------------------------------------

// rotate light around

static float angle = 0;

angle += 0.016f * 3.141592654f / 4;

vec3 light_intensity = vec3(1, 1, 1);

vec3 light_direction = vec3(0.3f * cos(angle), -1.0f, 0.3f * sin(angle));

float light_ambient = 0.3f;

vec3 light_direction_eyespace = view * vec4(light_direction, 0);

//----------------------------------------

// 1. render opaque objects

// fills the the depth buffer

//----------------------------------------

glClearColor(0.2f, 0.4f, 0.8f, 0.0f);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);

glEnable(GL_CULL_FACE);

glUseProgram(program_opaque);

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"

// instance count for transparent objects has been set to 0, these objects are not drawn

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);

CheckForGLErrors;

//----------------------------------------

// 2. render transparent objects off-screen into fragment buffer

// uses depth test and early_fragment_tests to make sure that

// unnecessary invisible fragments are not collected

//----------------------------------------

// transparent only

for (auto& cmd : cmds)

cmd.InstanceCount = 1 - cmd.InstanceCount;

// fill indirect buffer with draw command parameters (transparent only)

glNamedBufferData(

indirectbuffer,

cmds.size() * sizeof(DrawElementsIndirectCommand),

cmds.data(),

GL_DYNAMIC_DRAW);

// 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);

// show fragment count (previous frame)

//GLuint fragment_count = 0;

//glGetNamedBufferSubData(fragmentindexbuffer, 0, sizeof(GLuint), &fragment_count);

//cout << "fragment_count: " << fragment_count << endl;

// initialize fragment index buffer

uint atomic_fragment_index = 0;

glNamedBufferSubData(fragmentindexbuffer, 0, sizeof(uint), &atomic_fragment_index);

glEnable(GL_DEPTH_TEST);

glUseProgram(program_transparent_collect);

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"

// instance count for opaque objects has been set to 0, these objects are not drawn

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);

CheckForGLErrors;

//----------------------------------------

// 3. render screen rectangle, sort and blend transparent fragments

// uses no depth test, but blending to ensure that the opaque objects

// are not overwritten by the transparent objects

//----------------------------------------

// https://www.khronos.org/opengl/wiki/Memory_Model#External_visibility

// make this data externally visible from previous shader invocations:

// -- image access (load/store) for fragmentstartindextexture

// -- shader storage for fragmentbuffer

// -- optionally: atomic counter for fragmentindexbuffer

glMemoryBarrier(

GL_SHADER_IMAGE_ACCESS_BARRIER_BIT |

GL_SHADER_STORAGE_BARRIER_BIT |

GL_ATOMIC_COUNTER_BARRIER_BIT);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glUseProgram(program_transparent_present);

glBindVertexArray(vertexarray);

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);

glDisable(GL_BLEND);

glBlendFunc(GL_ONE, GL_ZERO);

CheckForGLErrors;

//----------------------------------------

// get frame time (without stalling the GPU)

//----------------------------------------

//if (measure_frametime)

//{

// measure_frametime = false;

//

// glEndQuery(GL_TIME_ELAPSED);

//}

//

//GLint64 frametime = 0;

//

//// check is frametime available

//glGetQueryObjecti64v(frametimequery, GL_QUERY_RESULT_AVAILABLE, &frametime);

//if (frametime)

//{

// measure_frametime = true;

//

// glGetQueryObjecti64v(frametimequery, GL_QUERY_RESULT, &frametime);

//

// cout << "frametime (ms): " << (double(frametime) / 1000000) << endl;

//}

//----------------------------------------

CheckForGLErrors;

}

void Graphics::ResizeFramebuffer(const glm::ivec2 framebuffer_size)

{

framebuffersize = framebuffer_size;

glViewport(0, 0, framebuffersize.x, framebuffersize.y);

// re-create fragment start index texture

glDeleteTextures(1, &fragmentstartindextexture);

glCreateTextures(GL_TEXTURE_2D, 1, &fragmentstartindextexture);

glTextureStorage2D(fragmentstartindextexture, 1, GL_R32UI, framebuffersize.x, framebuffersize.y);

glTextureParameteri(fragmentstartindextexture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTextureParameteri(fragmentstartindextexture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTextureParameteri(fragmentstartindextexture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTextureParameteri(fragmentstartindextexture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

// bind to image unit 1

glBindImageTexture(1, fragmentstartindextexture, 0, false, 0, GL_READ_WRITE, GL_R32UI);

// resize fragment start index initializer buffer

vector<uint> fragmentstartindexinitializer(framebuffersize.x * framebuffersize.y, 0xFFFFFFFF);

glNamedBufferData(

fragmentstartindexinitializerbuffer,

fragmentstartindexinitializer.size() * sizeof(uint),

fragmentstartindexinitializer.data(),

GL_DYNAMIC_COPY);

// resize fragmentbuffer too

glNamedBufferData(

fragmentbuffer,

framebuffersize.x * framebuffersize.y * 10 * sizeof(uvec4),

nullptr,

GL_DYNAMIC_DRAW);

CheckForGLErrors;

}

int Graphics::AddMaterial(const Material& material)

{

// reload materials next frame

reload_material_buffers = true;

materials.push_back(material);

// fix: texture index

if (materials.back().MapKd >= textures.size())

materials.back().MapKd = 0;

// fix: if diffuse texture is present, set diffuse value to vec3(1, 1, 1)

if (materials.back().MapKd != 0)

materials.back().SetKd(vec3(1, 1, 1));

return materials.size() - 1;

}

int Graphics::AddMesh(const Mesh& mesh)

{

// reload mesh data next frame

reload_mesh_buffers = true;

// store vertex and index offset as well as index count of mesh

MeshRef mesh_reference;

mesh_reference.BaseVertex = vertices.size();

mesh_reference.BaseIndex = indices.size();

mesh_reference.IndexCount = mesh.Faces.size() * 3;

mesh_references.push_back(mesh_reference);

// collect vertices for vertex buffer (containing all meshes)

vertices.insert(vertices.end(), mesh.Vertices.begin(), mesh.Vertices.end());

// collect indices for index buffer (containing all meshes)

for (auto& face : mesh.Faces)

{

indices.push_back(face.x);

indices.push_back(face.y);

indices.push_back(face.z);

}

// return array index as "mesh reference"

return mesh_references.size() - 1;

}

int Graphics::LoadTexture(const std::string& filename)

{

/* check if texture already exists */

for (int i = 0; i < texture_filenames.size(); i++)

if (texture_filenames.at(i) == filename)

return i;

/* check if null texture is requested */

if (filename == NULL_TEXTURE_NAME)

{

/* is wasnt found in the texture_map, so create it */

unsigned char null_texture_data[] = { 0xFF, 0xFF, 0xFF, 0xFF };

GLuint texture = 0;

glCreateTextures(GL_TEXTURE_2D, 1, &texture);

glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1);

glTextureSubImage2D(texture, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, null_texture_data);

glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

GLuint64 texture_handle = glGetTextureHandleARB(texture);

glMakeTextureHandleResidentARB(texture_handle);

textures.push_back(texture);

texture_handles.push_back(texture_handle);

texture_filenames.push_back(filename);

CheckForGLErrors;

return textures.size() - 1;

}

/* load image */

int texture_width = 0;

int texture_height = 0;

int texture_channels = 0;

unsigned char* texture_data = SOIL_load_image(

filename.c_str(),

&texture_width, /* return width */

&texture_height, /* return height */

&texture_channels, /* return channels */

SOIL_LOAD_RGBA /* create texture with 4 components (RGBA) */

);

if (!texture_data)

{

cout << "error: cant load texture file \"" << filename << "\"" << endl;

return LoadTexture(NULL_TEXTURE_NAME);

}

/* calculate lods */

GLuint lods = std::max(1.0, std::log2(std::max(texture_width, texture_height)));

/* generate new texture */

GLuint texture = 0;

glCreateTextures(GL_TEXTURE_2D, 1, &texture);

glTextureStorage2D(texture, lods, GL_RGBA8, texture_width, texture_height);

glTextureSubImage2D(texture, 0, 0, 0, texture_width, texture_height, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);

glGenerateTextureMipmap(texture);

//glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

//glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);

glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

GLuint64 texture_handle = glGetTextureHandleARB(texture);

glMakeTextureHandleResidentARB(texture_handle);

/* release texture memory */

SOIL_free_image_data(texture_data);

textures.push_back(texture);

texture_handles.push_back(texture_handle);

texture_filenames.push_back(filename);

CheckForGLErrors;

return textures.size() - 1;

}

Vertexshader: "opaque.vs.glsl"

#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;

}

Fragmentshader: "opaque.fs.glsl"

#version 460 core

#extension GL_ARB_bindless_texture : enable

// 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[];

};

// 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

out_color = ShadeFragment();

}

Vertexshader: "transparent_collect.vs.glsl"

#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;

}

Fragmentshader: "transparent_collect.fs.glsl"

#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;

}

Vertexshader: "transparent_present.vs.glsl"

#version 460 core

// per-vertex attributes

layout (location = 0) in vec3 in_position;

void main()

{

// vertex position

gl_Position = vec4(in_position, 1);

}

Fragmentshader: "transparent_present.fs.glsl"

#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;

// 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)

{

bool sorting = true;

while (sorting)

{

sorting = false;

for (uint i = 0; i < fragment_count - 1; i++)

{

if (local_fragments[i + 0].Depth < local_fragments[i + 1].Depth)

{

Fragment swap = local_fragments[i];

local_fragments[i + 0] = local_fragments[i + 1];

local_fragments[i + 1] = swap;

sorting = true;

}

}

}

}

vec4 BlendFragments(uint fragment_count)

{

vec4 destination_color = vec4(0, 0, 0, 0);

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 = vec4(0, 0, 0, 0);

}

}