Purpose of the Sample:
This sample demonstrates how to do shadow mapping.
CGame.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#ifndef __CGAME__ #define __CGAME__ #include <glad/glad.h> #include "shader_s.h" #include "Model.h" #include "glm/mat4x4.hpp" #include "glm/vec3.hpp" #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/type_ptr.hpp" class Model; class Mesh; struct GfxContext { glm::mat4 worldMatrix; glm::mat4 viewMatrix; glm::mat4 projectionMatrix; glm::mat4 mvpMatrix; glm::vec3 eyePos; glm::vec3 lightDirection; glm::mat4 lightViewProjectionMatrix; float time; }; class CGame { public: CGame() {}; void Initialize(unsigned int screenWidth, unsigned int screenHeight); void Shutdown(); void Render(); void OnResize(int screenWidth, int screenHeight); void ReloadShaders(); private: void SetupGPUProgram(); void SetupModel(); void DrawModel(Model& model, Shader& shader, GfxContext& context); void DrawScene(Shader* replacementShader); void RenderShadowMap(); void RenderScene(); void ResolveToScreen(); void CreateMSAAFBO(); void CreateShadowMapFBO(); Shader* m_pShaderPass0; Shader* m_pShaderPass1; Shader* m_ShadowMapShader; Model* m_pManModel; Model* m_pQuad; Model* m_pTorus; GLuint m_ColorTextureId; GLuint m_DepthStencilTextureId; GLuint m_IntermediateFBOId; GLuint m_ShadowMapNullColorTextureId; GLuint m_ShadowMapDepthStencilTextureId; GLuint m_ShadowMapFBOId; unsigned int m_Width; unsigned int m_Height; }; #endif |
CGame.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
#include "CGame.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image/stb_image.h" #include "Mesh.h" #include GfxContext gfxContext; void CGame::Initialize(unsigned int screenWidth, unsigned int screenHeight) { m_Width = screenWidth; m_Height = screenHeight; SetupGPUProgram(); SetupModel(); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEnable(GL_MULTISAMPLE); glEnable(GL_STENCIL_TEST); CreateMSAAFBO(); CreateShadowMapFBO(); glBindFramebuffer(GL_FRAMEBUFFER, 0); } void CGame::CreateMSAAFBO() { glGenFramebuffers(1, &m_IntermediateFBOId); glBindFramebuffer(GL_FRAMEBUFFER, m_IntermediateFBOId); glGenTextures(1, &m_ColorTextureId); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorTextureId); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB8, m_Width, m_Height, GL_TRUE); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorTextureId, 0); glGenTextures(1, &m_DepthStencilTextureId); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_DepthStencilTextureId); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_DEPTH24_STENCIL8, m_Width, m_Height, GL_TRUE); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, m_DepthStencilTextureId, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { int a = 0; ++a; } } void CGame::CreateShadowMapFBO() { int shadowTextureWidth = 1024; int shadowTextureHeight = 1024; glGenFramebuffers(1, &m_ShadowMapFBOId); glBindFramebuffer(GL_FRAMEBUFFER, m_ShadowMapFBOId); glGenTextures(1, &m_ShadowMapDepthStencilTextureId); glBindTexture(GL_TEXTURE_2D, m_ShadowMapDepthStencilTextureId); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadowTextureWidth, shadowTextureHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_ShadowMapDepthStencilTextureId, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { // Great! } } void CGame::Shutdown() { } void CGame::OnResize(int width, int height) { m_Width = width; m_Height = height; glViewport(0, 0, m_Width, m_Height); glScissor(0, 0, m_Width, m_Height); } void CGame::DrawModel(Model& model, Shader& shader, GfxContext& context) { gfxContext.mvpMatrix = gfxContext.projectionMatrix * gfxContext.viewMatrix * gfxContext.worldMatrix; shader.setMatrix4x4("modelMatrix", glm::value_ptr(gfxContext.worldMatrix)); shader.setMatrix4x4("viewMatrix", glm::value_ptr(gfxContext.viewMatrix)); shader.setMatrix4x4("projectionMatrix", glm::value_ptr(gfxContext.projectionMatrix)); shader.setMatrix4x4("mvpMatrix", glm::value_ptr(gfxContext.mvpMatrix)); shader.setVector3("eyePos", glm::value_ptr(gfxContext.eyePos)); shader.setVector3("lightDirection", glm::value_ptr(gfxContext.lightDirection)); shader.setMatrix4x4("lightViewProjMatrix", glm::value_ptr(gfxContext.lightViewProjectionMatrix)); shader.setFloat("time", gfxContext.time); model.meshes[0].Draw(shader); } void CGame::RenderShadowMap() { const char* debugGroupName = "Render Shadow Maps"; glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, strlen(debugGroupName), debugGroupName); glDisable(GL_MULTISAMPLE); glBindFramebuffer(GL_FRAMEBUFFER, m_ShadowMapFBOId); glViewport(0, 0, 1024, 1024); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glClearDepth(1.0f); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); DrawScene(m_ShadowMapShader); glCullFace(GL_BACK); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glPopDebugGroup(); } void CGame::RenderScene() { const char* debugGroupName = "Render Scene"; glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, strlen(debugGroupName), debugGroupName); glEnable(GL_MULTISAMPLE); glBindFramebuffer(GL_FRAMEBUFFER, m_IntermediateFBOId); glViewport(0, 0, m_Width, m_Height); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClearDepth(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); DrawScene(NULL); glPopDebugGroup(); } void CGame::ResolveToScreen() { const char* debugGroupName = "Resolve To Screen"; glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, strlen(debugGroupName), debugGroupName); // Blit intermediate FBO to backbuffer FBO glBindFramebuffer(GL_READ_FRAMEBUFFER, m_IntermediateFBOId); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBlitFramebuffer(0, 0, m_Width, m_Height, 0, 0, m_Width, m_Height, GL_COLOR_BUFFER_BIT, GL_NEAREST); glPopDebugGroup(); } void CGame::DrawScene(Shader* replacementShader) { Shader* shaderToUse = replacementShader != NULL ? replacementShader : m_pShaderPass0; // Light Matrices glm::vec3 lightPosition = glm::vec3(-5, 5, 10); gfxContext.lightDirection = glm::normalize(glm::vec3(0, 0, 0) - lightPosition); glm::mat4 lightViewMatrix = glm::lookAt(lightPosition, glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); float orthoProjScale = 5.0f; glm::mat4 lightProjectionMatrix = glm::ortho(-orthoProjScale, orthoProjScale, -orthoProjScale, orthoProjScale, 0.01f, 100.0f); gfxContext.lightViewProjectionMatrix = lightProjectionMatrix * lightViewMatrix; // Setup View Matrix const float degreesPerSecond = 45.0f; glm::mat4 viewRotation = glm::rotate(glm::mat4(), glm::radians(glm::radians(25.0f) * gfxContext.time), glm::vec3(0, 1, 0)); float x = cos(glm::radians(degreesPerSecond) * gfxContext.time) * 4.5f; float z = sin(glm::radians(degreesPerSecond) * gfxContext.time) * 4.5f; gfxContext.eyePos = glm::vec4(x, 3.0f, z, 0.0f); gfxContext.viewMatrix = glm::lookAt(gfxContext.eyePos, glm::vec3(0, 1, 0), glm::vec3(0, 1, 0)); gfxContext.projectionMatrix = glm::perspective(glm::radians(60.0f), m_Width / static_cast(m_Height), 0.03f, 100.0f); // Draw Man float scaleFactor = 0.4f; gfxContext.worldMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(scaleFactor, scaleFactor, scaleFactor)); gfxContext.worldMatrix = glm::rotate(gfxContext.worldMatrix, glm::radians(0.0f), glm::vec3(0, 1, 0)); glm::vec3 diffuseColor = glm::vec3(1, 0, 0); shaderToUse->use(); shaderToUse->setVector3("diffuseColor", glm::value_ptr(diffuseColor)); DrawModel(*m_pManModel, *shaderToUse, gfxContext); // Draw Torus scaleFactor = 0.3f; gfxContext.worldMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(scaleFactor, scaleFactor, scaleFactor)); gfxContext.worldMatrix = glm::translate(gfxContext.worldMatrix, glm::vec3(-5, 7, -10)); gfxContext.worldMatrix = glm::rotate(gfxContext.worldMatrix, glm::radians(360.0f) * gfxContext.time, glm::vec3(0, 1, 0)); diffuseColor = glm::vec3(1, 1, 0); shaderToUse->use(); shaderToUse->setVector3("diffuseColor", glm::value_ptr(diffuseColor)); DrawModel(*m_pTorus, *shaderToUse, gfxContext); // Draw a Floor quad diffuseColor = glm::vec3(0.8f, 0.8f, 0.8f); if (replacementShader == NULL) { shaderToUse = m_pShaderPass1; glActiveTexture(0); glBindTexture(GL_TEXTURE_2D, m_ShadowMapDepthStencilTextureId); } shaderToUse->use(); shaderToUse->setVector3("diffuseColor", glm::value_ptr(diffuseColor)); gfxContext.worldMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -0.01f, 0.0f)); gfxContext.worldMatrix = glm::scale(gfxContext.worldMatrix, glm::vec3(100.0f, 100.0f, 100.0f)); gfxContext.worldMatrix = glm::rotate(gfxContext.worldMatrix, glm::radians(0.0f), glm::vec3(0, 1, 0)); DrawModel(*m_pQuad, *shaderToUse, gfxContext); glBindTexture(GL_TEXTURE_2D, 0); } void CGame::Render() { static auto startTime = std::chrono::high_resolution_clock::now(); auto currentTime = std::chrono::high_resolution_clock::now(); gfxContext.time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count(); RenderShadowMap(); RenderScene(); ResolveToScreen(); } void CGame::SetupModel() { m_pManModel = new Model(); m_pManModel->Load("man.obj"); m_pTorus = new Model(); m_pTorus->Load("torus.obj"); m_pQuad = new Model(); m_pQuad->Load("quad.obj"); } void CGame::SetupGPUProgram() { m_pShaderPass0 = new Shader("vertexPass0.glsl", "fragmentPass0.glsl"); m_pShaderPass1 = new Shader("vertexPass1.glsl", "fragmentPass1.glsl"); m_ShadowMapShader = new Shader("shadowMapVertex.glsl", "shadowMapFragment.glsl"); } void CGame::ReloadShaders() { OutputDebugStringA("Reloading Shaders!\n"); SetupGPUProgram(); } |
vertexPass0.glsl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 mvpMatrix; uniform vec3 eyePos; uniform vec3 lightDirection; uniform mat4 lightViewProjMatrix; uniform float time; uniform vec3 diffuseColor; uniform vec3 specularColor; out vec3 fsNormal; out vec3 fsUV; out vec3 fsFragPos; out vec4 fsShadowPos; void main() { vec4 pos = mvpMatrix * vec4(position.xyz, 1); fsNormal = vec3(modelMatrix * vec4(normal, 0)).xyz; fsFragPos = vec3(modelMatrix * vec4(position.xyz, 0)).xyz; gl_Position = pos; } |
fragmentPass0.glsl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#version 330 core out vec4 FragColor; in vec3 fsNormal; in vec3 fsFragPos; uniform vec3 eyePos; uniform vec3 lightDirection; uniform vec3 diffuseColor; uniform vec3 specularColor; vec3 calculateDirectionalLighting(vec3 lightDir, vec3 normal, vec3 viewDir) { float di = clamp(dot(normal, lightDir), 0, 1); vec3 half = normalize( lightDir + viewDir ); float si = clamp(dot(normal, half), 0, 1); float ki = 0.01; vec3 ambient = ki * vec3(1); vec3 diffuse = di * diffuseColor; vec3 specular = pow(si, 20.0) * vec3(1); vec3 finalColor = ambient + diffuse + specular; return finalColor; } void main() { vec3 viewDir = normalize(eyePos - fsFragPos); vec3 normal = normalize(fsNormal); vec3 finalColor = calculateDirectionalLighting(-lightDirection, normal, viewDir); FragColor = vec4(finalColor, 1.0f); } |
vertexPass1.glsl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; layout (location = 2) in vec3 uv; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 mvpMatrix; uniform vec3 eyePos; uniform vec3 lightDirection; uniform mat4 lightViewProjMatrix; uniform float time; uniform vec3 diffuseColor; uniform vec3 specularColor; out vec3 fsNormal; out vec3 fsUV; out vec3 fsFragPos; out vec4 fsShadowPos; void main() { vec4 pos = mvpMatrix * vec4(position.xyz, 1); fsNormal = vec3(modelMatrix * vec4(normal, 0)).xyz; fsFragPos = vec3(modelMatrix * vec4(position.xyz, 1)).xyz; fsShadowPos = lightViewProjMatrix * vec4(fsFragPos.xyz,1); fsUV = uv; gl_Position = pos; } |
fragmentPass1.glsl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#version 330 core out vec4 FragColor; in vec3 fsNormal; in vec3 fsUV; in vec3 fsFragPos; in vec4 fsShadowPos; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 mvpMatrix; uniform vec3 eyePos; uniform vec3 lightDirection; uniform mat4 lightViewProjMatrix; uniform float time; uniform vec3 diffuseColor; uniform vec3 specularColor; uniform sampler2D shadowMapTexture; float generateCheckerboard(vec2 uv) { vec2 p = floor(uv*100.0); float s = mod( p.x + p.y, 2.0 ); return s; } vec3 calculateDirectionalLighting(vec3 lightDir, vec3 normal, vec3 viewDir, vec2 uv, float shadowIntensity) { normal = vec3(0,1,0); float di = clamp(dot(normal, lightDir), 0, 1); vec3 half = normalize( lightDir + viewDir ); float si = clamp(dot(normal, half), 0, 1); float ki = 0.2; vec3 ambient = (ki * vec3(1.0)) * (1.0-shadowIntensity); vec3 diffuse = di * (diffuseColor * generateCheckerboard(uv)); vec3 specular = pow(si, 10.0) * vec3(0.5); vec3 finalColor = ambient + diffuse + specular; return finalColor; } float calculateShadowIntensity(vec4 shadowPos) { vec3 shadowCoord = (shadowPos.xyz/shadowPos.w) * 0.5 + 0.5; float currentDepth = shadowCoord.z; float closestDepth = texture(shadowMapTexture, shadowCoord.xy).r; if(currentDepth > 1.0) { return 0.0; } float bias = 0.0001; float shadow = 0.0; if((currentDepth - bias) > closestDepth) { shadow = 1.0; } return shadow; } void main() { vec3 viewDir = normalize(eyePos - fsFragPos); vec3 normal = normalize(fsNormal); float shadowIntensity = calculateShadowIntensity(fsShadowPos); vec3 finalColor = calculateDirectionalLighting(-lightDirection, normal, viewDir, fsUV.xy, shadowIntensity); FragColor = vec4(finalColor, 1.0f); } |
shadowMapVertex.glsl
1 2 3 4 5 6 7 8 9 10 11 |
#version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; uniform mat4 modelMatrix; uniform mat4 lightViewProjMatrix; void main() { gl_Position = lightViewProjMatrix * modelMatrix * vec4(position.xyz, 1); } |
shadowMapFragment.glsl
1 2 3 4 5 6 7 |
#version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f); } |