Короче, в main задаются функции обратного вызова. 
glutDisplayFunc(RenderScene) - функция RenderScene - отрисовка кадра.
Остальное - обработчики клавиатуры и прочая фигня.

glutTimerFunc(TIME_INTERVAL_MS, timerFunc, 0) - таймер запускает через TIME_INTERVAL_MS функцию timerFunc, передаёт номер кадра 0, также там задаётся переменная номера текущего кадра, затем опять в конце функции timerFunc задаётся таймер glutTimerFunc(TIME_INTERVAL_MS, timerFunc, i + time_delta), но уже передаётся номер кадра инкрементированный. В той же функции вызывается glutPostRedisplay() - перерисовка.


Функция RenderScene, отрисовка кадра:

RegenerateShadowMap() - заполнение карты тени. Карта тени это просто текстура (область памяти в opengl. Обычно в текстуре хранится некая картинка, которая выводится на объект, но здесь не картинка). Как заполняется карта тени: перемещаемся в позицию источника света и для каждого пикселя текстуры записываем расстояние от источника света до первой встретившейся поверхности, т. е. эта поврехность освещена, не в тени.

glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); 				// Обнуляем матрицу наблюдения
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLfloat eyeX = radius * -sin(angleX + deltaAngleX) * cos(angleY + deltaAngleY);
GLfloat eyeY = radius * -sin(angleY + deltaAngleY);
GLfloat eyeZ = -radius * cos(angleX + deltaAngleX) * cos(angleY + deltaAngleY);
gluLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, 0, 1, 0);	// Преобразуем матрицу наблюдения в зависимости от того как мышь передвинул, ну типа камеру перемещаем

// Рисуем желтый шар, типа солнце
glPushMatrix();
glTranslatef(light0Pos[0], light0Pos[1], light0Pos[2]);
glColor3ub(255, 255, 40);
gluSphere(gluNewQuadric(), 1.0, 60, 60);
glPopMatrix();


// Устанавливаем туслое освещение, затем рисуем всю сцену
glLightfv(GL_LIGHT0, GL_AMBIENT, lowAmbient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lowDiffuse);
// Draw objects in the scene
DrawModels();

// Устанавливаем более яркий свет, будем потом рисовать сцену с таким светом, где теней не будет
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);

// Устанавливаем режим сравнения с тенью. Потом мы будем опять рисовать сцену, и оно будет сравнивать глубину того что рисует со значением в карте тени (а там расстояние до освещённое поверхности), если меньше то 1, иначе 0. Режим GL_MODULATE задаёт умножение этих нулей и единиц на значения цветов пикселей, которые мы будем далее рисовать. Т. е. где нули, там тень, чёрный цвет, а где единицы - цвет такой, какой рисуем.
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);

// Это чтобы тени были прозрачные. Где получился ноль, там рисоваться вообще ничего не будет, т. е. останется то, что мы рисовали ранне тусклым светом, типа тень
glAlphaFunc(GL_GREATER, 0.9f);
glEnable(GL_ALPHA_TEST);

// Параметры фильтрации текстуры
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

// Тут мы "натягиваем" текстуру тени на сцену с позиции плоскости источника света. У текстуры есть так называемые текстурные координаты, это расстояние от источника света до плоскости. А содержимое нашей текстуры тени (которую мы заполнили ранее) - это расстояния до освещённой поверхности, не теневой. Сравнивая эти значения, получим 0 или 1, как описано выше.
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
glTexGenfv(GL_R, GL_EYE_PLANE, rPlane);
glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane);

// Здесь просто рисуем сцену с установленным выше ярким освещением и с умножением на 0 и 1, а также с пропуском рисования, где получилось 0 (режим установлен выше, альфа-тест), т. е. там где пропускается, там рисуестя с тусклым освещением, как бы тень.
DrawModels();
