2. a) Using bindless textures

https://www.khronos.org/opengl/wiki/Bindless_Texture

Libraries:

SOIL (loading texture files)

Image used:

Main.cpp

#define GLEW_STATIC

#include <GL/glew.h>

#include <GLFW/glfw3.h>

#include <iostream>

#include <vector>

#include "Graphics.h"

using namespace std;

using namespace glm;

struct DebugUserParams {

bool ShowNotifications = true;

} debuguserparams;

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

{

}

}

}

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;

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;

/* 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 = 1.0f;

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 = 1.0f;

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

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

int material_ref_chrome = Graphics::AddMaterial(chrome);

int material_ref_gold = Graphics::AddMaterial(gold);

int material_ref_dog = Graphics::AddMaterial(dog);

int mesh_ref_cube = Graphics::AddMesh(cube);

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

// setup array of example objects

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

std::vector<Object> objects;

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

{

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

{

for (int z = -2; z <= 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_ref_dog;

if (abs(x) % 3 == 1)

object.Material_Ref = material_ref_chrome;

if (abs(x) % 3 == 2)

object.Material_Ref = material_ref_gold;

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

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

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;

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

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

// setup program

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

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

return false;

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

return false;

if (!LinkProgram(program, { 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);

glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

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

// resize framebuffer

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

ResizeFramebuffer(framebuffer_size);

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

// load null texture first

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

LoadTexture(NULL_TEXTURE_NAME);

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

CheckForGLErrors;

return true;

}

void Graphics::CleanUp()

{

// destroy GL objects

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

glDeleteProgram(program);

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

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

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

{

// 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<unsigned int> 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;

mvs.push_back(mv);

cmds.push_back(cmd);

materialindices.push_back(object.Material_Ref);

}

// 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(unsigned int),

materialindices.data(),

GL_DYNAMIC_DRAW);

int instance_count = cmds.size();

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

// light parameters

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

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

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

// clear screen

glClearColor(0.2f, 0.4f, 0.6f, 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);

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

CheckForGLErrors;

}

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

{

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

}

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;

}

Fragmentshader: "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;

void main()

{

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

// to framebuffer

out_color = vec4(color, d);

}