Original post :
http://content.gpwiki.org/index.php/Generating_Mesh_Shadows_On_Terrain_Using_OpenGL
//
// complete function.
//
// unsigned char *lightmap
// int lightmapSize
// unsigned char shadowColor[3]
// float lightdir[3]
void MeshShadows(unsigned char *lightmap, int lightmapSize, unsigned char shadowColor[3], float lightDir[3]) { // variable initialization int lightmapChunkSize = 128; if(lightmapSize < lightmapChunkSize) lightmapChunkSize = lightmapSize; float terrainDivisions = lightmapSize / lightmapChunkSize; int terrainChunkSize = Terrain.size() / terrainDivisions; unsigned char *chunk = new unsigned char[(lightmapChunkSize)*(lightmapChunkSize)*3]; // create shadow texture GLuint shadowChunk; glGenTextures(1, &shadowChunk); glBindTexture(GL_TEXTURE_2D, shadowChunk); glEnable(GL_TEXTURE_2D); glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, lightmapChunkSize, lightmapChunkSize, 0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); int terrainCol = 0; int terrainRow = 0; float x0 = 0; float y0 = 0; float x1 = terrainChunkSize; float y1 = terrainChunkSize; for(terrainRow = 0; terrainRow < terrainDivisions; terrainRow++) { for(terrainCol = 0; terrainCol < terrainDivisions; terrainCol++) { // setup orthogonal view glViewport(0, 0, terrainChunkSize * Terrain.size(), terrainChunkSize * Terrain.size()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, terrainChunkSize, 0, terrainChunkSize, -10000, 10000); glMatrixMode(GL_MODELVIEW); // apply light's direction to modelview matrix gluLookAt(lightDir[0], lightDir[1], lightDir[2], 0, 0, 0, 0, 1, 0); // loop through all vertices in terrain and find min and max points with respect to screen space float minX = 999999, maxX = -999999; float minY = 999999, maxY = -999999; double X, Y, Z; // get pointer to terrain vertices float *vertices = Terrain.vertices()->lock(); for(int i = y0-1; i < y1+1; i++) { if(i < 0) continue; for(int j = x0-1; j < x1+1; j++) { if(j < 0) continue; int index = i * Terrain.size() + j; // get screen coordinates for current vertex static GLint viewport[4]; static GLdouble modelview[16]; static GLdouble projection[16]; static GLfloat winX, winY, winZ; glGetDoublev( GL_MODELVIEW_MATRIX, modelview ); glGetDoublev( GL_PROJECTION_MATRIX, projection ); glGetIntegerv( GL_VIEWPORT, viewport ); gluProject(vertices[index*3+0], vertices[index*3+1], vertices[index*3+2], modelview, projection, viewport, &X, &Y, &Z); if(X < minX) minX = X; if(X > maxX) maxX = X; if(Y < minY) minY = Y; if(Y > maxY) maxY = Y; } } // clear min and max values static float minX2, minY2, maxX2, maxY2; minX2 = minX; minY2 = minY; maxX2 = maxX; maxY2 = maxY; // clear screen glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, lightmapChunkSize, lightmapChunkSize); // orient viewport so that terrain chunk fits inside glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(minX, maxX, minY, maxY, -10000, 10000); glMatrixMode(GL_MODELVIEW); // apply light's direction vector to model view transformation glLoadIdentity(); gluLookAt(lightDir[0], lightDir[1], lightDir[2], 0, 0, 0, 0, 1, 0); // disable writing to the color buffer glColorMask(false, false, false, false); // render terrain Terrain.render(); // enable writing to the color buffer glColorMask(true, true, true, true); // render scene meshes '''BLACK''' RenderAllSceneMeshes(); // bind shadowChunk texture and copy frame buffer data glBindTexture(GL_TEXTURE_2D, shadowChunk); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightmapChunkSize, lightmapChunkSize); glBindTexture(GL_TEXTURE_2D, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, terrainChunkSize * Terrain.size(), terrainChunkSize * Terrain.size()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 0, terrainChunkSize, terrainChunkSize, -10000, 10000); glMatrixMode(GL_MODELVIEW); // rotate view so that xz plane becomes the xy plane glLoadIdentity(); glRotatef(90, 1, 0, 0); // reset max and min values minX = minY = 999999; maxX = maxY = -999999; glGetDoublev( GL_MODELVIEW_MATRIX, modelview ); glGetDoublev( GL_PROJECTION_MATRIX, projection ); glGetIntegerv( GL_VIEWPORT, viewport ); // project each corner onto the screen // the corners are represented by the x0, y0, x1 and y1 values gluProject(x0, y0, modelview, projection, viewport, &X, &Y, &Z); if(X < minX) minX = X; if(X > maxX) maxX = X; if(Y < minY) minY = Y; if(Y > maxY) maxY = Y; gluProject(x1, y0, modelview, projection, viewport, &X, &Y, &Z); if(X < minX) minX = X; if(X > maxX) maxX = X; if(Y < minY) minY = Y; if(Y > maxY) maxY = Y; gluProject(x1, y1, modelview, projection, viewport, &X, &Y, &Z); if(X < minX) minX = X; if(X > maxX) maxX = X; if(Y < minY) minY = Y; if(Y > maxY) maxY = Y; gluProject(x0, y1, modelview, projection, viewport, &X, &Y, &Z); if(X < minX) minX = X; if(X > maxX) maxX = X; if(Y < minY) minY = Y; if(Y > maxY) maxY = Y; // resize and re-orient the viewport glViewport(0, 0, terrainChunkSize * Terrain.size(), terrainChunkSize * Terrain.size()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(minX, minY, maxX, maxY, -10000, 10000); glMatrixMode(GL_MODELVIEW); // rotate view so that xz plane becomes the xy plane glLoadIdentity(); glRotatef(90, 1, 0, 0); // setup projective texturing float PS[] = {1, 0, 0, 0}; float PT[] = {0, 1, 0, 0}; float PR[] = {0, 0, 1, 0}; float PQ[] = {0, 0, 0, 1}; glTexGenfv(GL_S, GL_EYE_PLANE, PS); glTexGenfv(GL_T, GL_EYE_PLANE, PT); glTexGenfv(GL_R, GL_EYE_PLANE, PR); glTexGenfv(GL_Q, GL_EYE_PLANE, PQ); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_GEN_Q); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); // setup texture matrix glBindTexture(GL_TEXTURE_2D, shadowChunk); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glTranslatef(0.5, 0.5, 0); glScalef(0.5, 0.5, 1); glOrtho(minX2, maxX2, minY2, maxY2, -10000, 10000); gluLookAt(lightDir[0], lightDir[1], lightDir[2], 0, 0, 0, 0, 1, 0); glMatrixMode(GL_MODELVIEW); // render the terrain Terrain.render(); glBindTexture(GL_TEXTURE_2D, shadowChunk); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightmapChunkSize, lightmapChunkSize); // disable projective texturing glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); glDisable(GL_TEXTURE_GEN_Q); // reset texture matrix glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); // get shadow texture data glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); for(int a = 0; a < lightmapChunkSize; a++) { for(int b = 0; b < lightmapChunkSize; b++) { int a2 = a + lightmapChunkSize * terrainRow; int b2 = b + lightmapChunkSize * terrainCol; lightmap[(a2 * lightmapSize + b2) * 3 + 0] = pixels[(a * lightmapChunkSize + b) * 3 + 0]; lightmap[(a2 * lightmapSize + b2) * 3 + 1] = pixels[(a * lightmapChunkSize + b) * 3 + 1]; lightmap[(a2 * lightmapSize + b2) * 3 + 2] = pixels[(a * lightmapChunkSize + b) * 3 + 2]; } } } } // increment which section on the terrain we are looking at x0 += terrainChunkSize; x1 += terrainChunkSize; } x0 = 0; x1 = terrainChunkSize; y0 += terrainChunkSize; y1 += terrainChunkSize; } // free memory glDeleteTextures(1, &shadowTexture); delete [] pixels; }