GLSL - cube shadows - projecting

Before i ask, i would like to apologize for starting here with a question.

The thing is that i couldn’t find answer to my problem anywhere, and decided to post it here finally, to speak to gurus.

Here is what i am trying to achieve:

Omni directional shadow, by using FBO and GL_TEXTURE_CUBE_MAP with GL_DEPTH_ATTACHMENT.

So far, all works fine - render to all 6 faces of the cube map within FBO looks promising, the only thing left is to project that within the shader.

Now, to visualize what exactly is happening:

  • here is the screen shot from the level editor, you can see a single omni light there that is the shadow-caster.

  • here is the scene rendered with light (without shadow)

  • here is the gdebugger cube map dump

With all that, i am passing the cube map to the fragment shader using:

uniform samplerCubeShadow shadowMap;

I sample it in fragment with:

float SHADOW=shadowCube( shadowMap, normalize(lightToSurface) );
gl_FragColor=light(0)*SHADOW;

where

lightToSurface = ((gl_ModelViewMatrix * gl_Vertex).xyz-gl_LightSource[0].position.xyz) / gl_LightSource[0].spotExponent;

spotExponent - is the radius, i pass the Omni light radius with that to the shader.

This produces :

The question is:
How to project the shadow by using samplerCubeShadow in GLSL ?

Many thanks for any help with this, I’ve spent days trying to figure that out, no success… What’s interesting is that even google does not pop up any example … is this samplerCubeShadow something that is rarely used, or not used at all?

float SHADOW=shadowCube( shadowMap, normalize(lightToSurface) );

First of all, where does “shadowCube” come from? It’s not part of the unextended standard GLSL (any version, even pre-1.30). So what extension provides it? Have you activated that extension?

Second, what values do you expect “shadowCube” to return? If it’s a float on the [0, 1] range that is proportional to the number of samples that pass the depth comparison test, then you seem to have forgotten a key ingredient:

The depth comparison reference value.

I can’t say how “shadowCube” works, but in 1.30 and above, “texture” when given a “samplerCubeShadow” sampler, takes a vec4, not a vec3. The fourth component is the reference value for the depth comparison. That is, the depth of this fragment, relative to the light source in question.

Alfonse Reinheart,
you are totally right, the ‘shadowCube’ was something i was told when i was speaking to few guys around.

And seriously I’ll try hard to go back with the histories and find out who and how was trying to use it.

update found it,

http://www.opengl.org/registry/specs/EXT/gpu_shader4.txt

OK, let’s switch to ‘texture’ when using samplerCubeShadow.
How should that be used then, can you help me out please?

I have totally lost the plot now, what should go in vec4 for texture and what is returned back ?

Thank you v.much.

Yes, I believe that’s the name of the texture access function in GLSL 1.2 and earlier (more on that below). samplerCubeShadow is the name of the sampler type.

OK, let’s switch to ‘texture’ when using samplerCubeShadow.
How should that be used then, can you help me out please?

How to project the shadow by using samplerCubeShadow in GLSL ?

I have totally lost the plot now, what should go in vec4 for texture and what is returned back ?

As EXT_gpu_shader4 indicates, coord.stp (i.e. coord.xyz) is the direction vector (from the point light to the fragment you are trying to evaluate shadows for). And coord.q is the R value (i.e. depth value to compare against).

Typically with hardware shadow lookups like this, the R value is the lights’ window-space depth value (0…1) associated with the fragment you are computing shadows for. However, I’m a little unsure about with cube maps because there are 6 distinct light frusta/projections. I’ll look around a bit. My guess is you use the direction vector (coord.stp) to figure out which face of the cubemap you’re on and use its camera-eye-to-light transform to back-project into the right light space yielding coord.q (the depth value of the fragment in the light’s window-space).

When you do a shadow lookup, the hardware does the depth compares and returns back to you a “visibility” term in 0…1, where 0 = no visibility (i.e. totally in shadow) and 1 = 100% visibility (i.e. not in shadow at all).

