From 6fd22a85d811f4ab78369334c52ec978e20c153d Mon Sep 17 00:00:00 2001 From: Nigel Barink Date: Fri, 2 Jun 2023 18:01:45 +0200 Subject: [PATCH] HDR skybox --- .gitattributes | 1 + Shaders/HDRMap.fs | 22 ++++ Shaders/HDRMap.vs | 12 ++ Shaders/HDRSkybox.fs | 16 +++ Shaders/HDRSkybox.vs | 16 +++ Textures/photostudio.hdr | 3 + src/Primitives/Skybox.cpp | 1 + src/Primitives/texture.h | 1 + src/Renderer/Renderer.cpp | 225 +++++++++++++++++++++++++++++++++++++- src/Utils.h | 32 ++++++ src/main.cpp | 2 - 11 files changed, 326 insertions(+), 5 deletions(-) create mode 100644 Shaders/HDRMap.fs create mode 100644 Shaders/HDRMap.vs create mode 100644 Shaders/HDRSkybox.fs create mode 100644 Shaders/HDRSkybox.vs create mode 100644 Textures/photostudio.hdr create mode 100644 src/Utils.h diff --git a/.gitattributes b/.gitattributes index bd2ae09..3a7dfcd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ *.mtl filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text +*.hdr filter=lfs diff=lfs merge=lfs -text diff --git a/Shaders/HDRMap.fs b/Shaders/HDRMap.fs new file mode 100644 index 0000000..acca71d --- /dev/null +++ b/Shaders/HDRMap.fs @@ -0,0 +1,22 @@ +#version 460 core +out vec4 FragColor; +in vec3 localPos; + +uniform sampler2D equirectangularMap; + +const vec2 invAtan = vec2(0.1591, 0.3183); + +vec2 SampleSphericalMap(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= invAtan; + uv += 0.5; + return uv; +} + +void main(){ + vec2 uv = SampleSphericalMap(localPos); + vec3 color = texture(equirectangularMap, uv).rgb; + + FragColor = vec4(color,1.0); +} \ No newline at end of file diff --git a/Shaders/HDRMap.vs b/Shaders/HDRMap.vs new file mode 100644 index 0000000..144055e --- /dev/null +++ b/Shaders/HDRMap.vs @@ -0,0 +1,12 @@ +#version 460 core +layout (location = 0) in vec3 aPos; + +out vec3 localPos; + +uniform mat4 projection; +uniform mat4 view; + +void main(){ + localPos = aPos; + gl_Position = projection * view * vec4(localPos, 1.0); +} \ No newline at end of file diff --git a/Shaders/HDRSkybox.fs b/Shaders/HDRSkybox.fs new file mode 100644 index 0000000..12afac5 --- /dev/null +++ b/Shaders/HDRSkybox.fs @@ -0,0 +1,16 @@ +#version 460 core +out vec4 FragColor; + +in vec3 localPos; + +uniform samplerCube environmentMap; + +void main(){ + vec3 envColor = texture(environmentMap, localPos).rgb; + + envColor = envColor /(envColor + vec3(1.0)); + envColor = pow(envColor, vec3(1.0/2.2)); + + FragColor = vec4(envColor,1.0); + +} \ No newline at end of file diff --git a/Shaders/HDRSkybox.vs b/Shaders/HDRSkybox.vs new file mode 100644 index 0000000..df3dc44 --- /dev/null +++ b/Shaders/HDRSkybox.vs @@ -0,0 +1,16 @@ +#version 460 core +layout (location = 0) in vec3 aPos; + +uniform mat4 projection; +uniform mat4 view; + +out vec3 localPos; + +void main(){ + localPos = aPos; + + mat4 rotView = mat4(mat3(view)); + vec4 clipPos = projection * rotView * vec4(localPos,1.0); + + gl_Position = clipPos.xyww; +} \ No newline at end of file diff --git a/Textures/photostudio.hdr b/Textures/photostudio.hdr new file mode 100644 index 0000000..935e555 --- /dev/null +++ b/Textures/photostudio.hdr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c236548680005d1215fef31fdc8c2968cf46250f70c4b56f3e9e4493eb613eb +size 6472197 diff --git a/src/Primitives/Skybox.cpp b/src/Primitives/Skybox.cpp index bf53c03..274b9e9 100644 --- a/src/Primitives/Skybox.cpp +++ b/src/Primitives/Skybox.cpp @@ -17,6 +17,7 @@ void Skybox::Destroy() { std::cout << "Skybox destroyed!" << std::endl; } + void Skybox::loadCubeTextures(const std::vector& texture_faces) { glGenTextures(1, &id); diff --git a/src/Primitives/texture.h b/src/Primitives/texture.h index 6020e1b..8d5803b 100644 --- a/src/Primitives/texture.h +++ b/src/Primitives/texture.h @@ -9,4 +9,5 @@ struct Texture{ std::string path; }; + Texture* CreateTexture(unsigned int width, unsigned int height); \ No newline at end of file diff --git a/src/Renderer/Renderer.cpp b/src/Renderer/Renderer.cpp index a523fa4..5a16f11 100644 --- a/src/Renderer/Renderer.cpp +++ b/src/Renderer/Renderer.cpp @@ -11,9 +11,11 @@ #include #include "../Primitives/Scene.h" #include "../model.h" +#include "../Utils.h" static enum class RenderPass { NONE = 0, SKYBOX, + HDR_SKYBOX, DEFAULT, PBR }; @@ -43,6 +45,84 @@ unsigned int normal; unsigned int metallic; unsigned int roughness; unsigned int ao; + + +unsigned int cubeVAO = 0; +unsigned int cubeVBO = 0; +void renderCube() { + if (cubeVAO == 0) { + float vertices[] = { + // back face + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left + // front face + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + // left face + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + // right face + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left + // bottom face + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right + // top face + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left + 1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left + }; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &cubeVBO); + // fill buffer + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + // link vertex attributes + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + } + + // render Cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); +} + + + +unsigned int envCubemap; +unsigned int envMapVAO; void Renderer::Setup() { // Create ScreenVAO @@ -70,6 +150,8 @@ void Renderer::Setup() // Load shaders shaders[static_cast(RenderPass::SKYBOX)] = Shader(); shaders[static_cast(RenderPass::SKYBOX)].Load("../Shaders/skybox.vs", "../Shaders/Cubemap.fs"); + shaders[static_cast(RenderPass::HDR_SKYBOX)] = Shader(); + shaders[static_cast(RenderPass::HDR_SKYBOX)].Load("../Shaders/HDRSkybox.vs", "../Shaders/HDRSkybox.fs"); shaders[static_cast(RenderPass::DEFAULT)] = Shader(); shaders[static_cast(RenderPass::DEFAULT)].Load("../Shaders/shader.vs", "../Shaders/shader.fs"); shaders[static_cast(RenderPass::PBR)] = Shader(); @@ -83,6 +165,122 @@ void Renderer::Setup() ao = TextureFromFile("../Textures/rusted_iron/ao.png","."); + // Create the skybox from an HDR equirectangular environment map + unsigned int captureFBO, captureRBO; + glGenFramebuffers(1, &captureFBO); + glGenRenderbuffers(1, &captureRBO); + + glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO); + + glGenTextures(1, &envCubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); + for (unsigned int i = 0; i < 6; ++i) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, nullptr); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + 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)) + }; + + auto eqShader = Shader(); + eqShader.Load("../Shaders/HDRMap.vs", "../Shaders/HDRMap.fs"); + + auto hdrTexture = LoadIBL("../Textures/photostudio.hdr"); + + eqShader.use(); + eqShader.setInt("equirectangularMap", 0); + eqShader.setMat4("projection", captureProjection); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, hdrTexture); + + glViewport(0, 0, 512, 512); + + glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + + for (unsigned int i = 0; i < 6; ++i) { + eqShader.setMat4("view", captureViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envCubemap, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + renderCube(); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, 800, 600); // restore viewport; + + std::vector m_skyboxVertices = { + // positions + -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 + }; + unsigned int envMapVBO; + + glGenVertexArrays(1, &envMapVAO); + glBindVertexArray(envMapVAO); + + glGenBuffers(1, &envMapVBO); + glBindBuffer(GL_ARRAY_BUFFER, envMapVBO); + + glBufferData(GL_ARRAY_BUFFER, m_skyboxVertices.size() * sizeof(float), &m_skyboxVertices[0], GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + + glBindVertexArray(0); + + } void Renderer::resize(int width, int height ) { @@ -222,8 +420,6 @@ void renderSphere() { } - - void Renderer::Render(Scene& scene) { @@ -235,6 +431,7 @@ void Renderer::Render(Scene& scene) auto model = glm::mat4(1.0f); // Skybox +#if false glDepthMask(GL_FALSE); Shader shader = shaders.at(static_cast(RenderPass::SKYBOX)); @@ -252,6 +449,28 @@ void Renderer::Render(Scene& scene) scene.skybox.Unbind(); glDepthMask(GL_TRUE); +#endif + + //HDR Environment Skybox + Shader shader = shaders.at(static_cast(RenderPass::HDR_SKYBOX)); + + glDepthFunc(GL_LEQUAL); + + shader.use(); + + shader.setMat4("projection", projection); + shader.setMat4("view", scene.MainCamera.GetViewMatrix()); + + glBindVertexArray(envMapVAO); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); + + glDrawArrays(GL_TRIANGLES, 0, 36); + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindVertexArray(0); + + glDepthFunc(GL_LESS); // Phong lighting @@ -306,7 +525,7 @@ void Renderer::Render(Scene& scene) glActiveTexture(GL_TEXTURE4); glBindTexture(GL_TEXTURE_2D, ao); - + // Render Spheres model = glm::mat4(1.0f); for (int row = 0; row < nrRows; ++row) { for (int col = 0; col < nrColumns; ++col) { diff --git a/src/Utils.h b/src/Utils.h new file mode 100644 index 0000000..5ac4213 --- /dev/null +++ b/src/Utils.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include + + +int LoadIBL(std::string file_path) { + stbi_set_flip_vertically_on_load(true); + int width, height, nrComponents; + float* data = stbi_loadf(file_path.c_str(), &width, &height, &nrComponents, 0); + unsigned int hdrTexture; + + if (data) { + glGenTextures(1, &hdrTexture); + glBindTexture(GL_TEXTURE_2D, hdrTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data); + + 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); + + stbi_image_free(data); + + } + else { + std::cout << "Failed to load HDR image." << std::endl; + } + + return hdrTexture; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d2e147b..bcf51c7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,8 +71,6 @@ public: renderer.Setup(); - - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); while(!window.shouldClose()) {