HDR skybox

This commit is contained in:
Nigel Barink 2023-06-02 18:01:45 +02:00
parent 5006149c8d
commit 6fd22a85d8
11 changed files with 326 additions and 5 deletions

1
.gitattributes vendored
View File

@ -2,3 +2,4 @@
*.mtl filter=lfs diff=lfs merge=lfs -text *.mtl filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text
*.hdr filter=lfs diff=lfs merge=lfs -text

22
Shaders/HDRMap.fs Normal file
View File

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

12
Shaders/HDRMap.vs Normal file
View File

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

16
Shaders/HDRSkybox.fs Normal file
View File

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

16
Shaders/HDRSkybox.vs Normal file
View File

@ -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;
}

BIN
Textures/photostudio.hdr (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -17,6 +17,7 @@ void Skybox::Destroy() {
std::cout << "Skybox destroyed!" << std::endl; std::cout << "Skybox destroyed!" << std::endl;
} }
void Skybox::loadCubeTextures(const std::vector<std::string>& texture_faces) void Skybox::loadCubeTextures(const std::vector<std::string>& texture_faces)
{ {
glGenTextures(1, &id); glGenTextures(1, &id);

View File

@ -9,4 +9,5 @@ struct Texture{
std::string path; std::string path;
}; };
Texture* CreateTexture(unsigned int width, unsigned int height); Texture* CreateTexture(unsigned int width, unsigned int height);

View File

@ -11,9 +11,11 @@
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include "../Primitives/Scene.h" #include "../Primitives/Scene.h"
#include "../model.h" #include "../model.h"
#include "../Utils.h"
static enum class RenderPass { static enum class RenderPass {
NONE = 0, NONE = 0,
SKYBOX, SKYBOX,
HDR_SKYBOX,
DEFAULT, DEFAULT,
PBR PBR
}; };
@ -43,6 +45,84 @@ unsigned int normal;
unsigned int metallic; unsigned int metallic;
unsigned int roughness; unsigned int roughness;
unsigned int ao; 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() void Renderer::Setup()
{ {
// Create ScreenVAO // Create ScreenVAO
@ -70,6 +150,8 @@ void Renderer::Setup()
// Load shaders // Load shaders
shaders[static_cast<int>(RenderPass::SKYBOX)] = Shader(); shaders[static_cast<int>(RenderPass::SKYBOX)] = Shader();
shaders[static_cast<int>(RenderPass::SKYBOX)].Load("../Shaders/skybox.vs", "../Shaders/Cubemap.fs"); shaders[static_cast<int>(RenderPass::SKYBOX)].Load("../Shaders/skybox.vs", "../Shaders/Cubemap.fs");
shaders[static_cast<int>(RenderPass::HDR_SKYBOX)] = Shader();
shaders[static_cast<int>(RenderPass::HDR_SKYBOX)].Load("../Shaders/HDRSkybox.vs", "../Shaders/HDRSkybox.fs");
shaders[static_cast<int>(RenderPass::DEFAULT)] = Shader(); shaders[static_cast<int>(RenderPass::DEFAULT)] = Shader();
shaders[static_cast<int>(RenderPass::DEFAULT)].Load("../Shaders/shader.vs", "../Shaders/shader.fs"); shaders[static_cast<int>(RenderPass::DEFAULT)].Load("../Shaders/shader.vs", "../Shaders/shader.fs");
shaders[static_cast<int>(RenderPass::PBR)] = Shader(); shaders[static_cast<int>(RenderPass::PBR)] = Shader();
@ -83,6 +165,122 @@ void Renderer::Setup()
ao = TextureFromFile("../Textures/rusted_iron/ao.png","."); 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<float> 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 ) { void Renderer::resize(int width, int height ) {
@ -222,8 +420,6 @@ void renderSphere() {
} }
void Renderer::Render(Scene& scene) void Renderer::Render(Scene& scene)
{ {
@ -235,6 +431,7 @@ void Renderer::Render(Scene& scene)
auto model = glm::mat4(1.0f); auto model = glm::mat4(1.0f);
// Skybox // Skybox
#if false
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
Shader shader = shaders.at(static_cast<int>(RenderPass::SKYBOX)); Shader shader = shaders.at(static_cast<int>(RenderPass::SKYBOX));
@ -252,6 +449,28 @@ void Renderer::Render(Scene& scene)
scene.skybox.Unbind(); scene.skybox.Unbind();
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
#endif
//HDR Environment Skybox
Shader shader = shaders.at(static_cast<int>(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 // Phong lighting
@ -306,7 +525,7 @@ void Renderer::Render(Scene& scene)
glActiveTexture(GL_TEXTURE4); glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, ao); glBindTexture(GL_TEXTURE_2D, ao);
// Render Spheres
model = glm::mat4(1.0f); model = glm::mat4(1.0f);
for (int row = 0; row < nrRows; ++row) { for (int row = 0; row < nrRows; ++row) {
for (int col = 0; col < nrColumns; ++col) { for (int col = 0; col < nrColumns; ++col) {

32
src/Utils.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <string>
#include <iostream>
#include <stb_image.h>
#include <glad/glad.h>
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;
}

View File

@ -71,8 +71,6 @@ public:
renderer.Setup(); renderer.Setup();
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
while(!window.shouldClose()) while(!window.shouldClose())
{ {