3D C/C++ tutorials - OpenGL 2.1 - GLSL shadow cube mapping
3D C/C++ tutorials -> OpenGL 2.1 -> GLSL shadow cube 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.
To compile and run these tutorials some or all of these libraries are required: FreeImage 3.16.0, GLEW 1.11.0, GLUT 3.7.6 / GLUT for Dev-C++, GLM 0.9.5.4
opengl_tutorials_win32_framework.h
...

#define SHADOW_CUBE_MAP_SIZE 512

class COpenGLRenderer
{
protected:
    int Width, Height;
    mat4x4 Model, View, Projection, ProjectionBiasInverse;
    
    int ObjectsCount, LightObjectID;
    CObject *Objects;

    CShaderProgram ShadowCubeMapping;

    GLuint ShadowCubeMap, FBO;
    mat4x4 LightView[6], LightProjection, LightTexture[6];

    int SelectedObject;
    float PlaneD;
    vec3 SelectedPoint, PlaneNormal;

public:
    COpenGLRenderer();
    ~COpenGLRenderer();

    bool Init();
    void Render(float FrameTime);
    void Resize(int Width, int Height);
    void Destroy();

    void MoveSelectedObject(int x, int y);
    void SelectObject(int x, int y);

protected:
    void RenderShadowCubeMap();
};

...
opengl_tutorials_win32_framework.cpp
...

COpenGLRenderer::COpenGLRenderer()
{
    SelectedObject = -1;

    Camera.SetViewMatrixPointer(&View);
}

COpenGLRenderer::~COpenGLRenderer()
{
}

