diff --git a/Project.pro b/Project.pro index 9f80370..4b1202f 100644 --- a/Project.pro +++ b/Project.pro @@ -42,7 +42,9 @@ HEADERS += \ include/MeshVBO.h \ include/MaterialWireframe.h \ include/MaterialFractal.h \ - include/MaterialEnvMap.h + include/MaterialEnvMap.h \ + include/MaterialBump.h + SOURCES += \ src/main.cpp \ @@ -60,7 +62,8 @@ SOURCES += \ src/MeshVBO.cpp \ src/MaterialWireframe.cpp \ src/MaterialFractal.cpp \ - src/MaterialEnvMap.cpp + src/MaterialEnvMap.cpp \ + src/MaterialBump.cpp OTHER_FILES += \ $$files(shaders/*, true) \ diff --git a/images/bricknormals.jpg b/images/bricknormals.jpg new file mode 100644 index 0000000..0cf616f Binary files /dev/null and b/images/bricknormals.jpg differ diff --git a/images/bricktexture.jpg b/images/bricktexture.jpg new file mode 100644 index 0000000..0fd5277 Binary files /dev/null and b/images/bricktexture.jpg differ diff --git a/images/facenormals.jpg b/images/facenormals.jpg new file mode 100644 index 0000000..be3dc55 Binary files /dev/null and b/images/facenormals.jpg differ diff --git a/images/facetexture.jpg b/images/facetexture.jpg new file mode 100644 index 0000000..bc2d17e Binary files /dev/null and b/images/facetexture.jpg differ diff --git a/include/MaterialBump.h b/include/MaterialBump.h new file mode 100644 index 0000000..a1294e8 --- /dev/null +++ b/include/MaterialBump.h @@ -0,0 +1,33 @@ +#ifndef MATERIALBUMP_H +#define MATERIALBUMP_H + +#include "Material.h" +#include +#include + +class MaterialBump : public Material +{ +public: + MaterialBump(const std::shared_ptr &io_camera, const std::shared_ptr &io_shaderLib, std::array* io_matrices) : + Material(io_camera, io_shaderLib, io_matrices) + {} + MaterialBump(const MaterialBump&) = default; + MaterialBump& operator=(const MaterialBump&) = default; + MaterialBump(MaterialBump&&) = default; + MaterialBump& operator=(MaterialBump&&) = default; + ~MaterialBump() override = default; + + virtual void init() override; + + virtual void update() override; + + virtual const char* shaderFileName() const override; + +private: + void initColor(); + void initNormal(); + std::unique_ptr m_colorMap; + std::unique_ptr m_normalMap; +}; + +#endif // MATERIALBUMP_H diff --git a/shaderPrograms/bump.json b/shaderPrograms/bump.json new file mode 100644 index 0000000..a692ef6 --- /dev/null +++ b/shaderPrograms/bump.json @@ -0,0 +1,5 @@ +{ + "Name" : "Bump", + "Vertex" : "shaders/bump_vert.glsl", + "Fragment" : "shaders/bump_frag.glsl" +} \ No newline at end of file diff --git a/shaders/bump_frag.glsl b/shaders/bump_frag.glsl new file mode 100644 index 0000000..f5a67c2 --- /dev/null +++ b/shaders/bump_frag.glsl @@ -0,0 +1,122 @@ +#version 430 +uniform sampler2D NormalTexture; +uniform sampler2D ColourTexture; + +// Set this property to change the scaling of the texture over the surface (higher means that the texture is repeated more frequently) +uniform float texCoordScale = 1.0; + +// The output colour which will be output to the framebuffer +layout (location=0) out vec4 fragColour; + +// Structure for holding light parameters +struct LightInfo +{ + vec4 Position; // Light position in eye coords. + vec3 La; // Ambient light intensity + vec3 Ld; // Diffuse light intensity + vec3 Ls; // Specular light intensity +}; + +// We'll have a single light in the scene with some default values +uniform LightInfo Light = LightInfo( + vec4(2.0, 2.0, 10.0, 1.0), // position + vec3(0.2, 0.2, 0.2), // La + vec3(1.0, 1.0, 1.0), // Ld + vec3(1.0, 1.0, 1.0) // Ls + ); + +// The material properties of our object +struct MaterialInfo +{ + vec3 Ka; // Ambient reflectivity + vec3 Kd; // Diffuse reflectivity + vec3 Ks; // Specular reflectivity + float Shininess; // Specular shininess factor +}; + +// The object has a material +uniform MaterialInfo Material = MaterialInfo( + vec3(0.1, 0.1, 0.1), // Ka + vec3(1.0, 1.0, 1.0), // Kd + vec3(1.0, 1.0, 1.0), // Ks + 10.0); // Shininess + +// Attributes passed on from the vertex shader +smooth in vec3 WSVertexPosition; +smooth in vec3 WSVertexNormal; +smooth in vec2 WSTexCoord; + +uniform vec3 camPos; + +/** From http://www.neilmendoza.com/glsl-rotation-about-an-arbitrary-axis/ + */ +mat4 rotationMatrix(vec3 axis, float angle) +{ + //axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + +/** + * Rotate a vector vec by using the rotation that transforms from src to tgt. + */ +vec3 rotateVector(vec3 src, vec3 tgt, vec3 vec) +{ + float angle = acos(dot(src,tgt)); + + // Check for the case when src and tgt are the same vector, in which case + // the cross product will be ill defined. + if (angle == 0.0) + { + return vec; + } + vec3 axis = normalize(cross(src,tgt)); + mat4 R = rotationMatrix(axis,angle); + + // Rotate the vec by this rotation matrix + vec4 _norm = R*vec4(vec,1.0); + return _norm.xyz / _norm.w; +} + +void main() +{ + // Calculate the normal (this is the expensive bit in Phong) + vec3 n = normalize( WSVertexNormal ); + + // Calculate the view vector + vec3 v = normalize(vec3(camPos-WSVertexPosition)); + + // Extract the normal from the normal map (rescale to [-1,1] + vec3 tgt = normalize(texture(NormalTexture, WSTexCoord*texCoordScale).rgb * 2.0 - 1.0); + + // The source is just up in the Z-direction + vec3 src = vec3(0.0, 0.0, 1.0); + + // Perturb the normal according to the target + vec3 np = rotateVector(src, tgt, n); + + // Calculate the light vector + vec3 s = normalize( vec3(Light.Position) - WSVertexPosition ); + + // Reflect the light about the surface normal + vec3 r = reflect( -s, np ); + + // Compute the light from the ambient, diffuse and specular components + vec3 lightColor = ( + Light.La * Material.Ka + + Light.Ld * Material.Kd * max( dot(s, np), 0.0 ) + + Light.Ls * Material.Ks * pow( max( dot(r,v), 0.0 ), Material.Shininess )); + + vec3 texColor = texture(ColourTexture, WSTexCoord*texCoordScale).rgb; + + // Use the following shader for the correct value + fragColour = vec4(texColor*lightColor,1.0); +} + + + diff --git a/shaders/bump_vert.glsl b/shaders/bump_vert.glsl new file mode 100644 index 0000000..802867b --- /dev/null +++ b/shaders/bump_vert.glsl @@ -0,0 +1,34 @@ +#version 430 + +// The modelview and projection matrices are no longer given in OpenGL 4.2 +uniform mat4 MVP; +uniform mat4 MV; +uniform mat4 N; // This is the inverse transpose of the mv matrix + +// The vertex position attribute +layout (location=0) in vec3 VertexPosition; + +// The texture coordinate attribute +layout (location=1) in vec2 TexCoord; + +// The vertex normal attribute +layout (location=2) in vec3 VertexNormal; + +// These attributes are passed onto the shader (should they all be smoothed?) +smooth out vec3 WSVertexPosition; +smooth out vec3 WSVertexNormal; +smooth out vec2 WSTexCoord; + +void main() { + // Transform the vertex normal by the inverse transpose modelview matrix + WSVertexNormal = normalize(vec3(N * vec4(VertexNormal, 1.0f))); + + // Compute the unprojected vertex position + WSVertexPosition = vec3(MV * vec4(VertexPosition, 1.0) ); + + // Copy across the texture coordinates + WSTexCoord = TexCoord; + + // Compute the position of the vertex + gl_Position = MVP * vec4(VertexPosition,1.0); +} diff --git a/src/DemoScene.cpp b/src/DemoScene.cpp index efdaf09..de1b1dc 100644 --- a/src/DemoScene.cpp +++ b/src/DemoScene.cpp @@ -4,6 +4,7 @@ #include "MaterialPhong.h" #include "MaterialFractal.h" #include "MaterialEnvMap.h" +#include "MaterialBump.h" #include //----------------------------------------------------------------------------------------------------- @@ -71,20 +72,23 @@ void DemoScene::keyPress(QKeyEvent* io_event) //----------------------------------------------------------------------------------------------------- void DemoScene::initMaterials() { - m_materials.reserve(6); + m_materials.reserve(7); + + m_materials.emplace_back(new MaterialBump(m_camera, m_shaderLib, &m_matrices)); 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)); m_materials.emplace_back(new MaterialWireframe(m_camera, m_shaderLib, &m_matrices)); m_materials.emplace_back(new MaterialFractal(m_camera, m_shaderLib, &m_matrices)); - for (size_t i = 0; i < m_materials.size(); ++i) + + for (auto& mat : m_materials) { - auto& mat = m_materials[i]; auto name = m_shaderLib->loadShaderProg(mat->shaderFileName()); mat->setShaderName(name); mat->apply(); } + m_materials[m_currentMaterial]->apply(); } //----------------------------------------------------------------------------------------------------- diff --git a/src/MaterialBump.cpp b/src/MaterialBump.cpp new file mode 100644 index 0000000..c72a640 --- /dev/null +++ b/src/MaterialBump.cpp @@ -0,0 +1,72 @@ +#include "MaterialBump.h" +#include "Scene.h" +#include "ShaderLib.h" + +void MaterialBump::init() +{ + initColor(); + auto shaderPtr = m_shaderLib->getShader(m_shaderName); + shaderPtr->setUniformValue("ColourTexture", 0); + + initNormal(); + shaderPtr->setUniformValue("NormalTexture", 2); + + update(); +} + +void MaterialBump::initColor() +{ + + using tex = QOpenGLTexture; + m_colorMap.reset(new QOpenGLTexture(QOpenGLTexture::Target2D)); + auto map = QImage("images/bricktexture.jpg").mirrored().convertToFormat(QImage::Format_RGB888); + m_colorMap->setSize(map.width(), map.height(), map.depth()); + m_colorMap->setFormat(tex::RGBFormat); + m_colorMap->allocateStorage(); + m_colorMap->bind(0, tex::ResetTextureUnit); + m_colorMap->setData(tex::RGB, tex::UInt8, map.constBits()); + m_colorMap->create(); + m_colorMap->setWrapMode(tex::Repeat); + m_colorMap->setMinMagFilters(tex::Linear, tex::Linear); +} + +void MaterialBump::initNormal() +{ + using tex = QOpenGLTexture; + m_normalMap.reset(new QOpenGLTexture(QOpenGLTexture::Target2D)); + auto map = QImage("images/bricknormals.jpg").mirrored().convertToFormat(QImage::Format_RGB888); + m_normalMap->setSize(map.width(), map.height(), map.depth()); + m_normalMap->setFormat(tex::RGBFormat); + m_normalMap->allocateStorage(); + m_normalMap->bind(2, tex::ResetTextureUnit); + m_normalMap->setData(tex::RGB, tex::UInt8, map.constBits()); + m_normalMap->create(); + m_normalMap->setWrapMode(tex::Repeat); + m_normalMap->setMinMagFilters(tex::Linear, tex::Linear); +} + +void MaterialBump::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* MaterialBump::shaderFileName() const +{ + return "shaderPrograms/bump.json"; +} diff --git a/src/MaterialEnvMap.cpp b/src/MaterialEnvMap.cpp index f8e60c9..b222f12 100644 --- a/src/MaterialEnvMap.cpp +++ b/src/MaterialEnvMap.cpp @@ -19,10 +19,15 @@ void MaterialEnvMap::init() void MaterialEnvMap::initGlossMap() { - m_glossMap.reset(new QOpenGLTexture(QImage("images/gloss.png"))); + using tex = QOpenGLTexture; + m_glossMap.reset(new QOpenGLTexture(QOpenGLTexture::Target2D)); + auto map = QImage("images/gloss.png").mirrored().convertToFormat(QImage::Format_RGBA8888); + m_glossMap->setSize(map.width(), map.height(), map.depth()); + m_glossMap->setFormat(tex::RGBAFormat); + m_glossMap->allocateStorage(); + m_glossMap->setData(tex::RGBA, tex::UInt8, map.constBits()); m_glossMap->create(); m_glossMap->bind(1); - using tex = QOpenGLTexture; m_glossMap->setWrapMode(tex::Repeat); m_glossMap->setMinMagFilters(tex::Linear, tex::Linear); }