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