diff --git a/Project.pro b/Project.pro index f2840de..9f80370 100644 --- a/Project.pro +++ b/Project.pro @@ -41,7 +41,8 @@ HEADERS += \ include/ShaderLib.h \ include/MeshVBO.h \ include/MaterialWireframe.h \ - include/MaterialFractal.h + include/MaterialFractal.h \ + include/MaterialEnvMap.h SOURCES += \ src/main.cpp \ @@ -58,7 +59,8 @@ SOURCES += \ src/ShaderLib.cpp \ src/MeshVBO.cpp \ src/MaterialWireframe.cpp \ - src/MaterialFractal.cpp + src/MaterialFractal.cpp \ + src/MaterialEnvMap.cpp OTHER_FILES += \ $$files(shaders/*, true) \ diff --git a/images/gloss.png b/images/gloss.png new file mode 100644 index 0000000..f41cb04 Binary files /dev/null and b/images/gloss.png differ diff --git a/images/sky_xneg.png b/images/sky_xneg.png new file mode 100644 index 0000000..cb3d048 Binary files /dev/null and b/images/sky_xneg.png differ diff --git a/images/sky_xpos.png b/images/sky_xpos.png new file mode 100644 index 0000000..fdf03f2 Binary files /dev/null and b/images/sky_xpos.png differ diff --git a/images/sky_yneg.png b/images/sky_yneg.png new file mode 100644 index 0000000..a9f355a Binary files /dev/null and b/images/sky_yneg.png differ diff --git a/images/sky_ypos.png b/images/sky_ypos.png new file mode 100644 index 0000000..b360c21 Binary files /dev/null and b/images/sky_ypos.png differ diff --git a/images/sky_zneg.png b/images/sky_zneg.png new file mode 100644 index 0000000..3610621 Binary files /dev/null and b/images/sky_zneg.png differ diff --git a/images/sky_zpos.png b/images/sky_zpos.png new file mode 100644 index 0000000..c7e23b0 Binary files /dev/null and b/images/sky_zpos.png differ diff --git a/include/MaterialEnvMap.h b/include/MaterialEnvMap.h new file mode 100644 index 0000000..2d8a9de --- /dev/null +++ b/include/MaterialEnvMap.h @@ -0,0 +1,36 @@ +#ifndef MATERIALENVMAP_H +#define MATERIALENVMAP_H + +#include "Material.h" +#include +#include + +class Camera; + +class MaterialEnvMap : public Material +{ +public: + MaterialEnvMap(const std::shared_ptr &io_camera, const std::shared_ptr &io_shaderLib, std::array* io_matrices) : + Material(io_camera, io_shaderLib, io_matrices) + {} + MaterialEnvMap(const MaterialEnvMap&) = default; + MaterialEnvMap& operator=(const MaterialEnvMap&) = default; + MaterialEnvMap(MaterialEnvMap&&) = default; + MaterialEnvMap& operator=(MaterialEnvMap&&) = default; + ~MaterialEnvMap() override = default; + + virtual void init() override; + + virtual void update() override; + + virtual const char* shaderFileName() const override; + +private: + void initEnvMap(); + void initGlossMap(); + std::unique_ptr m_envMap; + std::unique_ptr m_glossMap; + +}; + +#endif // MATERIALENVMAP_H diff --git a/shaderPrograms/envMap.json b/shaderPrograms/envMap.json new file mode 100644 index 0000000..a6a2c2a --- /dev/null +++ b/shaderPrograms/envMap.json @@ -0,0 +1,5 @@ +{ + "Name" : "EnvMap", + "Vertex" : "shaders/env_vert.glsl", + "Fragment" : "shaders/env_frag.glsl" +} \ No newline at end of file diff --git a/shaders/env_frag.glsl b/shaders/env_frag.glsl new file mode 100644 index 0000000..602d485 --- /dev/null +++ b/shaders/env_frag.glsl @@ -0,0 +1,45 @@ +#version 420 core + +// Attributes passed on from the vertex shader +smooth in vec3 FragmentPosition; +smooth in vec3 FragmentNormal; +smooth in vec2 FragmentTexCoord; + +uniform mat4 N; + +/// @brief our output fragment colour +layout (location=0) out vec4 FragColour; + +// A texture unit for storing the 3D texture +uniform samplerCube envMap; + +uniform sampler2D glossMap; + +// Set the maximum environment level of detail (cannot be queried from GLSL apparently) +// The mipmap level is determined by log_2(resolution), so if the texture was 4x4, +// there would be 8 mipmap levels (128x128,64x64,32x32,16x16,8x8,4x4,2x2,1x1). +// The LOD parameter can be anything inbetween 0.0 and 8.0 for the purposes of +// trilinear interpolation. +uniform float envMaxLOD = 2.5; + +uniform vec3 camPos; + +// The inverse View matrix +uniform mat4 invV; + +void main () { + // Calculate the normal (this is the expensive bit in Phong) + vec3 n = normalize( FragmentNormal ); + + // Calculate the eye vector + vec3 v = normalize(camPos-FragmentPosition); + + vec3 r = reflect(v, n); + + vec3 lookup = vec3(N * vec4(r, 0.0)); + + float gloss = (1.0 - texture(glossMap, FragmentTexCoord*2).r) * envMaxLOD; + + FragColour = texture(envMap, r, gloss); +} + diff --git a/shaders/env_vert.glsl b/shaders/env_vert.glsl new file mode 100644 index 0000000..b8530a5 --- /dev/null +++ b/shaders/env_vert.glsl @@ -0,0 +1,39 @@ +#version 420 + +// The vertex position attribute +layout (location=0) in vec3 inVert; + +// The texture coordinate attribute +layout (location=1) in vec2 inUV; + +// The vertex normal attribute +layout (location=2) in vec3 inNormal; + +// These attributes are passed onto the shader (should they all be smoothed?) +smooth out vec3 FragmentPosition; +smooth out vec3 FragmentNormal; +smooth out vec2 FragmentTexCoord; + +uniform mat4 M; // model view matrix calculated in the App +uniform mat4 MVP; // model view projection calculated in the app +uniform mat4 N; // normal matrix calculated in the app + +void main() +{ + // Transform the vertex normal by the inverse transpose modelview matrix + FragmentNormal = normalize(vec3(N * vec4(inNormal, 1.0))); + + // Compute the unprojected vertex position + FragmentPosition = vec3(M * vec4(inVert, 1.0) ); + + // Copy across the texture coordinates + FragmentTexCoord = inUV; + + // Compute the position of the vertex + gl_Position = MVP * vec4(inVert,1.0); +} + + + + + diff --git a/src/DemoScene.cpp b/src/DemoScene.cpp index 34d75b8..efdaf09 100644 --- a/src/DemoScene.cpp +++ b/src/DemoScene.cpp @@ -3,6 +3,7 @@ #include "MaterialPBR.h" #include "MaterialPhong.h" #include "MaterialFractal.h" +#include "MaterialEnvMap.h" #include //----------------------------------------------------------------------------------------------------- @@ -70,8 +71,8 @@ void DemoScene::keyPress(QKeyEvent* io_event) //----------------------------------------------------------------------------------------------------- void DemoScene::initMaterials() { - m_materials.reserve(5); - + m_materials.reserve(6); + m_materials.emplace_back(new MaterialEnvMap(m_camera, m_shaderLib, &m_matrices)); m_materials.emplace_back(new MaterialPhong(m_camera, m_shaderLib, &m_matrices)); 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)); diff --git a/src/MaterialEnvMap.cpp b/src/MaterialEnvMap.cpp new file mode 100644 index 0000000..f8e60c9 --- /dev/null +++ b/src/MaterialEnvMap.cpp @@ -0,0 +1,95 @@ +#include "MaterialEnvMap.h" +#include "Scene.h" +#include "ShaderLib.h" + +void MaterialEnvMap::init() +{ + initEnvMap(); + m_envMap->bind(0); + + auto shaderPtr = m_shaderLib->getShader(m_shaderName); + shaderPtr->setUniformValue("envMap", 0); + + initGlossMap(); + m_glossMap->bind(1); + shaderPtr->setUniformValue("glossMap", 1); + + update(); +} + +void MaterialEnvMap::initGlossMap() +{ + m_glossMap.reset(new QOpenGLTexture(QImage("images/gloss.png"))); + m_glossMap->create(); + m_glossMap->bind(1); + using tex = QOpenGLTexture; + m_glossMap->setWrapMode(tex::Repeat); + m_glossMap->setMinMagFilters(tex::Linear, tex::Linear); +} + +void MaterialEnvMap::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 MaterialEnvMap::update() +{ + auto shaderPtr = m_shaderLib->getShader(m_shaderName); + auto eye = m_cam->getCameraEye(); + shaderPtr->setUniformValue("camPos", QVector3D{eye.x, eye.y, eye.z}); + + // Scope the using declaration + { + using namespace SceneMatrices; + static constexpr std::array shaderUniforms = {{"M", "MVP", "N"}}; + // Send all our matrices to the GPU + for (const auto matrixId : {MODEL_VIEW, PROJECTION, NORMAL}) + { + // Convert from glm to Qt + QMatrix4x4 qmat(glm::value_ptr((*m_matrices)[matrixId])); + // Need to transpose the matrix as they both use different majors + shaderPtr->setUniformValue(shaderUniforms[matrixId], qmat.transposed()); + } + } +} + +const char* MaterialEnvMap::shaderFileName() const +{ + return "shaderPrograms/envMap.json"; +}