bool COpenGLRenderer::Init()
{
    if(gl_version < 21)
    {
        ErrorLog.Set("OpenGL 2.1 not supported!");
        return false;
    }

    bool Error = false;

    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_ARB_framebuffer_object)
    {
        ErrorLog.Append("GL_ARB_framebuffer_object not supported!\r\n");
        Error = true;
    }

    ObjectsCount = 7;

    Objects = new CObject[ObjectsCount];

    Error |= !Objects[0].Load("Models\\", "room.obj");
    Error |= !Objects[1].Load("Models\\Thor\\", "thor.obj");
    Error |= !Objects[2].Load("Models\\Spongebob\\", "spongebob_bind.obj");
    Error |= !Objects[3].Load("Models\\Alien2\\", "alien2.obj");
    Error |= !Objects[4].Load("Models\\Teapot\\", "teapot.obj");

    Error |= !Objects[5].Texture.LoadTexture2D("earthmap.jpg");
    
    Error |= !ShadowCubeMapping.Load("shadow_cube_mapping.vs", "shadow_cube_mapping.fs");

    if(Error)
    {
        return false;
    }

    Objects[0].Movable = false;

    Objects[1].Rotate(-90.0f, vec3(0.0f, 1.0f, 0.0f));
    Objects[1].Scale(1.75f / (Objects[1].Max.y - Objects[1].Min.y));
    Objects[1].Translate(vec3(-(Objects[1].Min.x + Objects[1].Max.x) / 2.0f, -Objects[1].Min.y, -(Objects[1].Min.z + Objects[1].Max.z) / 2.0f));
    Objects[1].Position = vec3(1.0f, 0.0f, -1.0f);

    Objects[2].Scale(0.875f / (Objects[2].Max.y - Objects[2].Min.y));
    Objects[2].Translate(vec3(-(Objects[2].Min.x + Objects[2].Max.x) / 2.0f, -Objects[2].Min.y, -(Objects[2].Min.z + Objects[2].Max.z) / 2.0f));
    Objects[2].Position = vec3(-1.0f, 0.0f, -1.0f);

    Objects[3].Scale(1.0f / (Objects[3].Max.y - Objects[3].Min.y));
    Objects[3].Translate(vec3(-(Objects[3].Min.x + Objects[3].Max.x) / 2.0f, -Objects[3].Min.y, -(Objects[3].Min.z + Objects[3].Max.z) / 2.0f));
    Objects[3].Position = vec3(0.0f, 0.0f, -2.5f);

    Objects[4].Color = vec3(1.0f, 0.5f, 0.0f);
    Objects[4].CullFace = false;
    Objects[4].FrontFace = GL_CW;
    Objects[4].Scale(0.25f / (Objects[4].Max.y - Objects[4].Min.y));
    Objects[4].Translate(vec3(-(Objects[4].Min.x + Objects[4].Max.x) / 2.0f, -Objects[4].Min.y, -(Objects[4].Min.z + Objects[4].Max.z) / 2.0f));
    Objects[4].Position = vec3(0.0f, 0.0f, 0.5f);

    Objects[5].CreateSphere(0.5f, 32);
    Objects[5].Position = vec3(1.0f, 0.5f, 1.0f);

    Objects[6].CreateSphere(0.03125f, 16, true);
    Objects[6].Position = vec3(0.0f, 1.0f, 0.0f);

    LightObjectID = 6;

    for(int i = 0; i < ObjectsCount; i++)
    {
        Objects[i].InitVertexBuffers();
    }

    ShadowCubeMapping.UniformLocations = new GLuint[3];
    ShadowCubeMapping.UniformLocations[0] = glGetUniformLocation(ShadowCubeMapping, "Model");
    ShadowCubeMapping.UniformLocations[1] = glGetUniformLocation(ShadowCubeMapping, "Texturing");
    ShadowCubeMapping.UniformLocations[2] = glGetUniformLocation(ShadowCubeMapping, "LightTexture");

    glUseProgram(ShadowCubeMapping);
    glUniform1i(glGetUniformLocation(ShadowCubeMapping, "Texture"), 0);
    glUniform1i(glGetUniformLocation(ShadowCubeMapping, "ShadowCubeMap"), 1);
    glUseProgram(0);

    glGenTextures(1, &ShadowCubeMap);
    glBindTexture(GL_TEXTURE_2D_ARRAY, ShadowCubeMap);
    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_COMPONENT32, SHADOW_CUBE_MAP_SIZE, SHADOW_CUBE_MAP_SIZE, 6, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    glBindTexture(GL_TEXTURE_2D_ARRAY, 0);

    glGenFramebuffers(1, &FBO);
    glBindFramebuffer(GL_FRAMEBUFFER, FBO);
    glDrawBuffers(0, NULL);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    LightProjection = PerspectiveProjectionMatrix(90.0f, SHADOW_CUBE_MAP_SIZE, SHADOW_CUBE_MAP_SIZE, 0.125f, 512.0f);

    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);

    Camera.LookAt(vec3(0.0f, 0.875f, 0.0f), vec3(0.0f, 0.875f, 2.5f), true);

    RenderShadowCubeMap();

    return true;
}

void COpenGLRenderer::Render(float FrameTime)
{
    glViewport(0, 0, Width, Height);

    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(&Projection);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glLightfv(GL_LIGHT0, GL_POSITION, &vec4(Objects[LightObjectID].Position, 1.0f));

    glLoadMatrixf(&View);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnable(GL_DEPTH_TEST);

    glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D_ARRAY, ShadowCubeMap);

    glActiveTexture(GL_TEXTURE0);

    glUseProgram(ShadowCubeMapping);

    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);

    for(int i = 0; i < ObjectsCount; i++)
    {
        if(Objects[i].TrianglesCount <= 0) continue;

        Model = TranslationMatrix(Objects[i].Position.x, Objects[i].Position.y, Objects[i].Position.z);
        glUniformMatrix4fv(ShadowCubeMapping.UniformLocations[0], 1, GL_FALSE, &Model);

        if(Objects[i].CullFace)
        {
            glEnable(GL_CULL_FACE);
        }

        glColor3fv(&Objects[i].Color);

        if(Objects[i].Texture)
        {
            glBindTexture(GL_TEXTURE_2D, Objects[i].Texture);
        }

        glUniform1i(ShadowCubeMapping.UniformLocations[1], Objects[i].Texture ? 1 : 0);

        glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO[0]);
        glTexCoordPointer(2, GL_FLOAT, 0, NULL);

        glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO[1]);
        glNormalPointer(GL_FLOAT, 0, NULL);

        glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO[2]);
        glVertexPointer(3, GL_FLOAT, 0, NULL);

        glFrontFace(Objects[i].FrontFace);

        glDrawArrays(GL_TRIANGLES, 0, Objects[i].TrianglesCount * 3);

        if(Objects[i].Texture)
        {
            glBindTexture(GL_TEXTURE_2D, 0);
        }

        if(Objects[i].CullFace)
        {
            glDisable(GL_CULL_FACE);
        }
    }
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    glUseProgram(0);

    glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
    
    glActiveTexture(GL_TEXTURE0);

    glDisable(GL_DEPTH_TEST);
}

