From 701ccce857c440ac49dd97d02020e2757bb8e5ac Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Tue, 18 Nov 2014 19:00:51 +0100 Subject: [PATCH] Shadow mapping --- Makefile | 2 +- Object.hh | 12 +++-- main.cc | 110 ++++++++++++++++++++++++++++++++++++++++++-- objectParser.cc | 2 +- shaders/shadow.fs | 5 ++ shaders/shadow.vs | 12 +++++ shaders/textured.fs | 40 +++++++++++++++- shaders/textured.vs | 10 ++-- texture.hh | 82 +++++++++++++++++++++++++++++++++ 9 files changed, 258 insertions(+), 17 deletions(-) create mode 100644 shaders/shadow.fs create mode 100644 shaders/shadow.vs diff --git a/Makefile b/Makefile index 83f2215..def1c28 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CXX=g++ -CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -Wno-unused-function -Wno-unused-parameter -std=c++11 +CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -Wno-unused-function -Wno-unused-parameter -Wno-sign-compare -std=c++11 LDOPTS=-flto LIBS=-lglbinding -lSDL2 -lSDL2_image -lobj CXXSRCS=main.cc objectParser.cc diff --git a/Object.hh b/Object.hh index e40bbf8..a235bcb 100644 --- a/Object.hh +++ b/Object.hh @@ -10,23 +10,27 @@ class Object { public: Object(VBOManager& vboManager, std::vector const& vas, std::vector const& indices, Program& prog) - : _vboManager(vboManager), _prog(prog), _indices(indices) { + : _vboManager(vboManager), _prog(prog), _indices(indices), _vaID(0) { construct(vas); } Object(VBOManager& vboManager, std::string const& filename, Program& prog) - : _vboManager(vboManager), _prog(prog) { + : _vboManager(vboManager), _prog(prog), _vaID(0) { auto tmp = readObject(filename); _indices = std::get<1>(tmp); construct(std::get<0>(tmp)); } ~Object() { + glDeleteVertexArrays(1, &_vaID); } - void draw(glm::mat4 const& modelview) const { + void draw(glm::mat4 const& modelview, Program *override = nullptr) const { glBindVertexArray(_vaID); - _prog.use(); + if (override) + override->use(); + else + _prog.use(); glUniformMatrix4fv(_prog.getUniformLocation("model_matrix"), 1, GL_FALSE, glm::value_ptr(modelview)); glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_SHORT, _indices.data()); diff --git a/main.cc b/main.cc index a4b8166..0cd4140 100644 --- a/main.cc +++ b/main.cc @@ -63,8 +63,8 @@ int main(int argc, char *argv[]) window = SDL_CreateWindow("SDL2 Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - 640, - 480, + 1680, + 1050, /*SDL_WINDOW_FULLSCREEN_DESKTOP |*/ SDL_WINDOW_OPENGL); if (!window) { @@ -131,11 +131,35 @@ int main(int argc, char *argv[]) std::printf("Warning: extension GL_EXT_texture_filter_anisotropic not supported\n"); Texture2D redTex("textures/red.png"); Texture2D whiteTex("textures/white.png"); + + const unsigned lights = 2; + const unsigned shadowMapSize = 512; + glm::mat4 shadowProj = glm::perspectiveFov(90.0f, static_cast(shadowMapSize), + static_cast(shadowMapSize), 1.0f, 128.0f); + std::vector shadowMaps; + for(unsigned i = 0;i < lights;++i) { + shadowMaps.emplace_back(shadowMapSize); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, static_cast(GL_LESS)); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, static_cast(GL_COMPARE_REF_TO_TEXTURE)); + } + + const glm::vec3 lightPos[lights] = {glm::vec3(2.0, -2.0, 10.0), + glm::vec3(2.0, 2.0, 10.0)}; + // const glm::vec3 lightColor[lights] = {glm::vec3(1.0, 0.9, 0.8), + // glm::vec3(0.0, 1.0, 0.0)}; + // const float lightIntensity[lights] = {75.0f, 25.0f}; + + + Framebuffer shadowFB; VertexShader vs{fileToString("shaders/textured.vs")}; FragmentShader fs{fileToString("shaders/textured.fs")}; - Program prog(vs, fs); - + Program prog{vs, fs}; + + VertexShader shadowVs{fileToString("shaders/shadow.vs")}; + FragmentShader shadowFs{fileToString("shaders/shadow.fs")}; + Program shadowProg{shadowVs, shadowFs}; + prog.use(); glUniformMatrix4fv(prog.getUniformLocation("projection_matrix"), 1, GL_FALSE, glm::value_ptr(proj)); @@ -143,6 +167,13 @@ int main(int argc, char *argv[]) glm::value_ptr(view)); glUniform1i(prog.getUniformLocation("texBase"), 0); + const GLint shadowMapTUs[] = {1, 2}; + glUniform1iv(prog.getUniformLocation("texShadowMaps"), 2, shadowMapTUs); + + shadowProg.use(); + glUniformMatrix4fv(prog.getUniformLocation("projection_matrix"), 1, GL_FALSE, + glm::value_ptr(shadowProj)); + Object box(vboManager, "objects/woodbox.obj", prog); Object pyramid(vboManager, "objects/pyramid.obj", prog); Object plane(vboManager, "objects/plane.obj", prog); @@ -165,18 +196,87 @@ int main(int argc, char *argv[]) } } + // Draw shadow maps + GLint origVP[4]; + glGetIntegerv(GL_VIEWPORT, origVP); + glViewport(0, 0, shadowMapSize, shadowMapSize); + shadowProg.use(); + shadowFB.bind(); + glPolygonOffset(1.1f, 4.0f); + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + glEnable(GL_POLYGON_OFFSET_POINT); + for (unsigned light = 0;light < lights;++light) { + for (unsigned i = 0;i < 6;++i) { + GLenum const face[6] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z}; + + const glm::vec3 dir[6] = {glm::vec3(1.0f, 0.0f, 0.0f), + glm::vec3(-1.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f), + glm::vec3(0.0f, -1.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 1.0f), + glm::vec3(0.0f, 0.0f, -1.0f)}; + const glm::vec3 up[6] = {glm::vec3(0.0f, 1.0f, 0.0f), + glm::vec3(0.0f, -1.0f, 0.0f), + glm::vec3(1.0f, 0.0f, 0.0f), + glm::vec3(-1.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f), + glm::vec3(0.0f, -1.0f, 0.0f)}; + + shadowMaps[light].bind(); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + face[i], shadowMaps[light].getID(), 0); + glClear(GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + + glm::mat4 view = glm::lookAt(lightPos[light], + lightPos[light]+dir[i], + up[i]); + glUniformMatrix4fv(prog.getUniformLocation("view_matrix"), 1, GL_FALSE, + glm::value_ptr(view)); + + glm::mat4 model = glm::translate(glm::vec3(0.5f, 0.5f, -0.5f)) * + glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) * + glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f)); + box.draw(model, &shadowProg); + plane.draw(glm::translate(glm::vec3(2.0f, -2.5f, 0.0f))* + glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f)), &shadowProg); + pyramid.draw(glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f)), &shadowProg); + } + } + + GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, UnusedMask()); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glViewport(origVP[0], origVP[1], origVP[2], origVP[3]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_POLYGON_OFFSET_LINE); + glDisable(GL_POLYGON_OFFSET_POINT); + + glActiveTexture(GL_TEXTURE2); + shadowMaps[1].bind(); + glActiveTexture(GL_TEXTURE1); + shadowMaps[0].bind(); + glActiveTexture(GL_TEXTURE0); glm::mat4 model = glm::translate(glm::vec3(0.5f, 0.5f, -0.5f)) * glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f)); cubeTex.bind(); + + // Shadow maps must be rendered before real drawing can begin + glClientWaitSync(fence, SyncObjectMask(), 0xffffffffu); + glDeleteSync(fence); + box.draw(model); whiteTex.bind(); plane.draw(glm::translate(glm::vec3(2.0f, -2.5f, 0.0f))* - glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f))); + glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f))); redTex.bind(); pyramid.draw(glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f))); diff --git a/objectParser.cc b/objectParser.cc index 494f9a5..dc83161 100644 --- a/objectParser.cc +++ b/objectParser.cc @@ -384,7 +384,7 @@ public: obj_parser.parse(filename); } - std::vector buildVA() + std::vector buildVA() const { std::vector ret; for (auto const& ent : _vertex_tc_norm) { diff --git a/shaders/shadow.fs b/shaders/shadow.fs new file mode 100644 index 0000000..9da1ce7 --- /dev/null +++ b/shaders/shadow.fs @@ -0,0 +1,5 @@ +#version 330 core + +void main(void) { + // Only standard depth calc. +} diff --git a/shaders/shadow.vs b/shaders/shadow.vs new file mode 100644 index 0000000..23dd3f1 --- /dev/null +++ b/shaders/shadow.vs @@ -0,0 +1,12 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +uniform mat4 projection_matrix; +uniform mat4 view_matrix; +uniform mat4 model_matrix; + +layout(location = 0) in vec3 vertex; + +void main(void) { + gl_Position = projection_matrix * view_matrix * model_matrix * vec4(vertex, 1.0); +} diff --git a/shaders/textured.fs b/shaders/textured.fs index c5eed25..085db7e 100644 --- a/shaders/textured.fs +++ b/shaders/textured.fs @@ -1,16 +1,52 @@ #version 330 core +const uint lights = 2u; + uniform sampler2D texBase; +uniform samplerCubeShadow texShadowMaps[lights]; + +float VectorToDepth (vec3 Vec) +{ + vec3 AbsVec = abs(Vec); + float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z)); + + // Replace f and n with the far and near plane values you used when + // you drew your cube map. + const float f = 128.0; + const float n = 1.0; + + float NormZComp = (f+n) / (f-n) - (2*f*n)/(f-n)/LocalZcomp; + return (NormZComp + 1.0) * 0.5; +} in vec2 fragTC; //in vec3 fragNorm; //in vec3 light0Vect; -in vec3 light; +in vec3 lightColors[lights]; +in vec3 lightVecs[lights]; out vec4 color; +const float bias = 0.00; + void main(void) { vec4 texColor = texture(texBase, fragTC); - color = texColor*vec4(light, 1.0)+texColor*0.05; + vec4 col = vec4(0.0, 0.0, 0.0, 0.0); + { + float depth = VectorToDepth(lightVecs[0])-bias; + vec3 nlv = normalize(lightVecs[0]); + float depth_compare = texture(texShadowMaps[0], vec4(nlv, depth)); + // color = vec4(depth_compare, 0.0, 0.0, 1.0); + col += texColor*vec4(lightColors[0], 1.0)*depth_compare; + } + { + float depth = VectorToDepth(lightVecs[1])-bias; + vec3 nlv = normalize(lightVecs[1]); + float depth_compare = texture(texShadowMaps[1], vec4(nlv, depth)); + // color = vec4(depth_compare, 0.0, 0.0, 1.0); + col += texColor*vec4(lightColors[1], 1.0)*depth_compare; + } + color = col+texColor*0.05; + } diff --git a/shaders/textured.vs b/shaders/textured.vs index c176fae..3dd6345 100644 --- a/shaders/textured.vs +++ b/shaders/textured.vs @@ -19,7 +19,8 @@ layout(location = 1) in vec2 vertexTC; layout(location = 2) in vec3 vertexNorm; out vec2 fragTC; -out vec3 light; +out vec3 lightColors[lights]; +out vec3 lightVecs[lights]; void main(void) { vec4 vertex_Pos = model_matrix * vec4(vertex, 1.0); @@ -28,12 +29,13 @@ void main(void) { fragTC = vertexTC; vec3 vertexNorm_trans = (model_matrix*vec4(vertexNorm, 0.0)).xyz; - light = vec3(0.0, 0.0, 0.0); + for (uint i = 0u;i < lights;++i) { vec3 lightVec = lightPos[i]-vertex_Pos.xyz; + lightVecs[i] = -lightVec; vec3 lightNorm = normalize(lightVec); float lightDist = length(lightVec); - light += lightColor[i]*clamp(dot(vertexNorm_trans, lightNorm), 0.0, 1.0)* - clamp(lightIntensity[i]/(lightDist*lightDist), 0.0, 1.0); + lightColors[i] = lightColor[i]*clamp(dot(vertexNorm_trans, lightNorm), 0.0, 1.0)* + clamp(lightIntensity[i]/(lightDist*lightDist), 0.0, 1.0); } } diff --git a/texture.hh b/texture.hh index 9c72e93..e4f62cf 100644 --- a/texture.hh +++ b/texture.hh @@ -22,6 +22,88 @@ static unsigned ilog2(unsigned in) return ret; } +class Framebuffer { +public: + Framebuffer() { + glGenFramebuffers(1, &_fbID); + } + + ~Framebuffer() { + glDeleteFramebuffers(1, &_fbID); + } + + void bind() const { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbID); + } + + GLuint getID() const { + return _fbID; + } + + void attachTexture(GLenum textarget, GLuint texID) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + textarget, texID, 0); + } + +private: + GLuint _fbID; +}; + +class TextureCubeMap { +public: + TextureCubeMap(unsigned size) { + glGenTextures(1, &_texID); + glBindTexture(GL_TEXTURE_CUBE_MAP, _texID); + + if(SDL_GL_ExtensionSupported("GL_ARB_texture_storage")) + glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_DEPTH_COMPONENT24, + size, size); + else { + std::printf("Warning: extension GL_ARB_texture_storage not supported!\n"); + for (unsigned i = 0;i < 6;++i) { + GLenum const face[6] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z}; + glTexImage2D(face[i], 0, static_cast(GL_DEPTH_COMPONENT16), size, size, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); + } + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, static_cast(GL_LINEAR)); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, static_cast(GL_LINEAR)); + } + + ~TextureCubeMap() { + glDeleteTextures(1, &_texID); + } + + TextureCubeMap(TextureCubeMap const& copy) = delete; + TextureCubeMap& operator=(TextureCubeMap const& copy) = delete; + + TextureCubeMap(TextureCubeMap && move) : _texID(move._texID) { + move._texID = 0; + } + + TextureCubeMap& operator=(TextureCubeMap && move) { + glDeleteTextures(1, &_texID); + _texID = move._texID; + move._texID = 0; + + return *this; + } + + void bind() const { + glBindTexture(GL_TEXTURE_CUBE_MAP, _texID); + } + + GLuint getID() const { + return _texID; + } + +private: + GLuint _texID; +}; + class Texture2D { public: Texture2D(unsigned width, unsigned height) {