From 0d819f2b9a71e862bce8799c6b0c1304e62219b3 Mon Sep 17 00:00:00 2001 From: hachem Date: Fri, 22 Aug 2025 12:24:05 +0200 Subject: [add]: add HDRI --- src/Platform/OpenGL/OpenGLFramebuffer.cpp | 1 - src/Platform/OpenGL/OpenGLShader.h | 1 + src/Platform/OpenGL/OpenGLTexture.cpp | 181 +++++++++++++++++++++++++++++- src/Platform/OpenGL/OpenGLTexture.h | 31 +++++ src/Platform/Vulkan/VulkanShader.h | 1 + src/Platform/Vulkan/VulkanTexture.cpp | 49 +++++++- src/Platform/Vulkan/VulkanTexture.h | 27 +++++ 7 files changed, 283 insertions(+), 8 deletions(-) (limited to 'src/Platform') diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.cpp b/src/Platform/OpenGL/OpenGLFramebuffer.cpp index 523d748..3bb62f6 100644 --- a/src/Platform/OpenGL/OpenGLFramebuffer.cpp +++ b/src/Platform/OpenGL/OpenGLFramebuffer.cpp @@ -123,7 +123,6 @@ namespace Donut bool multisample = m_Specification.Samples > 1; - // Attachments if (m_ColorAttachmentSpecifications.size()) { m_ColorAttachments.resize(m_ColorAttachmentSpecifications.size()); diff --git a/src/Platform/OpenGL/OpenGLShader.h b/src/Platform/OpenGL/OpenGLShader.h index e9a7dc2..99a150b 100644 --- a/src/Platform/OpenGL/OpenGLShader.h +++ b/src/Platform/OpenGL/OpenGLShader.h @@ -32,6 +32,7 @@ namespace Donut virtual void MemoryBarrier(uint32_t barriers) override; virtual const std::string& GetName() const override { return m_Name; } + virtual uint32_t GetRendererID() const override { return m_RendererID; } void UploadUniformInt( const std::string& name, int value); void UploadUniformIntArray(const std::string& name, int* values, uint32_t count); diff --git a/src/Platform/OpenGL/OpenGLTexture.cpp b/src/Platform/OpenGL/OpenGLTexture.cpp index e12dea4..d9033dc 100644 --- a/src/Platform/OpenGL/OpenGLTexture.cpp +++ b/src/Platform/OpenGL/OpenGLTexture.cpp @@ -1,5 +1,13 @@ #include "OpenGLTexture.h" +#include "Rendering/Shader.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +#include +#include +#include + namespace Donut { OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height) @@ -18,7 +26,6 @@ namespace Donut glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); } - // TODO(Hachem): Texture Loading OpenGLTexture2D::OpenGLTexture2D(const std::string& path) : m_Path(path) { @@ -69,4 +76,176 @@ namespace Donut GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY; glBindImageTexture(slot, m_RendererID, 0, GL_FALSE, 0, access, m_InternalFormat); } + + OpenGLCubemapTexture::OpenGLCubemapTexture(uint32_t width, uint32_t height) + : m_Width(width), m_Height(height) + { + m_InternalFormat = GL_RGB16F; + m_DataFormat = GL_RGB; + + glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); + + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + + OpenGLCubemapTexture::OpenGLCubemapTexture(const std::string& path) + : m_Path(path) + { + m_Width = 1024; + m_Height = 1024; + m_InternalFormat = GL_RGB16F; + m_DataFormat = GL_RGB; + + glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); + + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + LoadHDRI(path); + } + + OpenGLCubemapTexture::~OpenGLCubemapTexture() + { + glDeleteTextures(1, &m_RendererID); + } + + void OpenGLCubemapTexture::LoadHDRI(const std::string& path) + { + stbi_set_flip_vertically_on_load(true); + int width, height, channels; + float* hdrData = stbi_loadf(path.c_str(), &width, &height, &channels, 3); + + if (!hdrData) + { + DONUT_ERROR("Failed to load HDRI: {}", path); + float defaultSky[6 * 4] = + { + 0.5f, 0.7f, 1.0f, 1.0f, // Right + 0.5f, 0.7f, 1.0f, 1.0f, // Left + 0.5f, 0.7f, 1.0f, 1.0f, // Top + 0.5f, 0.7f, 1.0f, 1.0f, // Bottom + 0.5f, 0.7f, 1.0f, 1.0f, // Front + 0.5f, 0.7f, 1.0f, 1.0f // Back + }; + + for (int i = 0; i < 6; ++i) + glTextureSubImage3D(m_RendererID, 0, 0, 0, i, 1, 1, 1, GL_RGBA, GL_FLOAT, &defaultSky[i * 4]); + return; + } + + ConvertEquirectangularToCubemap(hdrData, width, height); + stbi_image_free(hdrData); + + DONUT_INFO("Successfully loaded HDRI: {} ({}x{})", path, width, height); + } + + void OpenGLCubemapTexture::ConvertEquirectangularToCubemap(float* hdrData, int width, int height) + { + uint32_t captureFBO, captureRBO; + glGenFramebuffers(1, &captureFBO); + glGenRenderbuffers(1, &captureRBO); + + glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_Width, m_Height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO); + + uint32_t hdrTexture; + glGenTextures(1, &hdrTexture); + glBindTexture(GL_TEXTURE_2D, hdrTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, hdrData); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + auto equirectShader = Shader::Create("Assets/Shaders/EquirectToCubemap.glsl"); + if (!equirectShader) + { + DONUT_ERROR("Failed to create equirectangular to cubemap shader"); + return; + } + + uint32_t shaderProgram = equirectShader->GetRendererID(); + + float vertices[] = + { + -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f + }; + + uint32_t cubeVAO, cubeVBO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &cubeVBO); + glBindVertexArray(cubeVAO); + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + + glm::mat4 captureProjection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f); + glm::mat4 captureViews[] = + { + glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)), + glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)), + glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)) + }; + + glUseProgram(shaderProgram); + glUniform1i(glGetUniformLocation(shaderProgram, "equirectangularMap"), 0); + glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, &captureProjection[0][0]); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, hdrTexture); + + glViewport(0, 0, m_Width, m_Height); + glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + for (unsigned int i = 0; i < 6; ++i) + { + glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, &captureViews[i][0][0]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_RendererID, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + } + glBindVertexArray(0); + + glDeleteVertexArrays(1, &cubeVAO); + glDeleteBuffers(1, &cubeVBO); + glDeleteProgram(shaderProgram); + glDeleteTextures(1, &hdrTexture); + glDeleteFramebuffers(1, &captureFBO); + glDeleteRenderbuffers(1, &captureRBO); + } + + void OpenGLCubemapTexture::SetData(void* data, uint32_t size) + { + DONUT_WARN("SetData not implemented for cubemaps"); + } + + void OpenGLCubemapTexture::Bind(uint32_t slot) const + { + glBindTextureUnit(slot, m_RendererID); + } + + void OpenGLCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const + { + GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY; + glBindImageTexture(slot, m_RendererID, 0, GL_TRUE, 0, access, m_InternalFormat); + } }; \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLTexture.h b/src/Platform/OpenGL/OpenGLTexture.h index 1c3cb05..220df09 100644 --- a/src/Platform/OpenGL/OpenGLTexture.h +++ b/src/Platform/OpenGL/OpenGLTexture.h @@ -34,4 +34,35 @@ namespace Donut uint32_t m_RendererID; GLenum m_InternalFormat, m_DataFormat; }; + + class OpenGLCubemapTexture + : public CubemapTexture + { + public: + OpenGLCubemapTexture(uint32_t width, uint32_t height); + OpenGLCubemapTexture(const std::string& path); + virtual ~OpenGLCubemapTexture(); + + virtual uint32_t GetWidth() const override { return m_Width; } + virtual uint32_t GetHeight() const override { return m_Height; } + virtual uint32_t GetRendererID() const override { return m_RendererID; } + + virtual void SetData(void* data, uint32_t size) override; + virtual void Bind(uint32_t slot = 0) const override; + virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override; + + virtual bool operator==(const Texture& other) const override + { + return m_RendererID == other.GetRendererID(); + } + + private: + void LoadHDRI(const std::string& path); + void ConvertEquirectangularToCubemap(float* hdrData, int width, int height); + + std::string m_Path; + uint32_t m_Width, m_Height; + uint32_t m_RendererID; + GLenum m_InternalFormat, m_DataFormat; + }; } diff --git a/src/Platform/Vulkan/VulkanShader.h b/src/Platform/Vulkan/VulkanShader.h index 2b75331..138a742 100644 --- a/src/Platform/Vulkan/VulkanShader.h +++ b/src/Platform/Vulkan/VulkanShader.h @@ -32,6 +32,7 @@ namespace Donut virtual void MemoryBarrier(uint32_t barriers) override; virtual const std::string& GetName() const override { return m_Name; } + virtual uint32_t GetRendererID() const override { return m_RendererID; } void UploadUniformInt(const std::string& name, int value); void UploadUniformIntArray(const std::string& name, int* values, uint32_t count); diff --git a/src/Platform/Vulkan/VulkanTexture.cpp b/src/Platform/Vulkan/VulkanTexture.cpp index 623e540..4145130 100644 --- a/src/Platform/Vulkan/VulkanTexture.cpp +++ b/src/Platform/Vulkan/VulkanTexture.cpp @@ -5,32 +5,69 @@ namespace Donut VulkanTexture2D::VulkanTexture2D(uint32_t width, uint32_t height) : m_Width(width), m_Height(height) { - // TODO(Hachem): Implement Vulkan texture creation with dimensions + m_InternalFormat = 0; + m_DataFormat = 0; + m_RendererID = 0; } VulkanTexture2D::VulkanTexture2D(const std::string& path) : m_Path(path) { - // TODO(Hachem): Implement Vulkan texture creation from file + m_Width = 1; + m_Height = 1; + m_InternalFormat = 0; + m_DataFormat = 0; + m_RendererID = 0; } VulkanTexture2D::~VulkanTexture2D() { - // TODO(Hachem): Implement Vulkan texture cleanup } void VulkanTexture2D::SetData(void* data, uint32_t size) { - // TODO(Hachem): Implement Vulkan texture data setting } void VulkanTexture2D::Bind(uint32_t slot) const { - // TODO(Hachem): Implement Vulkan texture binding } void VulkanTexture2D::BindAsImage(uint32_t slot, bool readOnly) const { - // TODO(Hachem): Implement Vulkan texture image binding + } + + // Vulkan Cubemap Implementation (Placeholder) + VulkanCubemapTexture::VulkanCubemapTexture(uint32_t width, uint32_t height) + : m_Width(width), m_Height(height) + { + m_InternalFormat = 0; + m_DataFormat = 0; + m_RendererID = 0; + } + + VulkanCubemapTexture::VulkanCubemapTexture(const std::string& path) + : m_Path(path) + { + m_Width = 1024; + m_Height = 1024; + m_InternalFormat = 0; + m_DataFormat = 0; + m_RendererID = 0; + } + + VulkanCubemapTexture::~VulkanCubemapTexture() + { + } + + void VulkanCubemapTexture::SetData(void* data, uint32_t size) + { + } + + void VulkanCubemapTexture::Bind(uint32_t slot) const + { + } + + void VulkanCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const + { } }; diff --git a/src/Platform/Vulkan/VulkanTexture.h b/src/Platform/Vulkan/VulkanTexture.h index ad711f9..dd9e1df 100644 --- a/src/Platform/Vulkan/VulkanTexture.h +++ b/src/Platform/Vulkan/VulkanTexture.h @@ -30,4 +30,31 @@ namespace Donut uint32_t m_RendererID; uint32_t m_InternalFormat, m_DataFormat; }; + + class VulkanCubemapTexture + : public CubemapTexture + { + public: + VulkanCubemapTexture(uint32_t width, uint32_t height); + VulkanCubemapTexture(const std::string& path); + virtual ~VulkanCubemapTexture(); + + virtual uint32_t GetWidth() const override { return m_Width; } + virtual uint32_t GetHeight() const override { return m_Height; } + virtual uint32_t GetRendererID() const override { return m_RendererID; } + + virtual void SetData(void* data, uint32_t size) override; + virtual void Bind(uint32_t slot = 0) const override; + virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override; + + virtual bool operator==(const Texture& other) const override + { + return m_RendererID == ((VulkanCubemapTexture&)other).m_RendererID; + } + private: + std::string m_Path; + uint32_t m_Width, m_Height; + uint32_t m_RendererID; + uint32_t m_InternalFormat, m_DataFormat; + }; }; -- cgit v1.3