From e3df71f8ce9671ad28cca8998aa5cec990271e47 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sat, 27 Feb 2021 21:28:21 +0000 Subject: [PATCH] Camera movement + implemented deferred rendering and SSAO --- data/assets.cfg | 4 +- data/shader/gbuffer.frag | 42 ++++++ data/shader/lighting.frag | 9 -- data/shader/model.frag | 10 +- data/shader/model.vert | 8 +- data/shader/screen_quad.vert | 4 +- data/shader/ssao.frag | 48 +++++++ data/shader/ssao_blur.frag | 19 +++ src/vpg/ecs/transform.cpp | 10 ++ src/vpg/ecs/transform.hpp | 7 + src/vpg/entry.cpp | 112 +++++++++++++-- src/vpg/gl/camera.cpp | 1 - src/vpg/gl/renderer.cpp | 257 ++++++++++++++++++++++++++++++++++- src/vpg/gl/renderer.hpp | 38 +++++- 14 files changed, 532 insertions(+), 37 deletions(-) create mode 100644 data/shader/gbuffer.frag delete mode 100644 data/shader/lighting.frag create mode 100644 data/shader/ssao.frag create mode 100644 data/shader/ssao_blur.frag diff --git a/data/assets.cfg b/data/assets.cfg index c5f4bd7..ce497ee 100644 --- a/data/assets.cfg +++ b/data/assets.cfg @@ -7,7 +7,9 @@ language.portuguese = static text text/portuguese.csv font.inconsolata = static font font/Inconsolata.otf -shader.lighting = static shader shader/screen_quad.vert shader/lighting.frag +shader.gbuffer = static shader shader/screen_quad.vert shader/gbuffer.frag +shader.ssao = static shader shader/screen_quad.vert shader/ssao.frag +shader.ssao_blur = static shader shader/screen_quad.vert shader/ssao_blur.frag shader.model = static shader shader/model.vert shader/model.frag model.chr_knight = dynamic model model/chr_knight.qb diff --git a/data/shader/gbuffer.frag b/data/shader/gbuffer.frag new file mode 100644 index 0000000..8058ea2 --- /dev/null +++ b/data/shader/gbuffer.frag @@ -0,0 +1,42 @@ +#version 330 core + +#define PI 3.1415926535897932384626433832795 + +in vec2 frag_uv; + +out vec4 frag_color; + +uniform sampler2D albedo_tex; +uniform sampler2D position_tex; +uniform sampler2D normal_tex; +uniform sampler2D ssao_tex; + +uniform vec3 sky_color; +uniform float z_far; + +uniform mat4 view; + +const vec3 world_light_dir = normalize(vec3(-0.7, 1.5, 0.5)); + +void main() { + vec3 albedo = texture(albedo_tex, frag_uv).rgb; + vec3 position = texture(position_tex, frag_uv).xyz; + vec3 normal = texture(normal_tex, frag_uv).xyz; + float ambient_occlusion = texture(ssao_tex, frag_uv).r; + + vec3 lighting = albedo * ambient_occlusion * 0.3f; + vec3 light_dir = normalize(mat3(view) * world_light_dir); + + vec3 diffuse = max(dot(normal, light_dir), 0.0f) * albedo * ambient_occlusion; + lighting += diffuse; + + if (normal.x == 0.0f && normal.y == 0.0f && normal.z == 0.0f) { + // Sky + lighting = albedo; + } + + // Fog + float depth = min(1.0f, length(position) / z_far); + float fog = depth * depth; + frag_color = vec4(mix(lighting, sky_color, fog), 1.0); +} \ No newline at end of file diff --git a/data/shader/lighting.frag b/data/shader/lighting.frag deleted file mode 100644 index 90010eb..0000000 --- a/data/shader/lighting.frag +++ /dev/null @@ -1,9 +0,0 @@ -#version 330 core - -in vec2 frag_uvs; - -out vec4 frag_color; - -void main() { - frag_color = vec4(frag_uvs, 0.0, 1.0); -} \ No newline at end of file diff --git a/data/shader/model.frag b/data/shader/model.frag index 37272db..35dbe1b 100644 --- a/data/shader/model.frag +++ b/data/shader/model.frag @@ -9,11 +9,17 @@ layout (std140) uniform Palette { Material palette[255]; }; +in vec3 frag_position; +in vec3 frag_normal; flat in uint frag_material; -out vec4 frag_color; +layout (location = 0) out vec3 albedo; +layout (location = 1) out vec3 position; +layout (location = 2) out vec3 normal; void main() { Material mat = palette[frag_material]; - frag_color = vec4(mat.color, 1.0); + albedo = mat.color; + position = frag_position; + normal = normalize(frag_normal); } \ No newline at end of file diff --git a/data/shader/model.vert b/data/shader/model.vert index bf1eb67..97ac3f1 100644 --- a/data/shader/model.vert +++ b/data/shader/model.vert @@ -4,6 +4,8 @@ layout (location = 0) in vec3 vert_position; layout (location = 1) in vec3 vert_normal; layout (location = 2) in uint vert_material; +out vec3 frag_position; +out vec3 frag_normal; flat out uint frag_material; uniform mat4 model; @@ -11,6 +13,10 @@ uniform mat4 view; uniform mat4 proj; void main() { + vec4 view_position = view * model * vec4(vert_position, 1.0f); + frag_position = view_position.xyz; + gl_Position = proj * view_position; + mat3 normal_matrix = transpose(inverse(mat3(view * model))); + frag_normal = normal_matrix * vert_normal; frag_material = vert_material; - gl_Position = proj * view * model * vec4(vert_position, 1.0); } \ No newline at end of file diff --git a/data/shader/screen_quad.vert b/data/shader/screen_quad.vert index 00f5fac..a9d4024 100644 --- a/data/shader/screen_quad.vert +++ b/data/shader/screen_quad.vert @@ -18,9 +18,9 @@ const vec2[6] uvs = vec2[6]( vec2(0.0, 0.0) ); -out vec2 frag_uvs; +out vec2 frag_uv; void main() { - frag_uvs = uvs[gl_VertexID]; + frag_uv = uvs[gl_VertexID]; gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0); } \ No newline at end of file diff --git a/data/shader/ssao.frag b/data/shader/ssao.frag new file mode 100644 index 0000000..2d5f14b --- /dev/null +++ b/data/shader/ssao.frag @@ -0,0 +1,48 @@ +#version 330 core + +in vec2 frag_uv; + +out float frag_color; + +const int NUM_SAMPLES = 64; + +uniform sampler2D position_tex; +uniform sampler2D normal_tex; +uniform sampler2D noise_tex; + +uniform mat4 projection; +uniform vec2 noise_scale; +uniform vec3 samples[NUM_SAMPLES]; + +const float radius = 0.5; +const float bias = 0.025; +const float magnitude = 1.1; +const float constrast = 1.1; + +void main() { + vec3 frag_pos = texture(position_tex, frag_uv).xyz; + vec3 normal = normalize(texture(normal_tex, frag_uv).xyz); + vec3 random_vec = normalize(texture(noise_tex, frag_uv * noise_scale).xyz); + + vec3 tangent = normalize(random_vec - normal * dot(random_vec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal); + + float occlusion = 0.0; + for (int i = 0; i < NUM_SAMPLES; ++i) { + vec3 sample_pos = tbn * samples[i]; + sample_pos = frag_pos + sample_pos * radius; + + vec4 offset = projection * vec4(sample_pos, 1.0); + offset.xy /= offset.w; + offset.xy = offset.xy * 0.5 + 0.5; + + float sample_depth = texture(position_tex, offset.xy).z; + float range_check = smoothstep(0.0, 1.0, radius / abs(frag_pos.z - sample_depth)); + occlusion += (sample_depth >= sample_pos.z + bias ? 1.0 : 0.0) * range_check; + } + + occlusion = 1.0 - occlusion / NUM_SAMPLES; + occlusion = pow(occlusion, magnitude); + frag_color = constrast * (occlusion - 0.5) + 0.5; +} \ No newline at end of file diff --git a/data/shader/ssao_blur.frag b/data/shader/ssao_blur.frag new file mode 100644 index 0000000..abeadf4 --- /dev/null +++ b/data/shader/ssao_blur.frag @@ -0,0 +1,19 @@ +#version 330 core + +in vec2 frag_uv; + +out float frag_color; + +uniform sampler2D ssao_tex; + +void main() { + vec2 texel_size = 1.0 / vec2(textureSize(ssao_tex, 0)); + float result = 0.0; + for (int x = -2; x < 2; ++x) { + for (int y = -2; y < 2; ++y) { + vec2 offset = vec2(float(x), float(y)) * texel_size; + result += texture(ssao_tex, frag_uv + offset).r; + } + } + frag_color = result / (4.0 * 4.0); +} \ No newline at end of file diff --git a/src/vpg/ecs/transform.cpp b/src/vpg/ecs/transform.cpp index 669250f..6a24919 100644 --- a/src/vpg/ecs/transform.cpp +++ b/src/vpg/ecs/transform.cpp @@ -12,6 +12,11 @@ vpg::ecs::Transform::Transform(Entity parent) { this->dirty = true; } +void vpg::ecs::Transform::translate(const glm::vec3& translation) { + this->position += translation; + this->dirty = true; +} + void Transform::set_position(const glm::vec3& position) { this->position = position; this->dirty = true; @@ -55,5 +60,10 @@ void Transform::update() { this->local = glm::scale(this->local, this->scale); this->local = glm::toMat4(this->get_rotation()) * this->local; this->local = glm::translate(this->local, this->position); + + this->forward = this->rotation * glm::vec3(0.0f, 0.0f, -1.0f); + this->up = this->rotation * glm::vec3(0.0f, 1.0f, 0.0f); + this->right = this->rotation * glm::vec3(1.0f, 0.0f, 0.0f); + this->dirty = false; } diff --git a/src/vpg/ecs/transform.hpp b/src/vpg/ecs/transform.hpp index 88c9b40..d17526d 100644 --- a/src/vpg/ecs/transform.hpp +++ b/src/vpg/ecs/transform.hpp @@ -10,6 +10,8 @@ namespace vpg::ecs { public: Transform(Entity parent = NullEntity); + void translate(const glm::vec3& translation); + void set_position(const glm::vec3& position); void set_rotation(const glm::quat& rotation); void set_scale(const glm::vec3& scale); @@ -17,6 +19,10 @@ namespace vpg::ecs { inline const glm::vec3& get_position() const { return this->position; } inline const glm::quat& get_rotation() const { return this->rotation; } inline const glm::vec3& get_scale() const { return this->scale; } + + inline const glm::vec3& get_forward() const { return this->forward; } + inline const glm::vec3& get_right() const { return this->right; } + inline const glm::vec3& get_up() const { return this->up; } glm::mat4 get_global(); const glm::mat4& get_local(); @@ -26,6 +32,7 @@ namespace vpg::ecs { private: Entity parent; glm::vec3 position, scale; + glm::vec3 forward, right, up; glm::quat rotation; glm::mat4 local; bool dirty; diff --git a/src/vpg/entry.cpp b/src/vpg/entry.cpp index 3e19fd2..1ea50aa 100644 --- a/src/vpg/entry.cpp +++ b/src/vpg/entry.cpp @@ -18,22 +18,47 @@ using namespace vpg; -class MyBehaviour : public ecs::IBehaviour { +class CameraBehaviour : public ecs::IBehaviour { public: - MyBehaviour(ecs::Entity entity) { - std::cout << "Initialized MyBehaviour on entity " << entity << '\n'; - } - - ~MyBehaviour() { - std::cout << "Destroyed MyBehaviour\n"; + CameraBehaviour(ecs::Entity entity, GLFWwindow* window) { + this->entity = entity; + this->window = window; + this->speed = 50.0f; } virtual void update(float dt) override { - std::cout << "Updated MyBehaviour with dt " << dt << '\n'; + auto transform = ecs::Coordinator::get_component(this->entity); + + if (glfwGetKey(this->window, GLFW_KEY_W) == GLFW_PRESS) { + transform->translate(transform->get_forward() * dt * this->speed); + } + else if (glfwGetKey(this->window, GLFW_KEY_S) == GLFW_PRESS) { + transform->translate(-transform->get_forward() * dt * this->speed); + } + + if (glfwGetKey(this->window, GLFW_KEY_D) == GLFW_PRESS) { + transform->translate(transform->get_right() * dt * this->speed); + } + else if (glfwGetKey(this->window, GLFW_KEY_A) == GLFW_PRESS) { + transform->translate(-transform->get_right() * dt * this->speed); + } + + if (glfwGetKey(this->window, GLFW_KEY_E) == GLFW_PRESS) { + transform->translate(transform->get_up() * dt * this->speed); + } + else if (glfwGetKey(this->window, GLFW_KEY_Q) == GLFW_PRESS) { + transform->translate(-transform->get_up() * dt * this->speed); + } } + +private: + ecs::Entity entity; + GLFWwindow* window; + + float speed; }; -static void load_test_scene(glm::vec2 window_sz) { +static void load_test_scene(GLFWwindow* window, glm::vec2 window_sz) { auto entity = ecs::Coordinator::create_entity(); auto transform = &ecs::Coordinator::add_component(entity, ecs::Transform()); ecs::Coordinator::add_component(entity, gl::Camera( @@ -43,6 +68,7 @@ static void load_test_scene(glm::vec2 window_sz) { (float)Config::get_float("camera.near", 0.1), (float)Config::get_float("camera.far", 1000.0) )); + ecs::Coordinator::add_component(entity, ecs::Behaviour::create(entity, window)); entity = ecs::Coordinator::create_entity(); transform = &ecs::Coordinator::add_component(entity, ecs::Transform()); @@ -51,6 +77,53 @@ static void load_test_scene(glm::vec2 window_sz) { ecs::Coordinator::add_component(entity, gl::Renderable(model)); } +void APIENTRY gl_debug_output( + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) { + + // Ignore non-significant error/warning codes + if (id == 131169 || id == 131185 || id == 131218 || id == 131204) { + return; + } + + std::cerr << "OpenGL debug message (" << id << "): " << message << std::endl; + + switch (source) { + case GL_DEBUG_SOURCE_API: std::cout << "Source: API" << std::endl; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: std::cout << "Source: Window System" << std::endl; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: std::cout << "Source: Shader Compiler" << std::endl; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: std::cout << "Source: Third Party" << std::endl; break; + case GL_DEBUG_SOURCE_APPLICATION: std::cout << "Source: Application" << std::endl; break; + case GL_DEBUG_SOURCE_OTHER: std::cout << "Source: Other" << std::endl; break; + } + + switch (type) { + case GL_DEBUG_TYPE_ERROR: std::cout << "Type: Error" << std::endl; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: std::cout << "Type: Deprecated Behaviour" << std::endl; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: std::cout << "Type: Undefined Behaviour"; break; + case GL_DEBUG_TYPE_PORTABILITY: std::cout << "Type: Portability" << std::endl; break; + case GL_DEBUG_TYPE_PERFORMANCE: std::cout << "Type: Performance" << std::endl; break; + case GL_DEBUG_TYPE_MARKER: std::cout << "Type: Marker" << std::endl; break; + case GL_DEBUG_TYPE_PUSH_GROUP: std::cout << "Type: Push Group" << std::endl; break; + case GL_DEBUG_TYPE_POP_GROUP: std::cout << "Type: Pop Group" << std::endl; break; + case GL_DEBUG_TYPE_OTHER: std::cout << "Type: Other" << std::endl; break; + } + + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: std::cout << "Severity: High" << std::endl; break; + case GL_DEBUG_SEVERITY_MEDIUM: std::cout << "Severity: Medium" << std::endl; break; + case GL_DEBUG_SEVERITY_LOW: std::cout << "Severity: Low" << std::endl; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: std::cout << "Severity: Notification" << std::endl; break; + } + + std::cout << std::endl; +} + int main(int argc, char** argv) { Config::load(argc, argv); @@ -68,6 +141,10 @@ int main(int argc, char** argv) { bool fullscreen = Config::get_boolean("window.fullscreen", false); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + GLFWwindow* window = glfwCreateWindow( window_sz.x, window_sz.y, @@ -86,6 +163,15 @@ int main(int argc, char** argv) { return 1; } + GLint flags; + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) { + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(gl_debug_output, nullptr); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); + } + // Load assets data::Manager::register_type(); data::Manager::register_type(); @@ -97,9 +183,9 @@ int main(int argc, char** argv) { // Init ECS ecs::Coordinator::init(); ecs::Coordinator::register_component(); - ecs::Coordinator::register_component(); // Init behaviour system + ecs::Coordinator::register_component(); auto behaviour_sys = ecs::Coordinator::register_system(); // Init renderer @@ -107,9 +193,9 @@ int main(int argc, char** argv) { ecs::Coordinator::register_component(); auto camera_sys = ecs::Coordinator::register_system(); auto renderable_sys = ecs::Coordinator::register_system(); - auto renderer = new gl::Renderer(camera_sys, renderable_sys); + auto renderer = new gl::Renderer(window_sz, camera_sys, renderable_sys); - load_test_scene(window_sz); + load_test_scene(window, window_sz); // TODO: Fix delta time @@ -122,7 +208,7 @@ int main(int argc, char** argv) { behaviour_sys->update(delta_time); // Render here - renderer->render(); + renderer->render(delta_time); glfwSwapBuffers(window); diff --git a/src/vpg/gl/camera.cpp b/src/vpg/gl/camera.cpp index 9508b7c..0509a1c 100644 --- a/src/vpg/gl/camera.cpp +++ b/src/vpg/gl/camera.cpp @@ -52,7 +52,6 @@ void Camera::update() { this->proj = glm::perspective(glm::radians(this->fov), this->aspect_ratio, this->z_near, this->z_far); this->view = glm::inverse(transform->get_global()); - //this->view = glm::lookAt(glm::vec3(0.0f, 0.0f, -25.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); // Extract frustum planes auto m = glm::transpose(this->proj * this->view); diff --git a/src/vpg/gl/renderer.cpp b/src/vpg/gl/renderer.cpp index 97fec9d..8a49f68 100644 --- a/src/vpg/gl/renderer.cpp +++ b/src/vpg/gl/renderer.cpp @@ -1,24 +1,42 @@ #include #include +#include #include +#include using namespace vpg; using namespace vpg::gl; -vpg::gl::Renderer::Renderer(CameraSystem* camera_sys, RenderableSystem* renderable_sys) { +vpg::gl::Renderer::Renderer(glm::ivec2 size, CameraSystem* camera_sys, RenderableSystem* renderable_sys) { + this->size = size; this->camera_sys = camera_sys; this->renderable_sys = renderable_sys; + this->gbuffer.shader = data::Manager::load("shader.gbuffer"); + this->ssao.shader = data::Manager::load("shader.ssao"); + this->ssao_blur.shader = data::Manager::load("shader.ssao_blur"); this->model_shader = data::Manager::load("shader.model"); this->model_shader->get_shader().bind_uniform_buffer("Palette", 0); + + this->sky_color = glm::vec3(0.1f, 0.5f, 0.8f); + this->wireframe = false; + this->debug_rendering = false; + + glGenVertexArrays(1, &this->screen_va); + + this->create_gbuffer(); + this->create_ssao(); } vpg::gl::Renderer::~Renderer() { + this->destroy_ssao(); + this->destroy_gbuffer(); + glDeleteVertexArrays(1, &this->screen_va); } -void vpg::gl::Renderer::render() { +void vpg::gl::Renderer::render(float dt) { // Get camera if (this->camera_sys->entities.empty()) { return; @@ -27,13 +45,25 @@ void vpg::gl::Renderer::render() { camera.update(); // Opaque pass - glClearColor(0.0f, 0.6f, 0.8f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, this->gbuffer.fbo); + glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glPolygonMode(GL_FRONT_AND_BACK, this->wireframe ? GL_LINE : GL_FILL); glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); glFrontFace(GL_CCW); + // Clear framebuffer + GLuint draw_buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; + glDrawBuffers(1, &draw_buffers[0]); + glClearColor(sky_color.r, sky_color.g, sky_color.b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDrawBuffers(2, &draw_buffers[1]); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDrawBuffers(3, &draw_buffers[0]); + glClear(GL_DEPTH_BUFFER_BIT); + auto model_loc = this->model_shader->get_shader().get_uniform_location("model"); auto view_loc = this->model_shader->get_shader().get_uniform_location("view"); auto proj_loc = this->model_shader->get_shader().get_uniform_location("proj"); @@ -57,4 +87,221 @@ void vpg::gl::Renderer::render() { break; } } + + // SSAO pass + glBindVertexArray(this->screen_va); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + glBindFramebuffer(GL_FRAMEBUFFER, this->ssao.fbo); + glDrawBuffers(1, &draw_buffers[0]); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.position); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.normal); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, this->ssao.noise); + + auto noise_scale = glm::vec2(float(this->size.x) / 4.0f, float(this->size.y) / 4.0f); + + auto& ssao_shader = this->ssao.shader->get_shader(); + ssao_shader.bind(); + glUniform1i(ssao_shader.get_uniform_location("position_tex"), 0); + glUniform1i(ssao_shader.get_uniform_location("normal_tex"), 1); + glUniform1i(ssao_shader.get_uniform_location("noise_tex"), 2); + glUniform3fv(ssao_shader.get_uniform_location("samples"), this->ssao.samples.size(), &this->ssao.samples[0][0]); + glUniform2fv(ssao_shader.get_uniform_location("noise_scale"), 1, &noise_scale[0]); + glUniformMatrix4fv(ssao_shader.get_uniform_location("projection"), 1, GL_FALSE, &camera.get_proj()[0][0]); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // SSAO Blur pass + glBindFramebuffer(GL_FRAMEBUFFER, this->ssao_blur.fbo); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this->ssao.color_buffer); + + auto& ssao_blur_shader = this->ssao_blur.shader->get_shader(); + ssao_blur_shader.bind(); + glUniform1i(ssao_blur_shader.get_uniform_location("ssao_tex"), 0); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // GBuffer pass + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.albedo); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.position); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.normal); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, this->ssao_blur.color_buffer); + + auto& lighting_shader = this->gbuffer.shader->get_shader(); + lighting_shader.bind(); + glUniform1i(lighting_shader.get_uniform_location("albedo_tex"), 0); + glUniform1i(lighting_shader.get_uniform_location("position_tex"), 1); + glUniform1i(lighting_shader.get_uniform_location("normal_tex"), 2); + glUniform1i(lighting_shader.get_uniform_location("ssao_tex"), 3); + glUniform3f(lighting_shader.get_uniform_location("sky_color"), this->sky_color.r, this->sky_color.g, this->sky_color.b); + glUniform1f(lighting_shader.get_uniform_location("z_far"), camera.get_z_far()); + glUniformMatrix4fv(lighting_shader.get_uniform_location("view"), 1, GL_FALSE, &camera.get_view()[0][0]); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // Debug draw on top of screen + glBindFramebuffer(GL_READ_FRAMEBUFFER, this->gbuffer.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, this->size.x, this->size.y, 0, 0, this->size.x, this->size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + if (this->debug_rendering) { + auto vp = camera.get_proj() * camera.get_view(); + Debug::flush(vp, dt); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void vpg::gl::Renderer::resize(glm::ivec2 size) { + this->destroy_gbuffer(); + this->destroy_ssao(); + this->size = size; + this->create_gbuffer(); + this->create_ssao(); +} + +void vpg::gl::Renderer::create_gbuffer() { + // Prepare GBuffer framebuffer and textures + glGenFramebuffers(1, &this->gbuffer.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, this->gbuffer.fbo); + + glGenTextures(1, &this->gbuffer.albedo); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.albedo); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, this->size.x, this->size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->gbuffer.albedo, 0); + + glGenTextures(1, &this->gbuffer.position); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.position); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, this->size.x, this->size.y, 0, GL_RGB, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, this->gbuffer.position, 0); + + glGenTextures(1, &this->gbuffer.normal); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.normal); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, this->size.x, this->size.y, 0, GL_RGB, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, this->gbuffer.normal, 0); + + glGenTextures(1, &this->gbuffer.depth); + glBindTexture(GL_TEXTURE_2D, this->gbuffer.depth); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, this->size.x, this->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, this->gbuffer.depth, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + std::cerr << "vpg::gl::Renderer::create_gbuffer() failed:\n" + << "GBuffer framebuffer is not complete\n"; + abort(); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void vpg::gl::Renderer::destroy_gbuffer() { + glDeleteFramebuffers(1, &this->gbuffer.fbo); + glDeleteTextures(1, &this->gbuffer.albedo); + glDeleteTextures(1, &this->gbuffer.position); + glDeleteTextures(1, &this->gbuffer.normal); + glDeleteTextures(1, &this->gbuffer.depth); +} + +void vpg::gl::Renderer::create_ssao() { + // SSAO framebuffer and textures + glGenFramebuffers(1, &this->ssao.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, this->ssao.fbo); + + glGenTextures(1, &this->ssao.color_buffer); + glBindTexture(GL_TEXTURE_2D, this->ssao.color_buffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, this->size.x, this->size.y, 0, GL_RED, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->ssao.color_buffer, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + std::cerr << "vpg::gl::Renderer::create_ssao() failed:\n" + << "SSAO framebuffer is not complete\n"; + abort(); + } + + // SSAO kernel + std::uniform_real_distribution random_floats(0.0f, 1.0f); + std::default_random_engine generator; + this->ssao.samples.resize(64); + for (int i = 0; i < this->ssao.samples.size(); ++i) { + auto sample = glm::vec3( + random_floats(generator) * 2.0 - 1.0, + random_floats(generator) * 2.0 - 1.0, + random_floats(generator) + ); + sample = glm::normalize(sample) * random_floats(generator); + + float scale = (float)i / (float)this->ssao.samples.size(); + scale = 0.1f + scale * scale * (1.0f - 0.1f); + sample *= scale; + + this->ssao.samples[i] = sample; + } + + // SSAO noise texture + std::vector ssao_noise; + for (int i = 0; i < 16; ++i) { + ssao_noise.push_back({ + random_floats(generator) * 2.0 - 1.0, + random_floats(generator) * 2.0 - 1.0, + 0.0f + }); + } + + glGenTextures(1, &this->ssao.noise); + glBindTexture(GL_TEXTURE_2D, this->ssao.noise); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssao_noise[0]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + // SSAO blur framebuffer and texture + glGenFramebuffers(1, &this->ssao_blur.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, this->ssao_blur.fbo); + + glGenTextures(1, &this->ssao_blur.color_buffer); + glBindTexture(GL_TEXTURE_2D, this->ssao_blur.color_buffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, this->size.x, this->size.y, 0, GL_RED, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->ssao_blur.color_buffer, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + std::cerr << "vpg::gl::Renderer::create_ssao() failed:\n" + << "SSAO blur framebuffer is not complete\n"; + abort(); + } +} + +void vpg::gl::Renderer::destroy_ssao() { + glDeleteFramebuffers(1, &this->ssao.fbo); + glDeleteTextures(1, &this->ssao.color_buffer); + glDeleteTextures(1, &this->ssao.noise); + glDeleteFramebuffers(1, &this->ssao_blur.fbo); + glDeleteTextures(1, &this->ssao_blur.color_buffer); } diff --git a/src/vpg/gl/renderer.hpp b/src/vpg/gl/renderer.hpp index ae57977..2f43e35 100644 --- a/src/vpg/gl/renderer.hpp +++ b/src/vpg/gl/renderer.hpp @@ -8,16 +8,48 @@ namespace vpg::gl { class Renderer { public: - Renderer(CameraSystem* camera_sys, RenderableSystem* renderable_sys); + Renderer(glm::ivec2 size, CameraSystem* camera_sys, RenderableSystem* renderable_sys); ~Renderer(); - void render(); + void render(float dt); + void resize(glm::ivec2 size); private: + void create_gbuffer(); + void destroy_gbuffer(); + void create_ssao(); + void destroy_ssao(); + CameraSystem* camera_sys; RenderableSystem* renderable_sys; data::Handle model_shader; - data::Handle lighting_shader; + + glm::vec3 sky_color; + bool wireframe; + bool debug_rendering; + + glm::ivec2 size; + unsigned int screen_va; + + struct { + unsigned int fbo; + unsigned int albedo, position, normal, depth; + data::Handle shader; + } gbuffer; + + struct { + unsigned int fbo; + unsigned int color_buffer; + unsigned int noise; + std::vector samples; + data::Handle shader; + } ssao; + + struct { + unsigned int fbo; + unsigned int color_buffer; + data::Handle shader; + } ssao_blur; }; } \ No newline at end of file