diff --git a/Criminowl.pro b/Criminowl.pro index 53f8d8d..d4625f8 100644 --- a/Criminowl.pro +++ b/Criminowl.pro @@ -23,6 +23,7 @@ DEFINES += QT_DEPRECATED_WARNINGS INCLUDEPATH += \ /usr/local/include/glm/glm \ /usr/local/include/glm \ + /usr/local/include/stb \ $$PWD/include \ $$PWD/ui \ $$PWD/shaders @@ -43,7 +44,8 @@ HEADERS += \ include/MaterialWireframe.h \ include/MaterialFractal.h \ include/MaterialEnvMap.h \ - include/MaterialBump.h + include/MaterialBump.h \ + include/HDR_cube.h SOURCES += \ @@ -63,7 +65,8 @@ SOURCES += \ src/MaterialWireframe.cpp \ src/MaterialFractal.cpp \ src/MaterialEnvMap.cpp \ - src/MaterialBump.cpp + src/MaterialBump.cpp \ + src/HDR_cube.cpp OTHER_FILES += \ $$files(shaders/*, true) \ diff --git a/include/DemoScene.h b/include/DemoScene.h index bc90531..9b77621 100644 --- a/include/DemoScene.h +++ b/include/DemoScene.h @@ -74,11 +74,6 @@ public slots: /// calls loadMesh. //----------------------------------------------------------------------------------------------------- void generateNewGeometry(); - //----------------------------------------------------------------------------------------------------- - /// @brief Used to link a Qt button to the scene, to cycle through the materials and apply them to - /// the current Mesh. - //----------------------------------------------------------------------------------------------------- - void nextMaterial(); private: //----------------------------------------------------------------------------------------------------- @@ -94,6 +89,9 @@ public slots: //----------------------------------------------------------------------------------------------------- virtual void renderScene() override; + void initSphereMap(); + void initCubeMap(); + private: //----------------------------------------------------------------------------------------------------- /// @brief Holds our test meshes. @@ -117,21 +115,18 @@ public slots: //----------------------------------------------------------------------------------------------------- /// @brief The materials used in this scene. //----------------------------------------------------------------------------------------------------- - std::vector> m_materials; - //----------------------------------------------------------------------------------------------------- - /// @brief Holds the index of the currently drawn mesh in our array of meshes. - //----------------------------------------------------------------------------------------------------- - size_t m_meshIndex = 0; - //----------------------------------------------------------------------------------------------------- - /// @brief The current material. - //----------------------------------------------------------------------------------------------------- - size_t m_currentMaterial = 0; + std::unique_ptr m_material; //----------------------------------------------------------------------------------------------------- /// @brief Is the mesh rotating. //----------------------------------------------------------------------------------------------------- bool m_rotating = false; + + unsigned int m_hdrTextureID; + unsigned int m_envTextureID; + unsigned int m_irradianceTextureID; + }; #endif // DEMOSCENE_H diff --git a/include/HDR_cube.h b/include/HDR_cube.h new file mode 100644 index 0000000..2ba17f5 --- /dev/null +++ b/include/HDR_cube.h @@ -0,0 +1,28 @@ +#ifndef MATERIALHDRCUBE_H +#define MATERIALHDRCUBE_H + +#include "Material.h" +#include +#include + +class HDR_cube +{ +public: + HDR_cube() = default; + HDR_cube(const HDR_cube&) = default; + HDR_cube& operator=(const HDR_cube&) = default; + HDR_cube(HDR_cube&&) = default; + HDR_cube& operator=(HDR_cube&&) = default; + ~HDR_cube() = default; + + void init(const std::shared_ptr &io_shaderLib, QOpenGLContext* io_context); + + unsigned int irradianceMapID() const noexcept { return m_irradianceMap; } + +private: + unsigned int m_irradianceMap; + unsigned int initSphereMap(QOpenGLContext *io_context); + +}; + +#endif // MATERIALHDRCUBE_H diff --git a/include/Material.h b/include/Material.h index 74ed971..de7f3b4 100644 --- a/include/Material.h +++ b/include/Material.h @@ -55,6 +55,10 @@ class Material //----------------------------------------------------------------------------------------------------- void setShaderName(const std::string &_name); //----------------------------------------------------------------------------------------------------- + /// @brief Used to get the name of the shader that this material should be applied to. + //----------------------------------------------------------------------------------------------------- + std::string getShaderName() const noexcept; + //----------------------------------------------------------------------------------------------------- /// @brief Used to update shader values. //----------------------------------------------------------------------------------------------------- virtual void update() = 0; diff --git a/include/MaterialEnvMap.h b/include/MaterialEnvMap.h index 2d8a9de..20eb0dd 100644 --- a/include/MaterialEnvMap.h +++ b/include/MaterialEnvMap.h @@ -5,8 +5,6 @@ #include #include -class Camera; - class MaterialEnvMap : public Material { public: diff --git a/include/MaterialFractal.h b/include/MaterialFractal.h index d0e0346..ca5eb4a 100644 --- a/include/MaterialFractal.h +++ b/include/MaterialFractal.h @@ -4,8 +4,6 @@ #include "Material.h" #include -class Camera; - class MaterialFractal : public Material { public: diff --git a/include/MaterialPBR.h b/include/MaterialPBR.h index 75ea092..706c5ec 100644 --- a/include/MaterialPBR.h +++ b/include/MaterialPBR.h @@ -3,6 +3,7 @@ #include "Material.h" #include "vec3.hpp" +#include "HDR_cube.h" class MaterialPBR : public Material { @@ -11,6 +12,7 @@ class MaterialPBR : public Material const std::shared_ptr &io_camera, const std::shared_ptr &io_shaderLib, std::array* io_matrices, + QOpenGLContext* io_context, const glm::vec3 &_albedo, const float _ao, const float _exposure, @@ -19,6 +21,7 @@ class MaterialPBR : public Material ) : Material(io_camera, io_shaderLib, io_matrices), m_albedo(_albedo), + m_context(io_context), m_ao(_ao), m_exposure(_exposure), m_roughness(_roughness), @@ -38,7 +41,13 @@ class MaterialPBR : public Material private: + void initEnvMap(); + std::unique_ptr m_envMap; + + glm::vec3 m_albedo; +// HDR_cube m_envMap; + QOpenGLContext* m_context; float m_ao; float m_exposure; float m_roughness; diff --git a/include/MaterialPhong.h b/include/MaterialPhong.h index 89206d1..785cc70 100644 --- a/include/MaterialPhong.h +++ b/include/MaterialPhong.h @@ -3,8 +3,6 @@ #include "Material.h" -class Camera; - class MaterialPhong : public Material { public: diff --git a/include/MaterialWireframe.h b/include/MaterialWireframe.h index be2e3dd..b9b4e28 100644 --- a/include/MaterialWireframe.h +++ b/include/MaterialWireframe.h @@ -3,8 +3,6 @@ #include "Material.h" -class Camera; - class MaterialWireframe : public Material { public: diff --git a/shaders/owl_pbr_frag.glsl b/shaders/owl_pbr_frag.glsl index b4225e9..945c8db 100644 --- a/shaders/owl_pbr_frag.glsl +++ b/shaders/owl_pbr_frag.glsl @@ -16,6 +16,8 @@ uniform float ao; // camera parameters uniform vec3 camPos; uniform float exposure; +uniform samplerCube irradianceMap; +uniform sampler2D sphereMap; // lights @@ -80,7 +82,10 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0) return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } // ---------------------------------------------------------------------------- - +vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) +{ + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} void main() @@ -88,7 +93,7 @@ void main() vec3 N = normalize(Normal); - vec3 eyeAlbedo = vec3(EyeVal); + vec3 eyeAlbedo = albedo;//vec3(texture(sphereMap, TexCoords));//albedo;//vec3(EyeVal); vec3 V = normalize(camPos - WorldPos); vec3 R = reflect(-V, N); @@ -140,7 +145,11 @@ void main() // ambient lighting (note that the next IBL tutorial will replace // this ambient lighting with environment lighting). - vec3 ambient = vec3(0.03) * eyeAlbedo * ao; + vec3 kS = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); + vec3 kD = 1.0 - kS; + vec3 irradiance = texture(irradianceMap, R).rgb; + vec3 diffuse = irradiance * albedo; + vec3 ambient = (kD * diffuse) * ao; vec3 color = ambient + Lo; diff --git a/shaders/owl_pbr_tess_control.glsl b/shaders/owl_pbr_tess_control.glsl index 6687fff..46f3ecf 100644 --- a/shaders/owl_pbr_tess_control.glsl +++ b/shaders/owl_pbr_tess_control.glsl @@ -37,7 +37,7 @@ void main(void) tc_uv[ID] = v_uv[ID]; float tessMask = mask(eyeMask(posA, eyeFuzz, 1.0), eyeMask(posB, eyeFuzz, 1.0), eyeFuzz, v_normal[ID].z); - int tessLevel = 1 + int(ceil(64 * smoothstep(0.0, 1.0, tessMask))); + int tessLevel = 1 + int(ceil(31 * smoothstep(0.0, 1.0, tessMask))); gl_TessLevelInner[0] = tessLevel; gl_TessLevelOuter[0] = tessLevel; diff --git a/src/DemoScene.cpp b/src/DemoScene.cpp index 057b1d8..1621e8b 100644 --- a/src/DemoScene.cpp +++ b/src/DemoScene.cpp @@ -7,6 +7,10 @@ #include "MaterialBump.h" #include #include +#include +#include "HDR_cube.h" +//#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" //----------------------------------------------------------------------------------------------------- void DemoScene::writeMeshAttributes() @@ -37,6 +41,13 @@ void DemoScene::init() { Scene::init(); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + +// HDR_cube map; +// map.init(m_shaderLib, context()); + initSphereMap(); + initCubeMap(); initMaterials(); initGeo(); @@ -63,25 +74,17 @@ void DemoScene::keyPress(QKeyEvent* io_event) { makeCurrent(); Scene::keyPress(io_event); - m_materials[m_currentMaterial]->handleKey(io_event, context()); + m_material->handleKey(io_event, context()); } //----------------------------------------------------------------------------------------------------- void DemoScene::initMaterials() { - m_materials.reserve(7); - - m_materials.emplace_back(new MaterialPBR(m_camera, m_shaderLib, &m_matrices, {0.5f, 0.0f, 0.0f}, 1.0f, 1.0f, 0.5f, 1.0f)); - m_materials.emplace_back(new MaterialPBR(m_camera, m_shaderLib, &m_matrices, {0.1f, 0.2f, 0.5f}, 0.5f, 1.0f, 0.4f, 0.2f)); - m_materials.emplace_back(new MaterialWireframe(m_camera, m_shaderLib, &m_matrices)); + m_material.reset(new MaterialPBR(m_camera, m_shaderLib, &m_matrices, context(), {0.5f, 0.5f, 0.5f}, 1.0f, 1.0f, 0.5f, 1.0f)); - for (auto& mat : m_materials) - { - auto name = m_shaderLib->loadShaderProg(mat->shaderFileName()); - mat->setShaderName(name); - mat->apply(); - } + auto name = m_shaderLib->loadShaderProg(m_material->shaderFileName()); + m_material->setShaderName(name); + m_material->apply(); - m_materials[m_currentMaterial]->apply(); } //----------------------------------------------------------------------------------------------------- void DemoScene::rotating( const bool _rotating ) @@ -104,15 +107,6 @@ void DemoScene::generateNewGeometry() setAttributeBuffers(); } //----------------------------------------------------------------------------------------------------- -void DemoScene::nextMaterial() -{ - makeCurrent(); - m_currentMaterial = (m_currentMaterial + 1) % m_materials.size(); - - m_materials[m_currentMaterial]->apply(); - setAttributeBuffers(); -} -//----------------------------------------------------------------------------------------------------- void DemoScene::renderScene() { Scene::renderScene(); @@ -123,9 +117,176 @@ void DemoScene::renderScene() m_matrices[MODEL_VIEW] = glm::rotate(m_matrices[MODEL_VIEW], glm::radians(-1.0f * m_rotating), glm::vec3(0.0f, 1.0f, 0.0f)); } - m_materials[m_currentMaterial]->update(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_irradianceTextureID); + + m_material->update(); + m_meshVBO.use(); glDrawElements(GL_PATCHES, m_owlMesh.getNIndicesData(), GL_UNSIGNED_SHORT, nullptr); } //----------------------------------------------------------------------------------------------------- + + + +void DemoScene::initSphereMap() +{ + stbi_set_flip_vertically_on_load(true); + int width, height, nrComponents; + float *data = stbi_loadf("images/Alexs_Apt_2k.hdr", &width, &height, &nrComponents, 0); + + auto funcs = context()->versionFunctions(); + funcs->glGenTextures(1, &m_hdrTextureID); + funcs->glBindTexture(GL_TEXTURE_2D, m_hdrTextureID); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + stbi_image_free(data); +} + +void DemoScene::initCubeMap() +{ + auto defaultFBO = defaultFramebufferObject(); + auto cubeMapShaderName = m_shaderLib->loadShaderProg("shaderPrograms/hdr_cubemap.json"); + auto cubeMapShader = m_shaderLib->getShader(cubeMapShaderName); + auto funcs = context()->versionFunctions(); + + QOpenGLVertexArrayObject vao; + // Create and bind our Vertex Array Object + vao.create(); + vao.bind(); + + Mesh cube; + cube.load("models/unitCube.obj"); + + MeshVBO vbo; + // Create and bind our Vertex Buffer Object + vbo.init(); + vbo.reset( + sizeof(GLushort), + cube.getNIndicesData(), + sizeof(GLfloat), + cube.getNVertData(), + cube.getNUVData(), + cube.getNNormData() + ); + + + unsigned int captureFBO, captureRBO; + + funcs->glGenFramebuffers(1, &captureFBO); + funcs->glGenRenderbuffers(1, &captureRBO); + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + funcs->glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + funcs->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); + funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO); + + funcs->glGenTextures(1, &m_envTextureID); + funcs->glBindTexture(GL_TEXTURE_CUBE_MAP, m_envTextureID); + for (unsigned int i = 0; i < 6; ++i) + { + // note that we store each face with 16 bit floating point values + funcs->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, nullptr); + } + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glm::mat4 captureProjectionGLM = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f); + QMatrix4x4 captureProjection(glm::value_ptr(captureProjectionGLM)); + captureProjection = captureProjection.transposed(); + 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)) + }; + + // convert HDR equirectangular environment map to cubemap equivalent + cubeMapShader->bind(); + cubeMapShader->setUniformValue("sphereMap", 0); + // Need to transpose the matrix as they both use different majors + cubeMapShader->setUniformValue("projection", captureProjection); + funcs->glActiveTexture(GL_TEXTURE0); + funcs->glBindTexture(GL_TEXTURE_2D, m_hdrTextureID); + + funcs->glViewport(0, 0, 512, 512); // don't forget to configure the viewport to the capture dimensions. + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + + { + using namespace MeshAttributes; + vbo.write(cube.getVertexData(), VERTEX); + vbo.setIndices(cube.getIndicesData()); + cubeMapShader->enableAttributeArray(VERTEX); + cubeMapShader->setAttributeBuffer(VERTEX, GL_FLOAT, vbo.offset(VERTEX), 3); + } + + for (unsigned int i = 0; i < 6; ++i) + { + // Convert from glm to Qt + QMatrix4x4 view(glm::value_ptr(captureViews[i])); + // Need to transpose the matrix as they both use different majors + cubeMapShader->setUniformValue("view", view.transposed()); + funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_envTextureID, 0); + funcs->glClearColor(0.f, 0.f, 0.f, 1.f); + funcs->glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT); + + funcs->glDrawElements(GL_TRIANGLES, cube.getNIndicesData(), GL_UNSIGNED_SHORT, nullptr); + } + funcs->glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + funcs->glGenTextures(1, &m_irradianceTextureID); + funcs->glBindTexture(GL_TEXTURE_CUBE_MAP, m_irradianceTextureID); + for (unsigned int i = 0; i < 6; ++i) + { + funcs->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 32, 32, 0, GL_RGB, GL_FLOAT, nullptr); + } + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + funcs->glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + funcs->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32); + + // pbr: solve diffuse integral by convolution to create an irradiance (cube)map. + // ----------------------------------------------------------------------------- + auto irradianceShaderName = m_shaderLib->loadShaderProg("shaderPrograms/hdr_cubemap_irradiance.json"); + auto irradianceShader = m_shaderLib->getShader(irradianceShaderName); + irradianceShader->bind(); + irradianceShader->setUniformValue("envMap", 0); + irradianceShader->setUniformValue("projection", captureProjection); + funcs->glActiveTexture(GL_TEXTURE0); + funcs->glBindTexture(GL_TEXTURE_CUBE_MAP, m_envTextureID); + + funcs->glViewport(0, 0, 32, 32); // don't forget to configure the viewport to the capture dimensions. + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + for (unsigned int i = 0; i < 6; ++i) + { + // Convert from glm to Qt + QMatrix4x4 view(glm::value_ptr(captureViews[i])); + // Need to transpose the matrix as they both use different majors + irradianceShader->setUniformValue("view", view.transposed()); + funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_irradianceTextureID, 0); + funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + funcs->glDrawElements(GL_TRIANGLES, cube.getNIndicesData(), GL_UNSIGNED_SHORT, nullptr); + } + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); +} diff --git a/src/HDR_cube.cpp b/src/HDR_cube.cpp new file mode 100644 index 0000000..d7da6ee --- /dev/null +++ b/src/HDR_cube.cpp @@ -0,0 +1,175 @@ +#include "HDR_cube.h" +#include "Scene.h" +#include "ShaderLib.h" +#include "Mesh.h" +#include "MeshVBO.h" +#include +#include +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" + + +void HDR_cube::init(const std::shared_ptr &io_shaderLib, QOpenGLContext *io_context) +{ + auto defaultFBO = io_context->defaultFramebufferObject(); + auto hdrTex = initSphereMap(io_context); + auto cubeMapShaderName = io_shaderLib->loadShaderProg("shaderPrograms/hdr_cubemap.json"); + auto cubeMapShader = io_shaderLib->getShader(cubeMapShaderName); + auto funcs = io_context->versionFunctions(); + + QOpenGLVertexArrayObject vao; + // Create and bind our Vertex Array Object + vao.create(); + vao.bind(); + + Mesh cube; + cube.load("models/unitCube.obj"); + + MeshVBO vbo; + // Create and bind our Vertex Buffer Object + vbo.init(); + vbo.reset( + sizeof(GLushort), + cube.getNIndicesData(), + sizeof(GLfloat), + cube.getNVertData(), + cube.getNUVData(), + cube.getNNormData() + ); + + + unsigned int captureFBO, captureRBO; + + funcs->glGenFramebuffers(1, &captureFBO); + funcs->glGenRenderbuffers(1, &captureRBO); + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + funcs->glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + funcs->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); + funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO); + + unsigned int envCubeMap; + funcs->glGenTextures(1, &envCubeMap); + funcs->glBindTexture(GL_TEXTURE_CUBE_MAP, envCubeMap); + for (unsigned int i = 0; i < 6; ++i) + { + // note that we store each face with 16 bit floating point values + funcs->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, nullptr); + } + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glm::mat4 captureProjectionGLM = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f); + QMatrix4x4 captureProjection(glm::value_ptr(captureProjectionGLM)); + captureProjection = captureProjection.transposed(); + 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)) + }; + + // convert HDR equirectangular environment map to cubemap equivalent + cubeMapShader->bind(); + cubeMapShader->setUniformValue("sphereMap", 0); + // Need to transpose the matrix as they both use different majors + cubeMapShader->setUniformValue("P", captureProjection); + funcs->glActiveTexture(GL_TEXTURE0); + funcs->glBindTexture(GL_TEXTURE_2D, hdrTex); + + funcs->glViewport(0, 0, 512, 512); // don't forget to configure the viewport to the capture dimensions. + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + + { + using namespace MeshAttributes; + vbo.write(cube.getVertexData(), VERTEX); + vbo.setIndices(cube.getIndicesData()); + cubeMapShader->enableAttributeArray(VERTEX); + cubeMapShader->setAttributeBuffer(VERTEX, GL_FLOAT, vbo.offset(VERTEX), 3); + } + + for (unsigned int i = 0; i < 6; ++i) + { + // Convert from glm to Qt + QMatrix4x4 view(glm::value_ptr(captureViews[i])); + // Need to transpose the matrix as they both use different majors + cubeMapShader->setUniformValue("M", view.transposed()); + funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envCubeMap, 0); + funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + funcs->glDrawElements(GL_TRIANGLES, cube.getNIndicesData(), GL_UNSIGNED_SHORT, nullptr); + } + funcs->glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + funcs->glGenTextures(1, &m_irradianceMap); + funcs->glBindTexture(GL_TEXTURE_CUBE_MAP, m_irradianceMap); + for (unsigned int i = 0; i < 6; ++i) + { + funcs->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 32, 32, 0, GL_RGB, GL_FLOAT, nullptr); + } + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + funcs->glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + funcs->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32); + + // pbr: solve diffuse integral by convolution to create an irradiance (cube)map. + // ----------------------------------------------------------------------------- + auto irradianceShaderName = io_shaderLib->loadShaderProg("shaderPrograms/hdr_cubemap_irradiance.json"); + auto irradianceShader = io_shaderLib->getShader(irradianceShaderName); + irradianceShader->bind(); + irradianceShader->setUniformValue("envMap", 0); + irradianceShader->setUniformValue("P", captureProjection); + funcs->glActiveTexture(GL_TEXTURE0); + funcs->glBindTexture(GL_TEXTURE_CUBE_MAP, envCubeMap); + + funcs->glViewport(0, 0, 32, 32); // don't forget to configure the viewport to the capture dimensions. + funcs->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + for (unsigned int i = 0; i < 6; ++i) + { + // Convert from glm to Qt + QMatrix4x4 view(glm::value_ptr(captureViews[i])); + // Need to transpose the matrix as they both use different majors + irradianceShader->setUniformValue("M", view.transposed()); + funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_irradianceMap, 0); + funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + funcs->glDrawElements(GL_TRIANGLES, cube.getNIndicesData(), GL_UNSIGNED_SHORT, nullptr); + } + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); +} + +unsigned int HDR_cube::initSphereMap(QOpenGLContext *io_context) +{ + stbi_set_flip_vertically_on_load(true); + int width, height, nrComponents; + float *data = stbi_loadf("images/LA_Downtown_Afternoon_Fishing_3k.hdr", &width, &height, &nrComponents, 0); + unsigned int hdrTexture; + + auto funcs = io_context->versionFunctions(); + funcs->glGenTextures(1, &hdrTexture); + funcs->glBindTexture(GL_TEXTURE_2D, hdrTexture); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + stbi_image_free(data); + return hdrTexture; +} + + diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 81dd721..b928057 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -8,7 +8,6 @@ void MainWindow::init(const std::shared_ptr &io_scene) m_ui.s_mainWindowGridLayout->addWidget(m_scene.get(),0,0,3,5); connect(m_ui.m_rotating, SIGNAL(clicked(bool)),m_scene.get(), SLOT(rotating(bool))); connect(m_ui.generate, SIGNAL( clicked(bool)), m_scene.get(), SLOT(generateNewGeometry())); - connect(m_ui.material, SIGNAL( clicked(bool)), m_scene.get(), SLOT(nextMaterial())); } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/Material.cpp b/src/Material.cpp index 066b163..9b4699c 100644 --- a/src/Material.cpp +++ b/src/Material.cpp @@ -14,5 +14,10 @@ void Material::setShaderName(const std::string &_name) m_shaderName = _name; } //----------------------------------------------------------------------------------------------------- +std::string Material::getShaderName() const noexcept +{ + return m_shaderName; +} +//----------------------------------------------------------------------------------------------------- void Material::handleKey(QKeyEvent*, QOpenGLContext*) {} diff --git a/src/MaterialEnvMap.cpp b/src/MaterialEnvMap.cpp index 65a90fc..f2a97ad 100644 --- a/src/MaterialEnvMap.cpp +++ b/src/MaterialEnvMap.cpp @@ -6,11 +6,11 @@ void MaterialEnvMap::init() { auto shaderPtr = m_shaderLib->getShader(m_shaderName); - initEnvMap(); - shaderPtr->setUniformValue("envMap", 0); +// initEnvMap(); +// shaderPtr->setUniformValue("envMap", 0); - initGlossMap(); - shaderPtr->setUniformValue("glossMap", 1); +// initGlossMap(); +// shaderPtr->setUniformValue("glossMap", 1); update(); } diff --git a/src/MaterialPBR.cpp b/src/MaterialPBR.cpp index d7d4df8..488eb4f 100644 --- a/src/MaterialPBR.cpp +++ b/src/MaterialPBR.cpp @@ -1,12 +1,18 @@ #include "MaterialPBR.h" #include "Scene.h" #include "ShaderLib.h" -#include +#include void MaterialPBR::init() { - auto shaderPtr = m_shaderLib->getCurrentShader(); + auto shaderPtr = m_shaderLib->getShader(m_shaderName); + // m_envMap.init(m_shaderLib, m_context); +// initEnvMap(); +// initSphereMap(); +// initCubeMap(); + shaderPtr->setUniformValue("irradianceMap", 0); +// shaderPtr->setUniformValue("sphereMap", 1); shaderPtr->setPatchVertexCount(3); shaderPtr->setUniformValue("albedo", QVector3D{m_albedo.x, m_albedo.y, m_albedo.z}); shaderPtr->setUniformValue("ao", m_ao); @@ -18,8 +24,53 @@ void MaterialPBR::init() update(); } + + +void MaterialPBR::initEnvMap() +{ + m_envMap.reset(new QOpenGLTexture(QOpenGLTexture::TargetCubeMap)); + static constexpr std::array paths = {{ + "images/sky_xpos.png", + "images/sky_ypos.png", + "images/sky_zpos.png", + "images/sky_xneg.png", + "images/sky_yneg.png", + "images/sky_zneg.png" + }}; + + using tex = QOpenGLTexture; + static constexpr std::array dataTypes = {{ + tex::CubeMapPositiveX, + tex::CubeMapPositiveY, + tex::CubeMapPositiveZ, + tex::CubeMapNegativeX, + tex::CubeMapNegativeY, + tex::CubeMapNegativeZ + }}; + std::array maps; + for (size_t i = 0; i < maps.size(); ++i) + maps[i] = QImage(paths[i]).mirrored().convertToFormat(QImage::Format_RGBA8888); + + m_envMap->create(); + m_envMap->bind(0); + m_envMap->setSize(maps[0].width(), maps[0].height(), maps[0].depth()); + m_envMap->setFormat(tex::RGBAFormat); + m_envMap->allocateStorage(); + + for (size_t i = 0; i < maps.size(); ++i) + m_envMap->setData(0, 0, dataTypes[i], tex::RGBA, tex::UInt8, maps[i].constBits()); + + m_envMap->setMinMagFilters(tex::LinearMipMapLinear, tex::Linear); + m_envMap->setWrapMode(tex::ClampToEdge); + m_envMap->generateMipMaps(); + m_envMap->setAutoMipMapGenerationEnabled(true); +} + + void MaterialPBR::update() { +// m_sphereMap->bind(1); +// m_envMap->bind(0); auto shaderPtr = m_shaderLib->getShader(m_shaderName); auto eye = m_cam->getCameraEye(); shaderPtr->setUniformValue("camPos", QVector3D{eye.x, eye.y, eye.z});