3D C/C++ tutorials -> OpenGL 2.1 -> GLSL shadow mapping
Use for personal or educational purposes only. Commercial and other profit uses strictly prohibited. Exploitation of content on a website or in a publication prohibited.
opengl_21_tutorials_win32_framework.h
...
#define SHADOW_MAP_SIZE 1024
class COpenGLRenderer
{
protected:
...
mat4x4 ..., ViewMatrixInverse, ..., ProjectionMatrixInverse;
protected:
CTexture Texture[2];
CShaderProgram ShadowMapping;
GLuint VBO, ShadowMap, FBO;
mat4x4 LightViewMatrix, LightProjectionMatrix, ShadowMatrix;
public:
bool Texturing, ShowShadowMap, Pause;
vec2 CursorPosition;
public:
...
protected:
void InitArrayBuffers();
};
...
opengl_21_tutorials_win32_framework.cpp
...
COpenGLRenderer::COpenGLRenderer()
{
Texturing = true;
ShowShadowMap = false;
Pause = false;
Camera.SetViewMatrixPointer(&ViewMatrix, &ViewMatrixInverse);
}
COpenGLRenderer::~COpenGLRenderer()
{
}
bool COpenGLRenderer::Init()
{
// ------------------------------------------------------------------------------------------------------------------------
bool Error = false;
// check OpenGL extensions ------------------------------------------------------------------------------------------------
if(!GL_ARB_depth_texture)
{
ErrorLog.Append("GL_ARB_depth_texture not supported!\r\n");
Error = true;
}
if(!GLEW_EXT_framebuffer_object)
{
ErrorLog.Append("GL_EXT_framebuffer_object not supported!\r\n");
Error = true;
}
// load textures and shaders ----------------------------------------------------------------------------------------------
Error |= !Texture[0].LoadTexture2D("floor.jpg");
Error |= !Texture[1].LoadTexture2D("cube.jpg");
Error |= !ShadowMapping.Load("shadowmapping.vs", "shadowmapping.fs");
// if an error occurred, return false -------------------------------------------------------------------------------------
if(Error)
{
return false;
}
// get uniform locations --------------------------------------------------------------------------------------------------
ShadowMapping.UniformLocations = new GLuint[2];
ShadowMapping.UniformLocations[0] = glGetUniformLocation(ShadowMapping, "Texturing");
ShadowMapping.UniformLocations[1] = glGetUniformLocation(ShadowMapping, "ShadowMatrix");
// set constant uniforms --------------------------------------------------------------------------------------------------
glUseProgram(ShadowMapping);
glUniform1i(glGetUniformLocation(ShadowMapping, "Texture"), 0);
glUniform1i(glGetUniformLocation(ShadowMapping, "ShadowMap"), 1);
glUseProgram(0);
// init array buffers -----------------------------------------------------------------------------------------------------
...
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, 1664, Data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// set light projection matrix --------------------------------------------------------------------------------------------
LightProjectionMatrix = perspective(35.0f, 1.0f, 4.0f, 16.0f);
// generate shadow map texture --------------------------------------------------------------------------------------------
glGenTextures(1, &ShadowMap);
glBindTexture(GL_TEXTURE_2D, ShadowMap);
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);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
// glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// generate FBO -----------------------------------------------------------------------------------------------------------
glGenFramebuffersEXT(1, &FBO);
// set light --------------------------------------------------------------------------------------------------------------
vec3 LightColor = vec3(1.0f, 1.0f, 1.0f);
glLightfv(GL_LIGHT0, GL_AMBIENT, &vec4(LightColor * 0.25f, 1.0f));
glLightfv(GL_LIGHT0, GL_DIFFUSE, &vec4(LightColor * 0.75f, 1.0f));
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0f / 128.0f);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 1.0f / 256.0f);
// set camera -------------------------------------------------------------------------------------------------------------
Camera.Look(vec3(1.75f, 1.75f, 5.0f), vec3(0.0f, 0.0f, 0.0f));
// ------------------------------------------------------------------------------------------------------------------------
return true;
}
void COpenGLRenderer::Render(float FrameTime)
{
static vec3 LightPosition = vec3(0.0f, 2.5f, 5.0f);
// render shadow map ------------------------------------------------------------------------------------------------------
if(!Pause)
{
glViewport(0, 0, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&LightProjectionMatrix);
LightViewMatrix = look(LightPosition, vec3(0.0f), vec3(0.0f, 1.0f, 0.0f));
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&LightViewMatrix);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
glDrawBuffers(0, NULL); glReadBuffer(GL_NONE);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ShadowMap, 0);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 32, (void*)20);
glDrawArrays(GL_QUADS, 0, 52);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glLoadMatrixf(&LightViewMatrix);
glTranslatef(0.0f, -0.375f, 0.5f);
glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
glutSolidTorus(0.125f, 0.25f, 64, 64);
glCullFace(GL_BACK);
glLoadMatrixf(&LightViewMatrix);
glTranslatef(0.0f, -0.31f, -0.5f);
glutSolidTeapot(0.25f);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
// render scene -----------------------------------------------------------------------------------------------------------
glViewport(0, 0, Width, Height);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&ProjectionMatrix);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&ViewMatrix);
glLightfv(GL_LIGHT0, GL_POSITION, &vec4(LightPosition, 1.0f));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glUseProgram(ShadowMapping);
ShadowMatrix = BiasMatrix * LightProjectionMatrix * LightViewMatrix * ViewMatrixInverse;
glUniformMatrix4fv(ShadowMapping.UniformLocations[1], 1, GL_FALSE, &ShadowMatrix);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, ShadowMap);
if(Texturing)
{
glUniform1i(ShadowMapping.UniformLocations[0], 1);
}
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 32, (void*)0);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 32, (void*)8);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 32, (void*)20);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, Texture[0]);
glColor3f(1.0f, 1.0f, 1.0f);
glDrawArrays(GL_QUADS, 0, 4);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, Texture[1]);
glColor3f(1.0f, 1.0f, 1.0f);
glDrawArrays(GL_QUADS, 4, 48);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUniform1i(ShadowMapping.UniformLocations[0], 0);
glLoadMatrixf(&ViewMatrix);
glTranslatef(0.0f, -0.375f, 0.5f);
glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
glutSolidTorus(0.125f, 0.25f, 64, 64);
glDisable(GL_CULL_FACE);
glLoadMatrixf(&ViewMatrix);
glTranslatef(0.0f, -0.31f, -0.5f);
glutSolidTeapot(0.25f);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glDisable(GL_DEPTH_TEST);
// display shadow map -----------------------------------------------------------------------------------------------------
if(ShowShadowMap)
{
glViewport(0, 0, 512, 512);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&ortho(0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ShadowMap);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 1.0f);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
// shadow depth test visualization ------------------------------------------------------------------------------------
glViewport(0, 0, Width, Height);
float Depth;
// read depth of the pixel under the cursor from the depth buffer
glReadPixels((int)CursorPosition.x, (int)CursorPosition.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &Depth);
// get NDC space position of the pixel under the cursor
vec4 NDCSpaceCursorPosition = vec4(CursorPosition.x / (float)(Width - 1), CursorPosition.y / (float)(Height - 1), Depth, 1.0f) * 2.0f - 1.0f;
// get view space position of the pixel under the cursor
vec4 Position = ProjectionMatrixInverse * NDCSpaceCursorPosition;
Position /= Position.w;
// multiply ShadowMatrix by Position
vec4 ShadowMapTexCoord = ShadowMatrix * Position;
if(ShadowMapTexCoord.w > 0.0)
{
// get shadow map texture space position of the pixel under the cursor
vec4 ShadowMapTexCoordProj = ShadowMapTexCoord / ShadowMapTexCoord.w;
if(ShadowMapTexCoordProj.x >= 0.0 && ShadowMapTexCoordProj.x < 1.0 && ShadowMapTexCoordProj.y >= 0.0 && ShadowMapTexCoordProj.y < 1.0 && ShadowMapTexCoordProj.z >= 0.0 && ShadowMapTexCoordProj.z < 1.0)
{
// read depth from the shadow map depth texture
glViewport(0, 0, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
glDrawBuffers(0, NULL); glReadBuffer(GL_NONE);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ShadowMap, 0);
glReadPixels((int)(ShadowMapTexCoordProj.x * SHADOW_MAP_SIZE), (int)(ShadowMapTexCoordProj.y * SHADOW_MAP_SIZE), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &Depth);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glViewport(0, 0, 512, 512);
if(Depth <= ShadowMapTexCoordProj.z)
{
// if the pixel under the cursor is in shadow, the cross will be green
glColor3f(0.0f, 1.0f, 0.0f);
}
else
{
// otherwise the cross will be red
glColor3f(1.0f, 0.0f, 0.0f);
}
glBegin(GL_LINES);
glVertex2f(ShadowMapTexCoordProj.x - 0.0078125f, ShadowMapTexCoordProj.y);
glVertex2f(ShadowMapTexCoordProj.x + 0.0078125f, ShadowMapTexCoordProj.y);
glVertex2f(ShadowMapTexCoordProj.x, ShadowMapTexCoordProj.y - 0.0078125f);
glVertex2f(ShadowMapTexCoordProj.x, ShadowMapTexCoordProj.y + 0.0078125f);
glEnd();
}
}
}
// move light -------------------------------------------------------------------------------------------------------------
if(!Pause)
{
LightPosition = rotate(LightPosition, 2.8125f * FrameTime, vec3(0.0f, 1.0f, 0.0f));
}
}
void COpenGLRenderer::Resize(int Width, int Height)
{
this->Width = Width;
this->Height = Height;
ProjectionMatrix = perspective(45.0f, (float)Width / (float)Height, 0.125f, 512.0f);
ProjectionMatrixInverse = inverse(ProjectionMatrix);
}
void COpenGLRenderer::Destroy()
{
Texture[0].Destroy();
Texture[1].Destroy();
ShadowMapping.Destroy();
glDeleteBuffers(1, &VBO);
glDeleteTextures(1, &ShadowMap);
if(GLEW_EXT_framebuffer_object)
{
glDeleteFramebuffersEXT(1, &FBO);
}
}
...
void COpenGLView::OnKeyDown(UINT Key)
{
switch(Key)
{
case VK_F1:
OpenGLRenderer.Texturing = !OpenGLRenderer.Texturing;
break;
case VK_F2:
OpenGLRenderer.ShowShadowMap = !OpenGLRenderer.ShowShadowMap;
break;
case VK_SPACE:
OpenGLRenderer.Pause = !OpenGLRenderer.Pause;
break;
}
}
void COpenGLView::OnMouseMove(int X, int Y)
{
...
OpenGLRenderer.CursorPosition = vec2((float)X, (float)(Height - 1 - Y));
}
...
shadowmapping.vs
#version 120
uniform mat4x4 ShadowMatrix;
varying vec4 ShadowMapTexCoord;
varying vec3 Normal, LightDirection;
void main()
{
vec4 Position = gl_ModelViewMatrix * gl_Vertex;
ShadowMapTexCoord = ShadowMatrix * Position;
Normal = gl_NormalMatrix * gl_Normal;
LightDirection = gl_LightSource[0].position.xyz - Position.xyz;
gl_FrontColor = gl_Color;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ProjectionMatrix * Position;
}
shadowmapping.fs
#version 120
uniform sampler2D Texture;
uniform sampler2D/*Shadow*/ ShadowMap;
uniform int Texturing;
varying vec4 ShadowMapTexCoord;
varying vec3 Normal, LightDirection;
void main()
{
float LightDistance2 = dot(LightDirection, LightDirection);
float LightDistance = sqrt(LightDistance2);
float NdotLD = max(dot(normalize(Normal), LightDirection / LightDistance), 0.0);
float Attenuation = gl_LightSource[0].constantAttenuation;
Attenuation += gl_LightSource[0].linearAttenuation * LightDistance;
Attenuation += gl_LightSource[0].quadraticAttenuation * LightDistance2;
// NdotLD *= shadow2DProj(ShadowMap, ShadowMapTexCoord).r;
if(ShadowMapTexCoord.w > 0.0)
{
vec3 ShadowMapTexCoordProj = ShadowMapTexCoord.xyz / ShadowMapTexCoord.w;
if(ShadowMapTexCoordProj.x >= 0.0 && ShadowMapTexCoordProj.x < 1.0 &&
ShadowMapTexCoordProj.y >= 0.0 && ShadowMapTexCoordProj.y < 1.0 &&
ShadowMapTexCoordProj.z >= 0.0 && ShadowMapTexCoordProj.z < 1.0)
{
if(texture2D(ShadowMap, ShadowMapTexCoordProj.xy).r <= ShadowMapTexCoordProj.z)
{
NdotLD = 0.0;
}
}
}
gl_FragColor = gl_Color;
if(Texturing == 1) gl_FragColor *= texture2D(Texture, gl_TexCoord[0].st);
gl_FragColor.rgb *= (gl_LightSource[0].ambient.rgb + gl_LightSource[0].diffuse.rgb * NdotLD) / Attenuation;
}
Shadow map
Download
|