void COpenGLRenderer::Resize(int Width, int Height)
{
    this->Width = Width;
    this->Height = Height;

    Projection = PerspectiveProjectionMatrix(45.0f, (float)Width, (float)Height, 0.125f, 512.0f);
    ProjectionBiasInverse = PerspectiveProjectionMatrixInverse(Projection) * BiasMatrixInverse();
}

void COpenGLRenderer::Destroy()
{
    for(int i = 0; i < ObjectsCount; i++)
    {
        Objects[i].Destroy();
    }

    delete [] Objects;

    if(gl_version >= 21)
    {
        ShadowCubeMapping.Delete();
    }

    glDeleteTextures(1, &ShadowCubeMap);

    if(GLEW_ARB_framebuffer_object)
    {
        glDeleteFramebuffers(1, &FBO);
    }
}

...

void COpenGLRenderer::MoveSelectedObject(int x, int y)
{
    ...

    if(NdotR != 0.0f)
    {
        ...

        RenderShadowCubeMap();
    }
}

...

void COpenGLRenderer::RenderShadowCubeMap()
{
    // calculate light matrices -----------------------------------------------------------------------------------------------

    LightView[0] = ViewMatrix(vec3( 0.0f, 0.0f, 1.0f), vec3(0.0f, 1.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f), Objects[LightObjectID].Position);
    LightView[1] = ViewMatrix(vec3( 0.0f, 0.0f,-1.0f), vec3(0.0f, 1.0f, 0.0f), vec3( 1.0f, 0.0f, 0.0f), Objects[LightObjectID].Position);
    LightView[2] = ViewMatrix(vec3( 1.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 1.0f), vec3( 0.0f,-1.0f, 0.0f), Objects[LightObjectID].Position);
    LightView[3] = ViewMatrix(vec3( 1.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f,-1.0f), vec3( 0.0f, 1.0f, 0.0f), Objects[LightObjectID].Position);
    LightView[4] = ViewMatrix(vec3(-1.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f), vec3( 0.0f, 0.0f,-1.0f), Objects[LightObjectID].Position);
    LightView[5] = ViewMatrix(vec3( 1.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f), vec3( 0.0f, 0.0f, 1.0f), Objects[LightObjectID].Position);

    LightTexture[0] = BiasMatrix() * LightProjection * LightView[0];
    LightTexture[1] = BiasMatrix() * LightProjection * LightView[1];
    LightTexture[2] = BiasMatrix() * LightProjection * LightView[2];
    LightTexture[3] = BiasMatrix() * LightProjection * LightView[3];
    LightTexture[4] = BiasMatrix() * LightProjection * LightView[4];
    LightTexture[5] = BiasMatrix() * LightProjection * LightView[5];

    glUseProgram(ShadowCubeMapping);
    glUniformMatrix4fv(ShadowCubeMapping.UniformLocations[2], 6, GL_FALSE, (GLfloat*)LightTexture);
    glUseProgram(0);
    
    // render shadow cube map -------------------------------------------------------------------------------------------------

    glViewport(0, 0, SHADOW_CUBE_MAP_SIZE, SHADOW_CUBE_MAP_SIZE);

    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(&LightProjection);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    glCullFace(GL_FRONT);

    glBindFramebuffer(GL_FRAMEBUFFER, FBO);

    glEnableClientState(GL_VERTEX_ARRAY);

    for(int i = 0; i < 6; i++)
    {
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, ShadowCubeMap, 0, i);

        glClear(GL_DEPTH_BUFFER_BIT);

        for(int ii = 0; ii < ObjectsCount; ii++)
        {
            if(Objects[ii].TrianglesCount <= 0) continue;

            glMatrixMode(GL_MODELVIEW);
            glLoadMatrixf(&LightView[i]);
            glTranslatef(Objects[ii].Position.x, Objects[ii].Position.y, Objects[ii].Position.z);

            glBindBuffer(GL_ARRAY_BUFFER, Objects[ii].VBO[2]);
            glVertexPointer(3, GL_FLOAT, 0, NULL);

            glFrontFace(Objects[ii].FrontFace);

            glDrawArrays(GL_TRIANGLES, 0, Objects[ii].TrianglesCount * 3);
        }
    }
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glDisableClientState(GL_VERTEX_ARRAY);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    glCullFace(GL_BACK);

    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);
}

