3D C/C++ tutorials -> OpenGL 2.1 -> Fast realistic soft penumbra shadows
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:
...
protected:
CTexture Texture;
CShaderProgram PenumbraShadowMapping;
GLuint VBO, ShadowMap, FBO;
vec3 LightPosition;
mat4x4 LightViewMatrices[5], LightProjectionMatrix, LightTextureMatrices[5];
public:
bool Pause;
float Radius;
public:
...
public:
...
void RenderShadowMap();
...
};
...
opengl_21_tutorials_win32_framework.cpp
...
COpenGLRenderer::COpenGLRenderer()
{
Pause = false;
Radius = 0.05f;
Camera.SetViewMatrixPointer(&ViewMatrix);
}
COpenGLRenderer::~COpenGLRenderer()
{
}
bool COpenGLRenderer::Init()
{
// ------------------------------------------------------------------------------------------------------------------------
bool Error = false;
// check OpenGL extensions ------------------------------------------------------------------------------------------------
if(!GLEW_ARB_depth_texture)
{
ErrorLog.Append("GL_ARB_depth_texture not supported!\r\n");
Error = true;
}
if(!GLEW_EXT_texture_array)
{
ErrorLog.Append("GL_EXT_texture_array 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.LoadTexture2D("texture.jpg");
Error |= !PenumbraShadowMapping.Load("penumbra_shadow_mapping.vs", "penumbra_shadow_mapping.fs");
// if an error occurred, return false -------------------------------------------------------------------------------------
if(Error)
{
return false;
}
// get uniform locations --------------------------------------------------------------------------------------------------
PenumbraShadowMapping.UniformLocations = new GLuint[3];
PenumbraShadowMapping.UniformLocations[0] = glGetUniformLocation(PenumbraShadowMapping, "LightPosition");
PenumbraShadowMapping.UniformLocations[1] = glGetUniformLocation(PenumbraShadowMapping, "CameraPosition");
PenumbraShadowMapping.UniformLocations[2] = glGetUniformLocation(PenumbraShadowMapping, "LightTextureMatrices");
// set constant uniforms --------------------------------------------------------------------------------------------------
glUseProgram(PenumbraShadowMapping);
glUniform1i(glGetUniformLocation(PenumbraShadowMapping, "Texture"), 0);
glUniform1i(glGetUniformLocation(PenumbraShadowMapping, "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);
// generate shadow map texture --------------------------------------------------------------------------------------------
glGenTextures(1, &ShadowMap);
glBindTexture(GL_TEXTURE_2D_ARRAY, ShadowMap);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 5, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
// generate FBO -----------------------------------------------------------------------------------------------------------
glGenFramebuffersEXT(1, &FBO);
// set light position and projection matrix -------------------------------------------------------------------------------
LightPosition = vec3(0.0f, 2.5f, -5.0f);
LightProjectionMatrix = perspective(45.0f, 1.0f, 0.125f, 512.0f);
// set camera -------------------------------------------------------------------------------------------------------------
Camera.Look(vec3(0.0f, 1.25f, 5.0f), vec3(0.0f, -0.5f, 0.0f));
// ------------------------------------------------------------------------------------------------------------------------
return true;
}
void COpenGLRenderer::RenderShadowMap()
{
// calculate light matrices -----------------------------------------------------------------------------------------------
LightViewMatrices[0] = look(LightPosition, vec3(0.0f), vec3(0.0f, 1.0f, 0.0f));
LightTextureMatrices[0] = BiasMatrix * LightProjectionMatrix * LightViewMatrices[0];
vec3 lp = vec3(0.0f);
lp += vec3(LightViewMatrices[0][0], LightViewMatrices[0][4], LightViewMatrices[0][8]);
lp += vec3(LightViewMatrices[0][1], LightViewMatrices[0][5], LightViewMatrices[0][9]);
lp = normalize(lp);
lp *= Radius;
lp += LightPosition;
for(int i = 1; i < 5; i++)
{
LightViewMatrices[i] = look(lp, vec3(0.0f), vec3(0.0f, 1.0f, 0.0f));
LightTextureMatrices[i] = BiasMatrix * LightProjectionMatrix * LightViewMatrices[i];
lp = rotate(lp, 90.0f, vec3(LightViewMatrices[0][2], LightViewMatrices[0][6], LightViewMatrices[0][10]));
}
// render shadow map ------------------------------------------------------------------------------------------------------
glViewport(0, 0, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE);
for(int i = 0; i < 5; i++)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
glDrawBuffers(0, NULL); glReadBuffer(GL_NONE);
glFramebufferTextureLayerEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, ShadowMap, 0, i);
glClear(GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&LightProjectionMatrix);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&LightViewMatrices[i]);
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);
glCullFace(GL_BACK);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
}
void COpenGLRenderer::Render(float FrameTime)
{
// render shadow map ------------------------------------------------------------------------------------------------------
if(!Pause)
{
RenderShadowMap();
}
// render scene -----------------------------------------------------------------------------------------------------------
glViewport(0, 0, Width, Height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&ProjectionMatrix);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&ViewMatrix);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, Texture);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D_ARRAY, ShadowMap);
glUseProgram(PenumbraShadowMapping);
glUniform3fv(PenumbraShadowMapping.UniformLocations[0], 1, &LightPosition);
glUniform3fv(PenumbraShadowMapping.UniformLocations[1], 1, &Camera.Position);
glUniformMatrix4fv(PenumbraShadowMapping.UniformLocations[2], 5, GL_FALSE, (GLfloat*)LightTextureMatrices);
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);
glDrawArrays(GL_QUADS, 0, 52);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
// move light -------------------------------------------------------------------------------------------------------------
if(!Pause)
{
LightPosition = rotate(LightPosition, 2.8125f * FrameTime, vec3(0.0f, 1.0f, 0.0f));
}
// ------------------------------------------------------------------------------------------------------------------------
Text.Set("Radius: %.03f", Radius);
}
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);
}
void COpenGLRenderer::Destroy()
{
Texture.Destroy();
PenumbraShadowMapping.Destroy();
glDeleteBuffers(1, &VBO);
glDeleteTextures(1, &ShadowMap);
if(GLEW_EXT_framebuffer_object)
{
glDeleteFramebuffersEXT(1, &FBO);
}
}
...
void COpenGLView::OnKeyDown(UINT Key)
{
switch(Key)
{
case VK_SPACE:
OpenGLRenderer.Pause = !OpenGLRenderer.Pause;
break;
case VK_ADD:
OpenGLRenderer.Radius += 0.001f;
if(OpenGLRenderer.Pause) OpenGLRenderer.RenderShadowMap();
break;
case VK_SUBTRACT:
OpenGLRenderer.Radius -= 0.001f;
if(OpenGLRenderer.Radius < 0.0f) OpenGLRenderer.Radius = 0.0f;
if(OpenGLRenderer.Pause) OpenGLRenderer.RenderShadowMap();
break;
}
}
...
penumbra_shadow_mapping.vs
#version 120
uniform vec3 LightPosition, CameraPosition;
uniform mat4x4 LightTextureMatrices[5];
varying vec3 LightDirection, LightDirectionReflected, CameraDirection, Normal;
varying vec4 ShadowTexCoord[5];
void main()
{
LightDirection = LightPosition - gl_Vertex.xyz;
LightDirectionReflected = reflect(-LightDirection, gl_Normal);
CameraDirection = CameraPosition - gl_Vertex.xyz;
Normal = gl_Normal;
gl_TexCoord[0] = gl_MultiTexCoord0;
for(int i = 0; i < 5; i++) ShadowTexCoord[i] = LightTextureMatrices[i] * gl_Vertex;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
penumbra_shadow_mapping.fs
#version 120
#extension GL_EXT_texture_array : enable
uniform sampler2D Texture;
uniform sampler2DArrayShadow ShadowMap;
varying vec3 LightDirection, LightDirectionReflected, CameraDirection, Normal;
varying vec4 ShadowTexCoord[5];
void main()
{
float Shadow = 0.0;
for(int i = 0; i < 5; i++)
{
Shadow += shadow2DArray(ShadowMap, vec4(ShadowTexCoord[i].xy / ShadowTexCoord[i].w, i, ShadowTexCoord[i].z / ShadowTexCoord[i].w)).r;
}
Shadow /= 5.0;
float NdotLD = max(dot(normalize(LightDirection), Normal), 0.0) * Shadow;
float Spec = pow(max(dot(normalize(LightDirectionReflected), normalize(CameraDirection)), 0.0), 32.0) * Shadow;
gl_FragColor = vec4(texture2D(Texture, gl_TexCoord[0].st).rgb * (0.25 + NdotLD * 0.75 + Spec), 1.0);
}
Download
|