На самом деле, матричные тени — дело простое. Тут нам и понадобится наш класс матриц из предыдущего урока. Остальной код (main.cpp. init.cpp, main.h) возьмите из урока инициализации.
Изменяем только файл main.cpp:
#include «CMatrix.h»// Переменные, хранящие угол вращения источника света:
float xRotation = 0.0f;
float yRotation = 0.0f;// Эта матрица будет использоватся для хранения матрицы тени
CMatrix4x4 ShadowMatrix;// Следующие переменные очень важны для функционирования теней. Первая — позиция
// источника света, вторая — нормаль поверхности, на которую отбрасывается тень.
CVector4 lightPos(1.5f, 2.3f, —2.0f, 1.0f);
CVector4 planeNormal(0.0f, 1.0f, 0.0f, 0.0f);
/////////////////////////////////////////////////////////////////////
//
// Функция, отрисовывающая куб
//
/////////////////////////////////////////////////////////////////////
void DrawCube()
{
// Передний полигон
glBegin(GL_QUADS);
glVertex3f(—0.2f, 0.0f, 0.0f);
glVertex3f(—0.2f, 0.5f, 0.0f);
glVertex3f(0.3f, 0.5f, 0.0f);
glVertex3f(0.3f, 0.0f, 0.0f);
glEnd();
// Задний полигон
glBegin(GL_QUADS);
glVertex3f(—0.2f, 0.0f, —0.5f);
glVertex3f(—0.2f, 0.5f, —0.5f);
glVertex3f(0.3f, 0.5f, —0.5f);
glVertex3f(0.3f, 0.0f, —0.5f);
glEnd();
// Левый полигон
glBegin(GL_QUADS);
glVertex3f(—0.2f, 0.0f, —0.5f);
glVertex3f(—0.2f, 0.5f, —0.5f);
glVertex3f(—0.2f, 0.5f, 0.0f);
glVertex3f(—0.2f, 0.0f, 0.0f);
glEnd();
// Правый полигон
glBegin(GL_QUADS);
glVertex3f(0.3f, 0.0f, —0.5f);
glVertex3f(0.3f, 0.5f, —0.5f);
glVertex3f(0.3f, 0.5f, 0.0f);
glVertex3f(0.3f, 0.0f, 0.0f);
glEnd();
// Верхний полигон
glBegin(GL_QUADS);
glVertex3f(—0.2f, 0.5f, 0.0f);
glVertex3f(—0.2f, 0.5f, —0.5f);
glVertex3f(0.3f, 0.5f, —0.5f);
glVertex3f(0.3f, 0.5f, 0.0f);
glEnd();
// Нижний полигон
glBegin(GL_QUADS);
glVertex3f(—0.2f, 0.0f, 0.0f);
glVertex3f(—0.2f, 0.0f, —0.5f);
glVertex3f(0.3f, 0.0f, —0.5f);
glVertex3f(0.3f, 0.0f, 0.0f);
glEnd();
}
///////////////////////////////////////////////////////////////
//
// Теперь изменим нашу основную функцию:
//
///////////////////////////////////////////////////////////////
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, —1.0f, —5.0f);
glRotatef(—yRotation, 1.0f, 0.0f, 0.0f);
glRotatef(—xRotation, 0.0f, 1.0f, 0.0f);
glColor3f(1.0f, 1.0f, 1.0f);
// Сначала мы отирсуем поверхность и забудем о ней. Затем я решил
// нарисовать тень. Чтобы сделать это, мы создадим матрицу тени, основываясь на
// позиции источника света и нормали поверхности. Создав эту матрицу, мы умножим её
// на текущую задействованную матрицу. Таким образом всё, что вы отрендерите далее,
// будет отрисовано плоским на поверхности, в точности как тень от обьекта. В принципе,
// ничего сложного, верно? Чтобы нарисовать что-то в качестве тени, вы должны закрасить
// обьект черным цветом и отключить текстуры. Я использовал glPush/PopMatrix, чтобы
// операции с тенью не затронули остальных обьектов сцены.
// Рисуем один большой обьект в качестве поверхности.
glColor3f(0.4f,0.8f,0.4f);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(15.0f, —0.01f, 15.0f);
glTexCoord2f(1.0, 0.0); glVertex3f(—15.0f, —0.01f, 15.0f);
glTexCoord2f(1.0, 1.0); glVertex3f(—15.0f, —0.01f, —15.0f);
glTexCoord2f(0.0, 1.0); glVertex3f(15.0f, —0.01f, —15.0f);
glEnd();
// Создадим матрицу тени на основе нормали поверхности и позиции источника света
ShadowMatrix.CreateShadowMatrix(planeNormal, lightPos);
// Теперь рисуем тень…
glDisable(GL_DEPTH_TEST);
glPushMatrix();
// Одно то, что мы создали матрицу тени, ещё ничего не значит. Теперь нужно
// умножить её на текущую матрицу:
glMultMatrixf(ShadowMatrix.matrix);
glColor3f(0.0f, 0.0f, 0.0f);
// Нарисуем куб как обычно. Новая матрица сделает его плоским.
DrawCube();
glPopMatrix();
glEnable(GL_DEPTH_TEST);
// Теперь мы можем нарисовать обычный куб.
glColor3f(0.8f, 0.5f, 0.5f);
DrawCube();
// Теперь нарисуем источник света
glColor3f(1.0f, 1.0f, 0.0f);
glTranslatef(lightPos.x, lightPos.y, lightPos.z);
GLUquadricObj *pObj = gluNewQuadric();
gluSphere(pObj, 0.05f, 6,6);
SwapBuffers(g_hDC);
}
//////////////////////////////////////////////////////////////////////
//
// И последнее, что осталось сделать — написать обработку клавиш:
//
case VK_UP:
lightPos.z -= 0.05f;
break;
case VK_DOWN:
lightPos.z += 0.05f;
break;
case VK_LEFT:
lightPos.x -= 0.05f;
break;
case VK_RIGHT:
lightPos.x += 0.05f;
break;
Вот и всё. У нас есть тень, которая следует всллед за движениями «солнца».
Чтобы создать тень с помощью этой техники, сначала создаётся матрица тени. Затем вы отрисовываете обьект дважды, один раз используя матрицу, второй раз нет. Но учитывайте и то, что при использовании этого способа значительно возрастает и нагрузка, так что для крупного проекта она может и не подойти.