diff --git a/assets/glsl/batch-3d.frag b/assets/glsl/batch-3d.frag index 1a3c725..ad73e1c 100644 --- a/assets/glsl/batch-3d.frag +++ b/assets/glsl/batch-3d.frag @@ -1,7 +1,10 @@ #version 450 core -layout(location = 0) out vec4 color; +layout(location = 0) out vec4 albedoSpec; // RGB diffuse color +layout(location = 1) out vec4 position; +layout(location = 2) out vec4 normal; +in vec3 v_position; in vec3 v_normal; in vec4 v_color; in vec2 v_textureCoordinates; @@ -46,5 +49,9 @@ void main() case 30: textureColor *= texture(u_textures[30], v_textureCoordinates); break; case 31: textureColor *= texture(u_textures[31], v_textureCoordinates); break; } - color = textureColor; + + albedoSpec.rgb = textureColor.rgb; + albedoSpec.a = 1.0; // TODO: read specular from model material + position = vec4(v_position, 1.0f); + normal = vec4(normalize(v_normal), 1.0f); } diff --git a/assets/glsl/batch-3d.vert b/assets/glsl/batch-3d.vert index d33fc16..edda3c6 100644 --- a/assets/glsl/batch-3d.vert +++ b/assets/glsl/batch-3d.vert @@ -6,6 +6,7 @@ layout(location = 2) in vec4 a_color; layout(location = 3) in vec2 a_textureCoordinates; layout(location = 4) in uint a_textureIndex; +out vec3 v_position; out vec3 v_normal; out vec4 v_color; out vec2 v_textureCoordinates; @@ -14,10 +15,12 @@ out flat uint v_textureIndex; layout(std140, binding = 0) uniform Camera { mat4 u_projectionView; + vec3 u_position; }; void main() { + v_position = a_position; v_normal = a_normal; v_color = a_color; v_textureCoordinates = a_textureCoordinates; diff --git a/assets/glsl/batch-cubemap.vert b/assets/glsl/batch-cubemap.vert index 6bac061..15238cf 100644 --- a/assets/glsl/batch-cubemap.vert +++ b/assets/glsl/batch-cubemap.vert @@ -8,7 +8,7 @@ out vec4 v_color; out vec3 v_textureCoordinates; out flat uint v_textureIndex; -uniform mat4 u_projectionView; +uniform mat4 u_projectionView2; void main() { @@ -16,5 +16,5 @@ void main() v_textureCoordinates = a_position; v_textureIndex = a_textureIndex; // Vclip = Camera projection * Camera view * Model transform * Vlocal - gl_Position = u_projectionView * vec4(a_position, 1.0f); + gl_Position = u_projectionView2 * vec4(a_position, 1.0f); } diff --git a/assets/glsl/lightsource.frag b/assets/glsl/lightsource.frag new file mode 100644 index 0000000..fd2311a --- /dev/null +++ b/assets/glsl/lightsource.frag @@ -0,0 +1,14 @@ +#version 450 core + +layout(location = 0) out vec4 color; + +in vec4 v_color; +in vec3 v_textureCoordinates; +in flat uint v_textureIndex; + +uniform samplerCube u_textures[32]; + +void main() +{ + color = v_color; +} diff --git a/assets/glsl/lightsource.vert b/assets/glsl/lightsource.vert new file mode 100644 index 0000000..4f93d2f --- /dev/null +++ b/assets/glsl/lightsource.vert @@ -0,0 +1,24 @@ +#version 450 core + +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec4 a_color; +layout(location = 2) in uint a_textureIndex; + +out vec4 v_color; +out vec3 v_textureCoordinates; +out flat uint v_textureIndex; + +layout(std140, binding = 0) uniform Camera +{ + mat4 u_projectionView; + vec3 u_position; +}; + +void main() +{ + v_color = a_color; + v_textureCoordinates = a_position; + v_textureIndex = a_textureIndex; + // Vclip = Camera projection * Camera view * Model transform * Vlocal + gl_Position = u_projectionView * vec4(a_position, 1.0f); +} diff --git a/assets/glsl/post-process.frag b/assets/glsl/post-process.frag new file mode 100644 index 0000000..012b9a7 --- /dev/null +++ b/assets/glsl/post-process.frag @@ -0,0 +1,78 @@ +#version 450 core + +layout(location = 0) out vec4 color; + +in vec4 v_color; +in vec2 v_textureCoordinates; +in flat uint v_textureIndex; + +uniform sampler2D u_textures[32]; + +// ----------------------------------------- + +layout(std140, binding = 0) uniform Camera { + mat4 u_projectionView; + vec3 u_position; +}; + +// ----------------------------------------- + +struct DirectionalLight { + vec3 direction; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +const int MAX_DIRECTIONAL_LIGHTS = 32; +layout(std140, binding = 1) uniform DirectionalLights { + DirectionalLight u_directionalLight[MAX_DIRECTIONAL_LIGHTS]; +}; + +// ----------------------------------------- + +void main() +{ + float isObject = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).a; + if (isObject == 0.0f) { + color = vec4(0,0,0,0); + return; + } + + vec3 albedo = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).rgb; + float specular = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).a; + vec3 position = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).rgb; + vec3 normal = texture(u_textures[v_textureIndex + 2], v_textureCoordinates).rgb; + + + vec3 lighting = vec3(0.0f, 0.0f, 0.0f);//albedo * v_color.xyz; + vec3 viewDirection = normalize(u_position - position); + + // Loop through all directional lights + for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; ++i) { + // Diffuse + vec3 lightDirection = normalize(-u_directionalLight[i].direction); + float diffuse = max(dot(normal, lightDirection), 0.0f); + + // Specular + vec3 reflectionDirection = reflect(-lightDirection, normal); + float specular = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 32); + + lighting += + (albedo * u_directionalLight[i].ambient) + + (albedo * diffuse * u_directionalLight[i].diffuse) + + (specular * u_directionalLight[i].specular); + } + + // Loop through all point lights + // TODO + // vec3 lightDirection = normalize(lightPosition - position); + // float diffuse = max(dot(normal, lightDirection), 0.0f); + // lighting += diffuse * albedo * u_lightColor; + + // Loop through all spot lights + // TODO + + color = vec4(lighting, 1.0f); +} diff --git a/assets/glsl/post-process.vert b/assets/glsl/post-process.vert new file mode 100644 index 0000000..a1db8f8 --- /dev/null +++ b/assets/glsl/post-process.vert @@ -0,0 +1,25 @@ +#version 450 core + +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec4 a_color; +layout(location = 2) in vec2 a_textureCoordinates; +layout(location = 3) in uint a_textureIndex; + +out vec4 v_color; +out vec2 v_textureCoordinates; +out flat uint v_textureIndex; + +layout(std140, binding = 0) uniform Camera +{ + mat4 u_projectionView; + vec3 u_position; +}; + +void main() +{ + v_color = a_color; + v_textureCoordinates = a_textureCoordinates; + v_textureIndex = a_textureIndex; + // Vclip = Model transform * Vlocal + gl_Position = vec4(a_position, 1.0f); +} diff --git a/src/inferno/application.cpp b/src/inferno/application.cpp index eff7f3c..39e8800 100644 --- a/src/inferno/application.cpp +++ b/src/inferno/application.cpp @@ -24,6 +24,7 @@ #include "inferno/render/context.h" #include "inferno/render/framebuffer.h" #include "inferno/render/uniformbuffer.h" +#include "inferno/system/rendersystem.h" // #include "inferno/render/gltf.h" #include "inferno/asset/shader.h" #include "inferno/asset/texture.h" @@ -53,27 +54,7 @@ Application::Application() Input::initialize(); RenderCommand::initialize(); - - m_framebuffer = Framebuffer::create({ - .attachments = { Framebuffer::Type::Color, Framebuffer::Type::Depth }, - .width = m_window->getWidth(), - .height = m_window->getHeight(), - .clearColor = { 0.2f, 0.3f, 0.3f, 1.0f }, - .clearBit = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, - }); - - m_screenFramebuffer = Framebuffer::create({ - .renderToScreen = true, - .clearColor = { 1.0f, 1.0f, 1.0f, 1.0f }, - .clearBit = GL_COLOR_BUFFER_BIT, - }); - - Uniformbuffer::the().setLayout( - "Camera", 0, - { - { BufferElementType::Mat4, "u_projectionView" }, - }); - Uniformbuffer::the().create("Camera"); + RenderSystem::the().initialize(m_window->getWidth(), m_window->getHeight()); m_scene = std::make_shared(); m_scene->initialize(); @@ -104,6 +85,8 @@ Application::~Application() Renderer2D::destroy(); Renderer3D::destroy(); RendererCubemap::destroy(); + RendererPostProcess::destroy(); + RendererLightCube::destroy(); RenderCommand::destroy(); AssetManager::destroy(); // Input::destroy(); @@ -167,10 +150,6 @@ int Application::run() // offset #endif - constexpr glm::vec4 vectorOne { 1.0f, 1.0f, 1.0f, 1.0f }; - constexpr glm::mat4 matIdentity { 1.0f }; - constexpr TransformComponent transformIdentity; - double gametime = 0; uint64_t frames = 0; @@ -196,33 +175,7 @@ int Application::run() // --------------------------------- // Render - m_framebuffer->bind(); - - RenderCommand::clearColor(m_framebuffer->clearColor()); - RenderCommand::clearBit(m_framebuffer->clearBit()); - - render(); - m_scene->render(); - // RendererCharacter::the().drawCharacter(character, f->texture()); - - m_framebuffer->unbind(); - - // --------------------------------- - // Framebuffer - - m_screenFramebuffer->bind(); - - RenderCommand::clearColor(m_screenFramebuffer->clearColor()); - RenderCommand::clearBit(m_screenFramebuffer->clearBit()); - - Renderer2D::the().setEnableDepthBuffer(false); - Uniformbuffer::the().setValue("Camera", "u_projectionView", matIdentity); - Renderer2D::the().drawQuad(transformIdentity, vectorOne, m_framebuffer->texture(0)); - Renderer2D::the().endScene(); - Renderer2D::the().setEnableDepthBuffer(true); - - m_screenFramebuffer->unbind(); m_window->render(); } @@ -259,8 +212,7 @@ bool Application::onWindowResize(WindowResizeEvent& e) { ruc::info("WindowResizeEvent {}x{}", e.getWidth(), e.getHeight()); - RenderCommand::setViewport(0, 0, e.getWidth(), e.getHeight()); - m_framebuffer->resize(e.getWidth(), e.getHeight()); + RenderSystem::the().resize(e.getWidth(), e.getHeight()); return true; } diff --git a/src/inferno/application.h b/src/inferno/application.h index 9804989..4a37833 100644 --- a/src/inferno/application.h +++ b/src/inferno/application.h @@ -51,8 +51,6 @@ private: float m_lastFrameTime { 0.0f }; std::unique_ptr m_window; - std::shared_ptr m_framebuffer; - std::shared_ptr m_screenFramebuffer; std::shared_ptr m_scene; // diff --git a/src/inferno/component/cubemap-component.cpp b/src/inferno/component/cubemap-component.cpp index 245c5be..433ef79 100644 --- a/src/inferno/component/cubemap-component.cpp +++ b/src/inferno/component/cubemap-component.cpp @@ -22,6 +22,9 @@ void fromJson(const ruc::Json& json, CubemapComponent& value) if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) { value.texture = AssetManager::the().load(json.at("texture").asString()); } + if (json.exists("isLight")) { + json.at("isLight").getTo(value.isLight); + } } } // namespace Inferno diff --git a/src/inferno/component/cubemap-component.h b/src/inferno/component/cubemap-component.h index 011836c..c461576 100644 --- a/src/inferno/component/cubemap-component.h +++ b/src/inferno/component/cubemap-component.h @@ -18,6 +18,7 @@ namespace Inferno { struct CubemapComponent { glm::vec4 color { 1.0f }; std::shared_ptr texture; + bool isLight { false }; }; void fromJson(const ruc::Json& json, CubemapComponent& value); diff --git a/src/inferno/render/buffer.h b/src/inferno/render/buffer.h index fd9a46c..aa52f71 100644 --- a/src/inferno/render/buffer.h +++ b/src/inferno/render/buffer.h @@ -10,7 +10,6 @@ #include // int32_t, uint8_t, uint32_t #include // std::shared_ptr #include -#include // std::pair #include namespace Inferno { diff --git a/src/inferno/render/framebuffer.cpp b/src/inferno/render/framebuffer.cpp index 55bd3c7..2a606d7 100644 --- a/src/inferno/render/framebuffer.cpp +++ b/src/inferno/render/framebuffer.cpp @@ -36,6 +36,14 @@ Framebuffer::~Framebuffer() glDeleteFramebuffers(1, &m_id); } +void Framebuffer::copyBuffer(std::shared_ptr from, std::shared_ptr to, uint32_t bits, uint32_t filter) +{ + glBindFramebuffer(GL_READ_FRAMEBUFFER, from->m_id); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // write to default framebuffer + glBlitFramebuffer(0, 0, from->m_width, from->m_height, 0, 0, to->m_width, to->m_height, bits, filter); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + // ----------------------------------------- void Framebuffer::bind() const @@ -86,22 +94,47 @@ void Framebuffer::createTextures() bind(); auto it = m_attachments.begin(); - uint8_t color_attachment = 0; + m_colorAttachmentCount = 0; size_t size = m_attachments.size(); m_textures.resize(size); for (size_t i = 0; i < size; ++i) { TypeProperties type = *(it + i); - if (type.type == Type::Color) { + if (type.type == Type::RGB8) { // Set color attachment 0 out of 32 - m_textures[i] = (TextureFramebuffer::create( - "", m_width, m_height, GL_RGB, GL_RGB)); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + color_attachment, GL_TEXTURE_2D, m_textures[i]->id(), 0); + m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGB8, GL_RGB); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0); + m_colorAttachmentCount++; + continue; + } + + if (type.type == Type::RGBA8) { // Color + // Set color attachment 0 out of 32 + m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA8, GL_RGBA); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0); + m_colorAttachmentCount++; + continue; + } + + if (type.type == Type::RGBA16F) { + // Set color attachment 0 out of 32 + m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA16F, GL_RGBA, GL_FLOAT); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0); + m_colorAttachmentCount++; + continue; + } + + if (type.type == Type::RGBA32F) { + // Set color attachment 0 out of 32 + m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA32F, GL_RGBA, GL_FLOAT); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0); + m_colorAttachmentCount++; + continue; } // This combined texture is required for older GPUs - if (type.type == Type::Depth24Stencil8) { + if (type.type == Type::Depth24Stencil8) { // Depth m_textures[i] = (TextureFramebuffer::create( "", m_width, m_height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8)); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0); @@ -117,7 +150,7 @@ void Framebuffer::createTextures() } } - VERIFY(color_attachment <= 32, "maximum color attachments was exceeded: {}/32", color_attachment); + VERIFY(m_colorAttachmentCount <= 32, "maximum color attachments was exceeded: {}/32", m_colorAttachmentCount); check(); unbind(); diff --git a/src/inferno/render/framebuffer.h b/src/inferno/render/framebuffer.h index bf86311..af76d07 100644 --- a/src/inferno/render/framebuffer.h +++ b/src/inferno/render/framebuffer.h @@ -24,11 +24,14 @@ public: None = 0, // Color - RGBA8 = 1, + RGB8 = 1, + RGBA8 = 2, + RGBA16F = 3, + RGBA32F = 4, // Depth/stencil - Depth32F = 2, - Depth24Stencil8 = 3, + Depth24Stencil8 = 5, + Depth32F = 6, // Defaults Color = RGBA8, @@ -59,12 +62,14 @@ public: // Factory function static std::shared_ptr create(const Properties& properties); + static void copyBuffer(std::shared_ptr from, std::shared_ptr to, uint32_t bits, uint32_t filter); void bind() const; void unbind() const; bool check() const; void resize(uint32_t width, uint32_t height); + uint8_t colorAttachmentCount() const { return m_colorAttachmentCount; } uint32_t id() const { return m_id; } uint32_t width() const { return m_width; } uint32_t height() const { return m_height; } @@ -87,6 +92,7 @@ private: private: bool m_renderToScreen { false }; + uint8_t m_colorAttachmentCount { 1 }; uint32_t m_id { 0 }; uint32_t m_width { 0 }; uint32_t m_height { 0 }; diff --git a/src/inferno/render/render-command.cpp b/src/inferno/render/render-command.cpp index e73d81a..e0c6cda 100644 --- a/src/inferno/render/render-command.cpp +++ b/src/inferno/render/render-command.cpp @@ -4,10 +4,12 @@ * SPDX-License-Identifier: MIT */ -#include // std::shared_ptr +#include // int32_t, uint32_t +#include // std::shared_ptr #include "glad/glad.h" #include "ruc/format/log.h" +#include "ruc/meta/assert.h" #include "inferno/render/buffer.h" #include "inferno/render/render-command.h" @@ -57,6 +59,26 @@ void RenderCommand::setDepthTest(bool enabled) enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST); } +void RenderCommand::setColorAttachmentCount(uint32_t count) +{ + static constexpr uint32_t colorAttachments[] = { + GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7, GL_COLOR_ATTACHMENT8, + GL_COLOR_ATTACHMENT9, GL_COLOR_ATTACHMENT10, GL_COLOR_ATTACHMENT11, + GL_COLOR_ATTACHMENT12, GL_COLOR_ATTACHMENT13, GL_COLOR_ATTACHMENT14, + GL_COLOR_ATTACHMENT15, GL_COLOR_ATTACHMENT16, GL_COLOR_ATTACHMENT17, + GL_COLOR_ATTACHMENT18, GL_COLOR_ATTACHMENT19, GL_COLOR_ATTACHMENT20, + GL_COLOR_ATTACHMENT21, GL_COLOR_ATTACHMENT22, GL_COLOR_ATTACHMENT23, + GL_COLOR_ATTACHMENT24, GL_COLOR_ATTACHMENT25, GL_COLOR_ATTACHMENT26, + GL_COLOR_ATTACHMENT27, GL_COLOR_ATTACHMENT28, GL_COLOR_ATTACHMENT29, + GL_COLOR_ATTACHMENT30, GL_COLOR_ATTACHMENT31 + }; + static constexpr uint32_t maxCount = sizeof(colorAttachments) / sizeof(colorAttachments[0]); + VERIFY(count > 0 && count <= maxCount, "incorrect colorbuffer count: {}/{}", count, maxCount); + glDrawBuffers(static_cast(count), colorAttachments); // Multiple Render Targets (MRT) +} + bool RenderCommand::depthTest() { unsigned char depthTest = GL_FALSE; diff --git a/src/inferno/render/render-command.h b/src/inferno/render/render-command.h index 2430b4c..9214621 100644 --- a/src/inferno/render/render-command.h +++ b/src/inferno/render/render-command.h @@ -6,7 +6,8 @@ #pragma once -#include // std::shadred_ptr +#include // int32_t, uint32_t +#include // std::shadred_ptr #include "glm/ext/vector_float4.hpp" // glm::vec4 @@ -25,6 +26,7 @@ public: static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height); static void setDepthTest(bool enabled); + static void setColorAttachmentCount(uint32_t count); static bool depthTest(); static int32_t textureUnitAmount(); diff --git a/src/inferno/render/renderer.cpp b/src/inferno/render/renderer.cpp index bac6ea0..d59c8b1 100644 --- a/src/inferno/render/renderer.cpp +++ b/src/inferno/render/renderer.cpp @@ -171,6 +171,7 @@ void Renderer::flush() // Render bool depthTest = RenderCommand::depthTest(); RenderCommand::setDepthTest(m_enableDepthBuffer); + RenderCommand::setColorAttachmentCount(m_colorAttachmentCount); RenderCommand::drawIndexed(m_vertexArray, m_elementIndex); RenderCommand::setDepthTest(depthTest); @@ -197,6 +198,11 @@ void Renderer::nextBatch() // ----------------------------------------- Renderer2D::Renderer2D(s) +{ + Renderer2D::initialize(); +} + +void Renderer2D::initialize() { Renderer::initialize(); @@ -216,6 +222,8 @@ Renderer2D::Renderer2D(s) // --------------------------------- // GPU + m_enableDepthBuffer = false; + // Create vertex buffer auto vertexBuffer = std::make_shared(sizeof(QuadVertex) * maxVertices); vertexBuffer->setLayout({ @@ -275,12 +283,17 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color, void Renderer2D::loadShader() { - m_shader = AssetManager::the().load("assets/glsl/batch-2d"); + m_shader = AssetManager::the().load("assets/glsl/post-process"); } // ----------------------------------------- RendererCubemap::RendererCubemap(s) +{ + RendererCubemap::initialize(); +} + +void RendererCubemap::initialize() { Renderer::initialize(); @@ -354,10 +367,10 @@ void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraVie // x x x 0 // x x x 0 // 0 0 0 1 - cameraView = glm::mat4(glm::mat3(cameraView)); + // cameraView = glm::mat4(glm::mat3(cameraView)); m_shader->bind(); - m_shader->setFloat("u_projectionView", cameraProjection * cameraView); + m_shader->setFloat("u_projectionView2", cameraProjection * cameraView); m_shader->unbind(); } @@ -482,6 +495,7 @@ Renderer3D::Renderer3D(s) // GPU m_enableDepthBuffer = true; + m_colorAttachmentCount = 3; // Create vertex buffer auto vertexBuffer = std::make_shared(sizeof(Vertex) * maxVertices); @@ -501,8 +515,8 @@ void Renderer3D::drawModel(std::span vertices, std::span maxVertices || m_elementIndex + elements.size() > maxElements) { @@ -512,9 +526,10 @@ void Renderer3D::drawModel(std::span vertices, std::spanposition = transform.transform * glm::vec4(vertex.position, 1.0f); - m_vertexBufferPtr->normal = vertex.normal; + m_vertexBufferPtr->normal = normalMatrix * vertex.normal; // take non-uniform scaling into consideration m_vertexBufferPtr->color = color; m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates; m_vertexBufferPtr->textureIndex = textureUnitIndex; @@ -565,4 +580,46 @@ void Renderer3D::startBatch() m_elementBufferPtr = m_elementBufferBase; } +// ----------------------------------------- + +void RendererPostProcess::drawQuad(const TransformComponent& transform, std::shared_ptr albedo, std::shared_ptr position, std::shared_ptr normal) +{ + nextBatch(); + + constexpr glm::vec2 textureCoordinates[] = { + { 0.0f, 0.0f }, + { 1.0f, 0.0f }, + { 1.0f, 1.0f }, + { 0.0f, 1.0f } + }; + + uint32_t textureUnitIndex = addTextureUnit(albedo); + addTextureUnit(position); + addTextureUnit(normal); + + // Add the quads 4 vertices + for (uint32_t i = 0; i < vertexPerQuad; i++) { + m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i]; + m_vertexBufferPtr->textureCoordinates = textureCoordinates[i]; + m_vertexBufferPtr->textureIndex = textureUnitIndex; + m_vertexBufferPtr++; + } + + m_vertexIndex += vertexPerQuad; + m_elementIndex += elementPerQuad; +} + +void RendererPostProcess::loadShader() +{ + ruc::error("POSTPROCESSING!"); + m_shader = AssetManager::the().load("assets/glsl/post-process"); +} + +// ----------------------------------------- + +void RendererLightCube::loadShader() +{ + m_shader = AssetManager::the().load("assets/glsl/lightsource"); +} + } // namespace Inferno diff --git a/src/inferno/render/renderer.h b/src/inferno/render/renderer.h index a2dd0be..f0e4936 100644 --- a/src/inferno/render/renderer.h +++ b/src/inferno/render/renderer.h @@ -112,6 +112,7 @@ protected: // GPU objects bool m_enableDepthBuffer { true }; + uint32_t m_colorAttachmentCount { 1 }; std::shared_ptr m_shader; std::shared_ptr m_vertexArray; }; @@ -124,7 +125,7 @@ protected: // ------------------------------------- -class Renderer2D final +class Renderer2D : public Renderer , public ruc::Singleton { public: @@ -138,15 +139,20 @@ public: void drawQuad(const TransformComponent& transform, glm::vec4 color, std::shared_ptr texture); void drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr texture); -private: - void loadShader() override; +protected: + Renderer2D() { Renderer2D::initialize(); } // Needed for derived classes + + void initialize(); // Default quad vertex positions glm::vec4 m_vertexPositions[vertexPerQuad]; + +private: + virtual void loadShader() override; }; // ------------------------------------- -class RendererCubemap final +class RendererCubemap : public Renderer , public ruc::Singleton { public: @@ -163,8 +169,13 @@ public: void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr texture); void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr texture); +protected: + RendererCubemap() { RendererCubemap::initialize(); } // Needed for derived classes + + void initialize(); + private: - void loadShader() override; + virtual void loadShader() override; // Default cubemap vertex positions glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube]; @@ -212,4 +223,47 @@ private: uint32_t* m_elementBufferPtr { nullptr }; }; +// ----------------------------------------- + +class RendererPostProcess final + : public Renderer2D + , public ruc::Singleton { +public: + RendererPostProcess(ruc::Singleton::s) + : Renderer2D() + { + } + virtual ~RendererPostProcess() = default; + + using Singleton::the; + using Singleton::destroy; + + void drawQuad(const TransformComponent& transform, std::shared_ptr albedo, std::shared_ptr position, std::shared_ptr normal); + +private: + virtual void loadShader() override; +}; + +// ----------------------------------------- + +class RendererLightCube final + : public RendererCubemap + , public ruc::Singleton { +public: + RendererLightCube(ruc::Singleton::s) + : RendererCubemap() + { + m_enableDepthBuffer = true; + } + virtual ~RendererLightCube() = default; + + using Singleton::the; + using Singleton::destroy; + + void beginScene(glm::mat4, glm::mat4) override {} + +private: + virtual void loadShader() override; +}; + } // namespace Inferno diff --git a/src/inferno/render/uniformbuffer.h b/src/inferno/render/uniformbuffer.h index 23979dc..8eb3c9a 100644 --- a/src/inferno/render/uniformbuffer.h +++ b/src/inferno/render/uniformbuffer.h @@ -13,6 +13,7 @@ #include "glad/glad.h" #include "glm/ext/matrix_float2x2.hpp" // glm::mat2 #include "glm/ext/matrix_float3x3.hpp" // glm::mat3 +#include "glm/ext/vector_float3.hpp" // glm::vec3 #include "ruc/singleton.h" #include "inferno/render/buffer.h" @@ -25,6 +26,23 @@ namespace Inferno { +// Uniform block layouts, using std140 memory layout rules + +#define MAX_DIRECTIONAL_LIGHTS 32 +struct UniformDirectionalLight { + glm::vec3 direction { 0 }; + float __padding0 { 0 }; + + glm::vec3 ambient { 0 }; + float __padding1 { 0 }; + glm::vec3 diffuse { 0 }; + float __padding2 { 0 }; + glm::vec3 specular { 0 }; + float __padding3 { 0 }; +}; + +// ----------------------------------------- + struct UniformbufferBlock { uint32_t id { 0 }; uint32_t size { 0 }; diff --git a/src/inferno/scene/scene.cpp b/src/inferno/scene/scene.cpp index 314f10f..7035119 100644 --- a/src/inferno/scene/scene.cpp +++ b/src/inferno/scene/scene.cpp @@ -9,7 +9,7 @@ #include // std::numeric_limits #include // std::pair -#include "entt/entity/entity.hpp" // ent::entity +#include "entt/entity/fwd.hpp" // ent::entity #include "ruc/file.h" #include "ruc/format/log.h" #include "ruc/json/json.h" @@ -25,6 +25,7 @@ #include "inferno/component/tagcomponent.h" #include "inferno/component/textareacomponent.h" #include "inferno/component/transformcomponent.h" +#include "inferno/render/renderer.h" #include "inferno/render/uniformbuffer.h" #include "inferno/scene/scene.h" #include "inferno/script/nativescript.h" @@ -84,17 +85,7 @@ void Scene::update(float deltaTime) void Scene::render() { - auto [projection, view] = CameraSystem::the().projectionView(); - Uniformbuffer::the().setValue("Camera", "u_projectionView", projection * view); - RendererCubemap::the().beginScene(projection, view); // camera, lights, environment - RenderSystem::the().render(); - TextAreaSystem::the().render(); - - RendererCubemap::the().endScene(); - Renderer3D::the().endScene(); - Renderer2D::the().endScene(); - RendererFont::the().endScene(); } void Scene::destroy() diff --git a/src/inferno/system/camerasystem.cpp b/src/inferno/system/camerasystem.cpp index a7fb14c..d82eeee 100644 --- a/src/inferno/system/camerasystem.cpp +++ b/src/inferno/system/camerasystem.cpp @@ -53,10 +53,23 @@ std::pair CameraSystem::projectionView() } VERIFY_NOT_REACHED(); + return {}; +} + +glm::vec3 CameraSystem::translate() +{ + auto view = m_registry->view(); + for (auto [entity, transform, camera] : view.each()) { + return transform.translate; + } + + VERIFY_NOT_REACHED(); return {}; } +// ----------------------------------------- + void CameraSystem::updateOrthographic(TransformComponent& transform, CameraComponent& camera) { // Update camera matrix diff --git a/src/inferno/system/camerasystem.h b/src/inferno/system/camerasystem.h index 466a85b..20b311e 100644 --- a/src/inferno/system/camerasystem.h +++ b/src/inferno/system/camerasystem.h @@ -32,6 +32,7 @@ public: * @brief Return a pair from the camera component: { projection, view } */ std::pair projectionView(); + glm::vec3 translate(); void setRegistry(std::shared_ptr registry) { m_registry = registry; }; diff --git a/src/inferno/system/rendersystem.cpp b/src/inferno/system/rendersystem.cpp index 1cf7529..e7c45b7 100644 --- a/src/inferno/system/rendersystem.cpp +++ b/src/inferno/system/rendersystem.cpp @@ -4,41 +4,148 @@ * SPDX-License-Identifier: MIT */ -#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians -#include "inferno/component/model-component.h" +#include // int32_t + +#include "glad/glad.h" #include "ruc/format/log.h" #include "inferno/component/cubemap-component.h" #include "inferno/component/model-component.h" #include "inferno/component/spritecomponent.h" #include "inferno/component/transformcomponent.h" +#include "inferno/render/framebuffer.h" +#include "inferno/render/render-command.h" #include "inferno/render/renderer.h" +#include "inferno/render/uniformbuffer.h" +#include "inferno/system/camerasystem.h" #include "inferno/system/rendersystem.h" +#include "inferno/system/textareasystem.h" namespace Inferno { RenderSystem::RenderSystem(s) { - ruc::info("RenderSystem initialized"); } RenderSystem::~RenderSystem() { } +// ----------------------------------------- + +void RenderSystem::initialize(uint32_t width, uint32_t height) +{ + m_framebuffer = Framebuffer::create({ + .attachments = { Framebuffer::Type::Color, Framebuffer::Type::RGBA16F, Framebuffer::Type::RGBA16F, Framebuffer::Type::Depth }, + .width = width, + .height = height, + .clearColor = { 0.0f, 0.0f, 0.0f, 0.0f }, + .clearBit = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, + }); + + m_screenFramebuffer = Framebuffer::create({ + .renderToScreen = true, + .clearColor = { 1.0f, 1.0f, 1.0f, 1.0f }, + .clearBit = GL_COLOR_BUFFER_BIT, + }); + + Uniformbuffer::the().setLayout( + "Camera", 0, + { + { BufferElementType::Mat4, "u_projectionView" }, + { BufferElementType::Vec3, "u_position" }, + }); + Uniformbuffer::the().create("Camera"); + + Uniformbuffer::the().setLayout( + "DirectionalLights", + { + .size = sizeof(UniformDirectionalLight) * MAX_DIRECTIONAL_LIGHTS, + .bindingPoint = 1, + .uniformLocations = { + { "u_directionalLight", 0 }, + }, + }); + Uniformbuffer::the().create("DirectionalLights"); + + ruc::info("RenderSystem initialized"); +} + void RenderSystem::render() { - auto quadView = m_registry->view(); + static constexpr TransformComponent transformIdentity; - for (auto [entity, transform, sprite] : quadView.each()) { - Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture); - } + // --------------------------------- + // Deferred rendering to a framebuffer - auto cubemapView = m_registry->view(); + framebufferSetup(m_framebuffer); - for (auto [entity, transform, cubemap] : cubemapView.each()) { - RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture); - } + renderGeometry(); + + framebufferTeardown(m_framebuffer); + + // --------------------------------- + // Forward rendering to the screen + + framebufferSetup(m_screenFramebuffer); + + renderSkybox(); + + // Render 3D geometry post-processing + RendererPostProcess::the().drawQuad(transformIdentity, m_framebuffer->texture(0), m_framebuffer->texture(1), m_framebuffer->texture(2)); + RendererPostProcess::the().endScene(); + + // Visual representation of light sources + Framebuffer::copyBuffer(m_framebuffer, m_screenFramebuffer, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + renderLightCubes(); + + // Render 2D, UI + renderOverlay(); + + framebufferTeardown(m_screenFramebuffer); +} + +void RenderSystem::resize(int32_t width, int32_t height) +{ + RenderCommand::setViewport(0, 0, width, height); + m_framebuffer->resize(width, height); + m_screenFramebuffer->resize(width, height); +} + +// ----------------------------------------- + +void RenderSystem::framebufferSetup(std::shared_ptr framebuffer) +{ + framebuffer->bind(); + + RenderCommand::setColorAttachmentCount(framebuffer->colorAttachmentCount()); + RenderCommand::clearColor(framebuffer->clearColor()); + RenderCommand::clearBit(framebuffer->clearBit()); +} + +void RenderSystem::framebufferTeardown(std::shared_ptr framebuffer) +{ + framebuffer->unbind(); + + RenderCommand::setColorAttachmentCount(1); +} + +void RenderSystem::renderGeometry() +{ + auto [projection, view] = CameraSystem::the().projectionView(); + auto translate = CameraSystem::the().translate(); + Uniformbuffer::the().setValue("Camera", "u_projectionView", projection * view); + Uniformbuffer::the().setValue("Camera", "u_position", translate); + + static UniformDirectionalLight directionalLights[1] = { + { + .direction = { -8.0f, -8.0f, -8.0f }, + .ambient = { 0.1f, 0.1f, 0.1f }, + .diffuse = { 1.0f, 1.0f, 1.0f }, + .specular = { 1.0f, 1.0f, 1.0f }, + }, + }; + Uniformbuffer::the().setValue("DirectionalLights", "u_directionalLight", directionalLights); auto modelView = m_registry->view(); @@ -49,6 +156,54 @@ void RenderSystem::render() model.color, model.model->texture() ? model.model->texture() : model.texture); } + + Renderer3D::the().endScene(); +} + +void RenderSystem::renderSkybox() +{ + auto [projection, view] = CameraSystem::the().projectionView(); + RendererCubemap::the().beginScene(projection, view); // camera, lights, environment + + auto cubemapView = m_registry->view(); + + for (auto [entity, transform, cubemap] : cubemapView.each()) { + if (!cubemap.isLight) { + RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture); + } + } + + RendererCubemap::the().endScene(); +} + +void RenderSystem::renderLightCubes() +{ + auto [projection, view] = CameraSystem::the().projectionView(); + RendererLightCube::the().beginScene(projection, view); // camera, lights, environment + + auto cubemapView = m_registry->view(); + + for (auto [entity, transform, cubemap] : cubemapView.each()) { + if (cubemap.isLight) { + RendererLightCube::the().drawCubemap(transform, cubemap.color, nullptr); + } + } + + RendererLightCube::the().endScene(); +} + +void RenderSystem::renderOverlay() +{ + auto quadView = m_registry->view(); + + for (auto [entity, transform, sprite] : quadView.each()) { + Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture); + } + + Renderer2D::the().endScene(); + + TextAreaSystem::the().render(); + RendererFont::the().endScene(); } } // namespace Inferno diff --git a/src/inferno/system/rendersystem.h b/src/inferno/system/rendersystem.h index d074f0f..21a64c1 100644 --- a/src/inferno/system/rendersystem.h +++ b/src/inferno/system/rendersystem.h @@ -6,25 +6,39 @@ #pragma once -#include //std::shared_ptr +#include // int32_t, uint32_t +#include //std::shared_ptr -#include "entt/entity/registry.hpp" // entt::entity, entt::registry -#include "ruc/singleton.h" +#include "entt/entity/fwd.hpp" // entt::registry -#include "inferno/render/renderer.h" +#include "ruc/singleton.h" namespace Inferno { +class Framebuffer; + class RenderSystem final : public ruc::Singleton { public: RenderSystem(s); virtual ~RenderSystem(); + void initialize(uint32_t width, uint32_t height); void render(); + void resize(int32_t width, int32_t height); + void setRegistry(std::shared_ptr registry) { m_registry = registry; }; private: + void framebufferSetup(std::shared_ptr framebuffer); + void framebufferTeardown(std::shared_ptr framebuffer); + void renderGeometry(); + void renderSkybox(); + void renderLightCubes(); + void renderOverlay(); + + std::shared_ptr m_framebuffer; + std::shared_ptr m_screenFramebuffer; std::shared_ptr m_registry; };