...
shadow_cube_mapping.vs
#version 120

uniform mat4x4 Model;

varying vec3 Position, Normal;

void main()
{
    Position = (Model * gl_Vertex).xyz;
    Normal = gl_Normal;
    gl_FrontColor = gl_Color;
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = gl_ModelViewProjectionMatrix * vec4(Position, 1.0);
}
shadow_cube_mapping.fs
#version 120

#extension GL_EXT_texture_array : enable

uniform sampler2D Texture;
uniform sampler2DArrayShadow ShadowCubeMap;

uniform int Texturing;
uniform mat4x4 LightTexture[6];

varying vec3 Position, Normal;

void main()
{
    vec3 LightDirection = gl_LightSource[0].position.xyz - Position;
    float LightDistance2 = dot(LightDirection, LightDirection);
    float LightDistance = sqrt(LightDistance2);
    LightDirection /= LightDistance;
    float Attenuation = gl_LightSource[0].constantAttenuation;
    Attenuation += gl_LightSource[0].linearAttenuation * LightDistance;
    Attenuation += gl_LightSource[0].quadraticAttenuation * LightDistance2;
    
    float Axis[6];

    Axis[0] = -LightDirection.x;
    Axis[1] = LightDirection.x;
    Axis[2] = -LightDirection.y;
    Axis[3] = LightDirection.y;
    Axis[4] = -LightDirection.z;
    Axis[5] = LightDirection.z;
    
    int MaxAxisID = 0;
    
    for(int i = 1; i < 6; i++)
    {
        if(Axis[i] > Axis[MaxAxisID])
        {
            MaxAxisID = i;
        }
    }
    
    vec4 ShadowTexCoord = LightTexture[MaxAxisID] * vec4(Position, 1.0);
    ShadowTexCoord.xyz /= ShadowTexCoord.w;
    
    ShadowTexCoord.w = ShadowTexCoord.z;
    ShadowTexCoord.z = float(MaxAxisID);
    
    float Shadow = shadow2DArray(ShadowCubeMap, ShadowTexCoord).r;
    float NdotLD = max(dot(normalize(Normal), LightDirection), 0.0) * Shadow;
    vec3 Light = (gl_LightSource[0].ambient.rgb + gl_LightSource[0].diffuse.rgb * NdotLD) / Attenuation;
    
    gl_FragColor.rgb = gl_Color.rgb;
    if(Texturing == 1) gl_FragColor.rgb *= texture2D(Texture, gl_TexCoord[0].st).rgb;
    gl_FragColor.rgb *= Light;
}
Download
glsl_shadow_cube_mapping.zip (Visual Studio 2005 Professional)

© 2010 - 2016 Bc. Michal Belanec, michalbelanec (at) centrum (dot) sk
Last update June 25, 2016
OpenGL® is a registered trademark of Silicon Graphics Inc.