If you set NEAREST filtering on the depth texture you bind to the shadow sampler, then you’ll always get only 0 or 1. However if you enable LINEAR filtering, then this enables PCF shadow lookups, which means the hardware may do multiple shadow lookups, compares on each, and blend the results, giving you 0, 1, or some number in between. With LUMINANCE DEPTH_TEXTURE_MODE, this value is stuffed in .r, .g, and .b of the return value, so you can grab it from any of these.

By the way, this presumes you’ve got your texture attributes set up as follows (typical for shadow map lookups):


glTexParameteri( target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
glTexParameteri( target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
glTexParameteri( target, GL_DEPTH_TEXTURE_MODE  , GL_LUMINANCE );
glTexParameteri( target, GL_TEXTURE_MIN_FILTER  , GL_LINEAR );
glTexParameteri( target, GL_TEXTURE_MAG_FILTER  , GL_LINEAR );

Also note that pre-GLSL 1.3, your texture access function should be shadowCube (or shadowCubeProj – more likely with point light source shadows which are perspective projection). Post-GLSL 1.3 it’d be texture (or textureProj) – in GLSL 1.3 they stopped having different names for each type of sampler and just overloaded the texture access function names.

However in both cases, AFAIK, the sampler type you should use is samplerCubeShadow (though it’s unclear whether GLSL 1.2 supported this). The shadow on the end of the name requires: 1) that you bind a depth texture to it, 2) depth compare (GL_TEXTURE_COMPARE_MODE) must be enabled on the texture.

That said, I haven’t personally coded depth cubemaps yet so there may be some errors here.

Cube + Proj is not valid. Projection by q doesn’t work for cubemaps.

Cube translates an .str vector to a face + .st offset; for non-shadow lookups q would be the length of the vector which is meaningless. For shadow, q is actually the reference value.

SamplerCubeShadow is not supported in GLSL 1.20 unless you enable GL_EXT_gpu_shader4.

Dark Photon:

Thank you for a great introduction into this, of course my texture is set up as follows, so it meets the requirements.

glGenTextures(1, &tDepthCubeMap);
glBindTexture(GL_TEXTURE_CUBE_MAP, tDepthCubeMap);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_DEPTH_TEXTURE_MODE , GL_LUMINANCE );
glTexParameterf( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
glTexParameterf( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

for (int face = 0; face < 6; face++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_DEPTH_COMPONENT32F , 512, 512, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

My guess is you use the direction vector (coord.stp) to figure out which face of the cubemap you’re on and use its camera-eye-to-light transform to back-project into the right light space yielding coord.q (the depth value of the fragment in the light’s window-space).

That would mean, something like :

uniform samplerCubeShadow shadowMap;

float visibility=texture( shadowMap, lightToSurface );

where


lightToSurface=vec4(gl_LightSource[0].position-(gl_ModelViewMatrix * gl_Vertex));

That lightToSurface when passed to texture(…) should lookup the correct face of the cube, what is exactly that vec4’s 4th component (.q) - how do i calculate it ?

Thanks to all of you guys, hopefully we can get that sorted out…

update

I made a small test here,
this is the actual light setup:

The depth map

And the shader’s are as follows:

[vertex shader]

//made it vec3 - frag recreates it as vec4
varying vec3 lightToSurface;
vec3 vertexPos = vec3(gl_ModelViewMatrix * gl_Vertex);
lightToSurface = (vertexPos-gl_LightSource[0].position) / gl_LightSource[0].spotExponent;

[fragment shader]

vec4 lts=vec4(lightToSurface.x,lightToSurface.y,lightToSurface.z,1);
float cDepth=texture( shadowMap, normalize(lts));
gl_FragColor=light(0) * cDepth;

‘1’ in the vector.w results:

vec4 lts=vec4(lightToSurface.x,lightToSurface.y,lightToSurface.z,3);
float cDepth=texture( shadowMap, normalize(lts));
gl_FragColor=light(0) * cDepth;

‘3’ results

Of course what is strange too is that the ‘shadow’ or whatever it is there ‘rotates’ to camera view.

Good point. I wasn’t thinking. With shadowCubeProj/textureProj on a samplerCubeShadow, we’d be out of vec4 texcoord components to play with, so there’s no room for .xyzw and the reference value (5 values).

Also, googling around looks like most folks just use samplerCube and textureCube/texture (i.e. straight texture lookup; no hardware depth comparison) to sample cube depth maps for point light shadows for some reason. They don’t get the free PCF doing it that way, so I’m not sure why, yet.

Almost there,

The problem i have now is related to view matrix,
when my camera is moved, shadows are moving along with camera view vector.

Anyways,

here’s how shaders look now:

[vertex]

varying vec3 LightDirection;
varying float distance;

//Calculate light direction for fragment to lookup correct face
//works
vec3 ecPos = gl_ModelViewMatrix * gl_Vertex;
vec3 aux = vec3(gl_LightSource[0].position-ecPos);
LightDirection = aux;
float far=gl_LightSource[0].spotExponent;
float near=1;

//Calculate far/near planes
//works
distance = max(max(abs(LightDirection.x),abs(LightDirection.y)),abs(LightDirection.z));
distance = ((far+near)/(far-near)) + (1.0/distance)((-2.0far*near)/(far-near));
distance = (distance+1.0)/2.0;

gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

[/vertex]

[fragment]

varying vec3 LightDirection;
varying float distance;

void main()
{
float cDepth=texture( shadowMap, ( vec4(LightDirection.x,LightDirection.y,LightDirection.z,distance) ) );
gl_FragColor= light(0) * cDepth;
}

[/fragment]

<object width=“425” height=“350”> <param name=“movie” value=“http://www.youtube.com/v/Y-Ii3SjhBeA”></param> <param name=“wmode” value=“transparent”></param> <embed src=“http://www.youtube.com/v/Y-Ii3SjhBeA” type=“application/x-shockwave-flash” wmode=“transparent” width=“425” height=“350”> </embed></object>

I’ve seen guys doing that on a simple samplerCube, they pack the depth values when rendering into a color cube map instead of using depth maps from what i’ve seen. But that sounds like a ‘backdoor’ - why? If we have a samplerCubeShadow here…?

If i could fix the matrix… i think that would be all…

Reading some more on point light source shadows with cube depth maps, it’s become apparent to me there are some subtleties I’m not intimately familiar with. I’d suggest you read these links:

the first one entirely, and the last two where they specifically talk about “cube” maps. Apparently there are several options here.

Seems radial distance is often stored in the cube map rather than cube face eye-space Z distance.

However, the way that occurred to me first is the most like directional light source shadow mapping (2D texture vs. cube texture). That is:

  1. [li] having 6 separate camera-eye-space-to-light-clip-space transform matrices (see below for a diagram), [] you choose the right one based the fragment-to-eye-vector transformed back into the light’s frame of reference, and [] you transform your fragment position by that selected transform in turn to give you the light-space position in the frame of reference of that face and thus the depth value (i.e. reference value, i.e. coord.q) to pass to the shadowCube/texture call.

This is I think what the STALKER link calls treating an omni light as 6 spotlights.

By the way, in your current code, that you’re seeing the shadows rotate with your camera makes sense because you’re using a vector in the camera’s EYE-SPACE to lookup into the shadow map. You need to be using a vector that is relative to the frame of reference of the “light”, not the “camera”. That’ll get you the right direction vector for the lookup. Then the only trick is getting the right depth value for the comparison. And that’s apparently going to depend on which alg you use for generating your depth cubemaps. See the links above.

this is getting v.heavy…

Thanks photon, I’ll try to implement what you’ve told,
will come back with results… if i manage to finish this.

Right, i knew it’s gonna be a nightmare to implement that,
anyone has any example in regards to what Dark Photon said?

I understand i can’t do anything with what i already have as i need to have 6 separate matrices for each face of the cube calculated and passed to the shader right?

Any idea how to calculate them per face attached to FBO ?

Ok managed to calculate light matrices per face,
this is how they look per face:

[268447156] FACE:0
-1.000000 0.000000 0.000000 0.000000
0.000000 1.000000 0.000000 0.000000
0.000000 0.000000 -1.000000 0.000000
0.000000 -88.000000 0.000000 1.000000
[268447156] FACE:1
1.000000 0.000000 0.000000 0.000000
0.000000 1.000000 0.000000 0.000000
0.000000 0.000000 1.000000 0.000000
0.000000 -88.000000 0.000000 1.000000
[268447156] FACE:2
0.000000 1.000000 0.000000 0.000000
0.000000 0.000000 1.000000 0.000000
1.000000 0.000000 0.000000 0.000000
0.000000 0.000000 -88.000000 1.000000
[268447156] FACE:3
0.000000 -1.000000 0.000000 0.000000
0.000000 0.000000 -1.000000 0.000000
1.000000 0.000000 0.000000 0.000000
0.000000 0.000000 88.000000 1.000000
[268447156] FACE:4
0.000000 0.000000 -1.000000 0.000000
0.000000 1.000000 0.000000 0.000000
1.000000 0.000000 0.000000 0.000000
0.000000 -88.000000 0.000000 1.000000
[268447156] FACE:5
0.000000 0.000000 1.000000 0.000000
0.000000 1.000000 0.000000 0.000000
-1.000000 0.000000 0.000000 0.000000
0.000000 -88.000000 0.000000 1.000000

Photon,

having 6 separate camera-eye-space-to-light-clip-space transform matrices (see below for a diagram),
you choose the right one based the fragment-to-eye-vector transformed back into the light’s frame of reference, and

This is messy, can you please explain what do i do with those 6 matrices and which should i choose and how do i know exactly which is the correct one?

Well, I’m not suggesting you take this approach over the others. I’m suggesting that you get intimately familiar with the pros and cons of each and take the approach that works best for your requirements (I’m certainly not that intimate with the pros/cons of various approaches myself – I haven’t implemented point light shadows with cubemaps nor read the literature with that level of detail in mind …yet).

However, if you were to take this approach:

  1. Compute unit vector from light to fragment in eye space,
  2. Project onto light’s (cube-map’s) basis vectors,
  3. The maximum component of this vector in the light’s FoR is the cube face to select,
  4. Use this face index to select the eye-to-clip transform for that face,
  5. Transform fragment’s eye-space position by this transform
  6. Do shadow map lookup for that face.

Probably could be optimized some. But as the references state, there are pros and cons for doing this. Note that with this you don’t strictly need a cube map at all. You can use 6 2D depth maps, one per face, which apparently opens up some filtering options (and allegedly GPU depth comparisons, though I don’t quite appreciate why yet; I need to read up on this).

The cons are of course that you’re managing the projections for each of the faces separately. If instead you store radial distance in the cube map, you don’t need to do that. Just compute a direction vector, do a depth lookup, and compare to fragment distance. …or at least that’s the jist I got from spot-skimming. I’ll read about this in more detail soon.

I think you can calculate the face and depth value ® to test against the depth cube without having to store an array of values for each cube face.

I will assume that the matrices you used to render each face are as follows:


  face0: mat_light_projection * mat_face[0] * mat_light;
  ...
  face5: mat_light_projection * mat_face[5] * mat_light;

Where:
mat_light is matrix to transform world space coordinates into light space.
mat_light_projection is a perspective projection matrix with a fov of 90 degrees.
mat_face[n] are the matrices to transform light space coordinates to each face which I will refer to as face space.

Given a camera space position, cs_position, we can convert it to light space as follows:

ls_position = mat_light * mat_camera_inv * cs_position

In light space, the light’s position is at (0, 0, 0). So ls_position is also the unnormalized direction vector we can use to index the cube map.

Cube map face selection is done by taking the component with the largest magnitude. If we were to do that with ls_position and transform it by the selected mat_face[n] you will see that the Z value of the new position is the same as the component that was used to select this face, but always negative. This is very useful because the value in the depth buffer is only dependent on Z and not X or Y.

This allows us to construct a vector in ‘face space’ that should yield the same depth value as ls_position would have once we project it. Since the same projection matrix is used for all faces it really doesn’t matter which face the position belongs to.

Below is some pseudo-GLSL that should get you started. The calculation of depth from Z can be simplified a lot but left as it is for clarity.



## inputs

// camera space position
vec4 cs_position;

// camera space to world space transform
mat4x4 mat_camera_inv;

// world space to light space transform
mat4x4 mat_light;

// projection matrix you used to render the cube map faces
mat4x4 mat_light_projection;

// depth cube map
uniform samplerCubeShadow shadow;

## code

// transform the vertex from camera to light space
vec4 ls_position = mat_light * mat_camera_inv * cs_position;

// calculate our 'face space' Z
vec4 abs_position = abs(ls_position);
float fs_z = -max(abs_position.x, max(abs_position.y, abs_position.z));

// calculate depth from Z
vec4 clip = mat_light_projection * vec4(0.0, 0.0, fs_z, 1.0);
float depth = (clip.z / clip.w) * 0.5 + 0.5;

vec4 result = shadowCube(shadow, vec4(ls_position.xyz, depth));


deadc0de - thanks for sharing,

just for ‘clarity’ - did you manage to have cube shadow mapping done at all? or i am trying to get something running that would never work…

i am getting down to study your pseudocode now…

Yes. It works. Below is a standalone GLUT program that will do depth cube map shadows as I explained above.

<div class=“ubbcode-block”><div class=“ubbcode-header”>Click to reveal… <input type=“button” class=“form-button” value=“Show me!” onclick=“toggle_spoiler(this, ‘Yikes, my eyes!’, ‘Show me!’)” />]<div style=“display: none;”>

// Example Depth Cube Map Shadowing

#include <glut/glut.h>
#include <math.h>
#include <stdlib.h>


#define WINDOW_SIZE     (512)
#define WINDOW_NEAR     (1.0)
#define WINDOW_FAR      (100.0)

#define SHADOW_SIZE     (256)
#define SHADOW_NEAR     (1.0)
#define SHADOW_FAR      (10.0)


static float camera_position[3] = { -8.0f, 8.0f, -8.0f };
static float camera_view_matrix[16];
static float camera_view_matrix_inv[16];
static float camera_projection_matrix[16];

static float light_distance = 5.0f;
static float light_inclination = 45.0f * (M_PI / 180.0f);
static float light_azimuth = 0.0f;

static float light_position_ws[3];
static float light_position_cs[3];
static float light_view_matrix[16];
static float light_face_matrix[6][16];
static float light_projection_matrix[16];

static GLuint tex_depth_cube;

static GLuint program_shadow;
static GLuint shader_shadow_vs;

static GLuint program_render;
static GLuint shader_render_vs;
static GLuint shader_render_fs;

static GLuint framebuffer_shadow;

static const char *shader_shadow_vs_source =
	"#version 120
"
	"#extension GL_EXT_gpu_shader4 : require
"
	"void main()
"
	"{
"
	"	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
"
	"}
"
;
static const char *shader_render_vs_source =
	"#version 120
"
	"#extension GL_EXT_gpu_shader4 : require
"
	"varying vec4 position_cs;
"
	"varying vec3 normal_cs;
"
	"varying vec3 color;
"
	"void main()
"
	"{
"
	"	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
"
	"	position_cs = gl_ModelViewMatrix * gl_Vertex;
"
	"	normal_cs = gl_NormalMatrix * gl_Normal;
"
	"	color = gl_Color.rgb;
"
	"}
"
;
static const char *shader_render_fs_source =
	"#version 120
"
	"#extension GL_EXT_gpu_shader4 : require
"
	"varying vec4 position_cs;
"
	"varying vec3 normal_cs;
"
	"varying vec3 color;
"
	"uniform mat4x4 camera_view_matrix_inv;
"
	"uniform mat4x4 light_view_matrix;
"
	"uniform mat4x4 light_projection_matrix;
"
	"uniform samplerCubeShadow shadow;
"
	"uniform vec3 light_position;
"
	"void main()
"
	"{
"
	"	vec4 position_ls = light_view_matrix * camera_view_matrix_inv * position_cs;
"
	
	// shadow map test
	"	vec4 abs_position = abs(position_ls);
"
	"	float fs_z = -max(abs_position.x, max(abs_position.y, abs_position.z));
"
	"	vec4 clip = light_projection_matrix * vec4(0.0, 0.0, fs_z, 1.0);
"
	"	float depth = (clip.z / clip.w) * 0.5 + 0.5;
"
	"	vec4 result = shadowCube(shadow, vec4(position_ls.xyz, depth));
"
	
	"	vec3 lvector = light_position - position_cs.xyz;
"
	"	float ldistance = length(lvector);
"
	"	float lintensity = max(dot(normal_cs, normalize(lvector)), 0.0) * 10.0;
"
	"	lintensity /= ldistance * ldistance;
"
	"	lintensity /= lintensity + 0.5;
"
	"	vec3 diffuse = lintensity * result.xyz * color;
"
	"	gl_FragColor = vec4(diffuse,1);
"
	"}
"
;

static void
app_init()
{
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	
	// create camera matrix
	glLoadIdentity();
	gluLookAt(camera_position[0], camera_position[1], camera_position[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	glGetFloatv(GL_MODELVIEW_MATRIX, camera_view_matrix);
	
	// create camera inverse matrix
	camera_view_matrix_inv[ 0] = camera_view_matrix[ 0];
	camera_view_matrix_inv[ 1] = camera_view_matrix[ 4];
	camera_view_matrix_inv[ 2] = camera_view_matrix[ 8];
	camera_view_matrix_inv[ 4] = camera_view_matrix[ 1];
	camera_view_matrix_inv[ 5] = camera_view_matrix[ 5];
	camera_view_matrix_inv[ 6] = camera_view_matrix[ 9];
	camera_view_matrix_inv[ 8] = camera_view_matrix[ 2];
	camera_view_matrix_inv[ 9] = camera_view_matrix[ 6];
	camera_view_matrix_inv[10] = camera_view_matrix[10];
	camera_view_matrix_inv[12] = camera_position[0];
	camera_view_matrix_inv[13] = camera_position[1];
	camera_view_matrix_inv[14] = camera_position[2];
	camera_view_matrix_inv[ 3] = 0.0f;
	camera_view_matrix_inv[ 7] = 0.0f;
	camera_view_matrix_inv[11] = 0.0f;
	camera_view_matrix_inv[15] = 1.0f;
	
	// create light face matrices
	glLoadIdentity(); gluLookAt(0.0, 0.0, 0.0,  1.0, 0.0, 0.0,  0.0,-1.0, 0.0); // +X
	glGetFloatv(GL_MODELVIEW_MATRIX, light_face_matrix[0]);
	glLoadIdentity(); gluLookAt(0.0, 0.0, 0.0, -1.0, 0.0, 0.0,  0.0,-1.0, 0.0); // -X
	glGetFloatv(GL_MODELVIEW_MATRIX, light_face_matrix[1]);
	glLoadIdentity(); gluLookAt(0.0, 0.0, 0.0,  0.0, 1.0, 0.0,  0.0, 0.0, 1.0); // +Y
	glGetFloatv(GL_MODELVIEW_MATRIX, light_face_matrix[2]);
	glLoadIdentity(); gluLookAt(0.0, 0.0, 0.0,  0.0,-1.0, 0.0,  0.0, 0.0,-1.0); // -Y
	glGetFloatv(GL_MODELVIEW_MATRIX, light_face_matrix[3]);
	glLoadIdentity(); gluLookAt(0.0, 0.0, 0.0,  0.0, 0.0, 1.0,  0.0,-1.0, 0.0); // +Z
	glGetFloatv(GL_MODELVIEW_MATRIX, light_face_matrix[4]);
	glLoadIdentity(); gluLookAt(0.0, 0.0, 0.0,  0.0, 0.0,-1.0,  0.0,-1.0, 0.0); // -Z
	glGetFloatv(GL_MODELVIEW_MATRIX, light_face_matrix[5]);
	
	// create light projection matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(90.0, 1.0, SHADOW_NEAR, SHADOW_FAR);
	glGetFloatv(GL_PROJECTION_MATRIX, light_projection_matrix);
	glMatrixMode(GL_MODELVIEW);
	
	// create depth cube map
	glGenTextures(1, &tex_depth_cube);
	glBindTexture(GL_TEXTURE_CUBE_MAP, tex_depth_cube);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
	for (size_t i = 0; i < 6; ++i) {
		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_SIZE, SHADOW_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
	}
	glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
	
	// create shadow shader
	shader_shadow_vs = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(shader_shadow_vs, 1, &shader_shadow_vs_source, NULL);
	glCompileShader(shader_shadow_vs);
	
	// create shadow program
	program_shadow = glCreateProgram();
	glAttachShader(program_shadow, shader_shadow_vs);
	glLinkProgram(program_shadow);
	
	// create render shader
	shader_render_vs = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(shader_render_vs, 1, &shader_render_vs_source, NULL);
	glCompileShader(shader_render_vs);
	shader_render_fs = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(shader_render_fs, 1, &shader_render_fs_source, NULL);
	glCompileShader(shader_render_fs);
	
	// create render program
	program_render = glCreateProgram();
	glAttachShader(program_render, shader_render_vs);
	glAttachShader(program_render, shader_render_fs);
	glLinkProgram(program_render);
	
	// create shadow framebuffer
	glGenFramebuffersEXT(1, &framebuffer_shadow);
}

static void
draw_shadow_casters()
{
	glColor3ub(255,0,0);
	glPushMatrix();
		glTranslated(-1.0, 0.0,-1.0);
		glutSolidSphere(1.0, 20, 10);
	glPopMatrix();
	
	glColor3ub(0,255,0);
	glPushMatrix();
		glTranslated( 1.0, 0.0, 1.0);
		glutSolidCube(2.0);
	glPopMatrix();
	
	glColor3ub(0,0,255);
	glPushMatrix();
		glTranslated( 1.0, 0.0,-1.0);
		glutSolidIcosahedron();
	glPopMatrix();
	
	glColor3ub(255,0,255);
	glPushMatrix();
		glTranslated(-1.0, 0.0, 1.0);
		glutSolidOctahedron();
	glPopMatrix();
}

static void
draw_scene()
{
	draw_shadow_casters();
	
	glColor3ub(255, 255, 255);
	glNormal3f(0.0f, 1.0f, 0.0f);
	glBegin(GL_QUADS);
		glVertex3i( 10, -1,-10);
		glVertex3i(-10, -1,-10);
		glVertex3i(-10, -1, 10);
		glVertex3i( 10, -1, 10);
	glEnd();
}

static void
app_update()
{
	// rotate the light about the Y-axis
	light_azimuth = fmodf(light_azimuth + (0.1f * M_PI / 180.0f), 2.0f * M_PI);
	
	// update the world space light position
	light_position_ws[0] = light_distance * sinf(light_inclination) * cosf(light_azimuth);
	light_position_ws[1] = light_distance * cosf(light_inclination);
	light_position_ws[2] = light_distance * sinf(light_inclination) * sinf(light_azimuth);
	
	// create the light view matrix (construct this as you would a camera matrix)
	glLoadIdentity();
	glTranslatef(-light_position_ws[0], -light_position_ws[1], -light_position_ws[2]);
	glGetFloatv(GL_MODELVIEW_MATRIX, light_view_matrix);
	
	// transform world space light position to camera space
	light_position_cs[0] =
		camera_view_matrix[ 0] * light_position_ws[0] +
		camera_view_matrix[ 4] * light_position_ws[1] +
		camera_view_matrix[ 8] * light_position_ws[2] +
		camera_view_matrix[12];
	light_position_cs[1] =
		camera_view_matrix[ 1] * light_position_ws[0] +
		camera_view_matrix[ 5] * light_position_ws[1] +
		camera_view_matrix[ 9] * light_position_ws[2] +
		camera_view_matrix[13];
	light_position_cs[2] =
		camera_view_matrix[ 2] * light_position_ws[0] +
		camera_view_matrix[ 6] * light_position_ws[1] +
		camera_view_matrix[10] * light_position_ws[2] +
		camera_view_matrix[14];
}

static void
app_display()
{
	app_update();
	
	////////////////////////////////////////////////////////////////////////////
	// RENDER DEPTH CUBE MAP
	
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer_shadow);
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	glViewport(0, 0, SHADOW_SIZE, SHADOW_SIZE);
	
	glCullFace(GL_FRONT);
	
	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf(light_projection_matrix);
	glMatrixMode(GL_MODELVIEW);
	
	for (size_t i = 0; i < 6; ++i) {
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, tex_depth_cube, 0);
		
		glClear(GL_DEPTH_BUFFER_BIT);
		
		glLoadMatrixf(light_face_matrix[i]);
		glMultMatrixf(light_view_matrix);
		
		glUseProgram(program_shadow);
		
		draw_shadow_casters();
	}
	
	////////////////////////////////////////////////////////////////////////////
	// RENDER SCENE
	
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	glDrawBuffer(GL_BACK);
	glReadBuffer(GL_BACK);
	glViewport(0, 0, WINDOW_SIZE, WINDOW_SIZE);
	
	glCullFace(GL_BACK);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, 1.0, WINDOW_NEAR, WINDOW_FAR);
	glMatrixMode(GL_MODELVIEW);
	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	glLoadMatrixf(camera_view_matrix);
	
	glUseProgram(program_render);
	glUniform1i(glGetUniformLocation(program_render, "shadow"), 0);
	glUniform3fv(glGetUniformLocation(program_render, "light_position"), 1, light_position_cs);
	glUniformMatrix4fv(glGetUniformLocation(program_render, "camera_view_matrix_inv"), 1, GL_FALSE, camera_view_matrix_inv);
	glUniformMatrix4fv(glGetUniformLocation(program_render, "light_view_matrix"), 1, GL_FALSE, light_view_matrix);
	glUniformMatrix4fv(glGetUniformLocation(program_render, "light_projection_matrix"), 1, GL_FALSE, light_projection_matrix);
	
	glEnable(GL_TEXTURE_CUBE_MAP);
	glBindTexture(GL_TEXTURE_CUBE_MAP, tex_depth_cube);
	
	draw_scene();
	
	glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
	glDisable(GL_TEXTURE_CUBE_MAP);
	
	glUseProgram(0);
	glPushMatrix();
		glTranslatef(light_position_ws[0], light_position_ws[1], light_position_ws[2]);
		glColor3ub(255,255,0);
		glutSolidSphere(0.1, 10, 5);
	glPopMatrix();
	
	glutSwapBuffers();
}

static void
app_idle()
{
	glutPostRedisplay();
}

int
main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_ALPHA | GLUT_STENCIL | GLUT_DOUBLE);
	glutInitWindowSize(WINDOW_SIZE, WINDOW_SIZE);
	glutInitWindowPosition(-1, -1);
	glutCreateWindow("example");
	
	glutDisplayFunc(app_display);
	glutIdleFunc(app_idle);
	
	app_init();

	glutMainLoop();
	
	return (0);
}

[/QUOTE]</div>

perfect!
thank you very much !!

i appreciate your help, there is nothing else than a source code that can clearly help out people to understand the thing.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.