Программа генерирования теней и отражений
#include "stdafx.h"
#include "GL.h"
#include "Scene.h"
#include "GLDoc.h"
#include "GLView.h"
static GLfloat floorVertices[4][3] = {
{ -4.0, -0.0, 4.0 },
{ 4.0, -0.0, 4.0 },
{ 4.0, -0.0, -4.0 },
{ -4.0, -0.0, -4.0 }
};
enum { A, B, C, D};
void CScene::Default()
{
// rendering modes are all ON
stencilReflection=TRUE; stencilShadow=TRUE; offsetShadow=TRUE;
renderShadow=TRUE; renderObject=TRUE; renderReflection=TRUE;
fov=45.0; zNear=1.0; zFar=20.0; cx=320.0; cy=240.0;
tx=0.0; ty=0.0; tz=-6.0;
rx=0.0; ry=0.0; rz=0.0; curry=0.0;
rotate=FALSE;
pView=NULL;
// fog std params
fog.afFogStartEnd(zFar-(zNear+zFar)/4.0,zFar);
fog.afEyeNearFar(zNear, zFar);
fog.afFogColor(0.66f, 0.0f, 0.0f);
fog.afFogDensity(0.35f);
fog.afFogMode(GL_LINEAR);
fog.SetAFType(0);
InitGL();
InitShadow();
InitLights();
Resize(640, 480);
object.CreateFont();
object.SetString(CString("SGI OpenGL"));
objectTexture.load(CString("aaa.bbb"));
objectTexture.SetDefaultParams();
floorTexture.load(CString("droplets.pcx"));
floorTexture.SetDefaultParams();
}
void CScene::InitLights()
{
//default
CLight *plight=new CLight(0);
plight->Default(); plight->On();
lights.AddHead(plight);
for (int i=1;iDefault();
if (i==1) plight->On();
else plight->Off();
plight->SetPosition(1.5f, 1.4f, -5.f, 1.f);
lights.AddTail(plight);
}
}
void CScene::FreeAll()
{
// удалить все источники света
while (!lights.IsEmpty()) {
CLight *plight=(CLight*)lights.RemoveHead();
delete plight;
}
// удалить глобальную палитру
if (CImage::globalPalette) delete [] CImage::globalPalette;
}
void CScene::InitShadow()
{
// установить плоскость для проецирования теней
findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
}
void CScene::DrawFloor(BOOL shadow)
{
glDisable(GL_LIGHTING);
if (shadow) {
glBegin(GL_QUADS);
glVertex3fv(floorVertices[0]);
glVertex3fv(floorVertices[1]);
glVertex3fv(floorVertices[2]);
glVertex3fv(floorVertices[3]);
glEnd();
} else {
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
floorTexture.SetActive();
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex3fv(floorVertices[0]);
glTexCoord2f(0.0, 1.0);
glVertex3fv(floorVertices[1]);
glTexCoord2f(1.0, 1.0);
glVertex3fv(floorVertices[2]);
glTexCoord2f(1.0, 0.0);
glVertex3fv(floorVertices[3]);
glEnd();
glDisable(GL_TEXTURE_2D);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
glEnable(GL_LIGHTING);
}
void CScene::DrawObjects(BOOL shadow)
{
if (shadow) {
object.Render(shadow);
} else {
glEnable(GL_TEXTURE_2D);
objectTexture.SetActive();
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
object.Render(shadow);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_TEXTURE_2D);
}
}
void CScene::Render()
{
// очищаем буферы на каждом кадре
// stencil=0 по умолчанию
if (!stencilReflection && !stencilShadow)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
else glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
MakeShadowMatrices(); // создать матрицы для тени от каждого источника
glPushMatrix(); // сохранить матрицу видового преобразования
MakeTextureMatrix(); // задать матрицу текстуры
if (rotate) {glRotatef(curry,0,1,0); curry+=ry;} // вращение OY
SetLightPositions(); // установить положения источников света
if (renderReflection) {
//==1== подготовка буфера трафарета для вывода отражения
if (stencilReflection) {
// не изменять цвет или значение в Z-буфере. изменять stencil
// этот проход только для буфера трафарета : в результате получим
// единицу в буфере трафарета для всех точек "пола"
glDisable(GL_DEPTH_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
DrawFloor(TRUE);
// разрешить цвет и Z-буфер
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_DEPTH_TEST);
// и рендерим только туда, где stencil=1
glStencilFunc(GL_EQUAL, 1, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
//==2== вывод отражения объекта
glPushMatrix();
// ВАЖНЫЙ ШАГ для получения отражения:
// отражаем объект сквозь пол (Y=0)
glScalef(1.0, -1.0, 1.0);
// отражаем положение источника света
SetLightPositions();
// чтобы всё было хорошо с нормалями нужно включить это ...
glEnable(GL_NORMALIZE);
glCullFace(GL_FRONT); // отсекаем лицевые грани
// здесь получаем отражение объекта
DrawObjects();
// восстановить установки для нормалей и режима отсечения
glDisable(GL_NORMALIZE);
glCullFace(GL_BACK);
glPopMatrix();
// снова установить положение источника света (неотражённого)
SetLightPositions();
if (stencilReflection) glDisable(GL_STENCIL_TEST);
}
//==3== установление параметров плоскости и вывод её на экран
// отсечение нелицевых граней можно использовать для задания различного
// покрытия плоскости с разных сторон
// Низ - синего цвета
glFrontFace(GL_CW); // переключить порядок обхода
glColor4f(1.0f, 1.0f, 0.7f, 1.0f);
DrawFloor();
glFrontFace(GL_CCW);
if (renderShadow) {
if (stencilShadow) {
// рисуем пол со значением в буфере трафарета=3
// Т. о. мы будем рисовать тень только один раз на каждый
// пиксель только заданной плоскости
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
}
}
// Рисуем "верх" пола. Используем смешение для получения зеркального отражения
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(1.0f, 1.0f, 1.0f, 0.3f);
DrawFloor();
glDisable(GL_BLEND);
//==4== вывод самих объектов (рисуем НОРМАЛЬНЫЕ объекты (не отражение))
if (renderObject) {
DrawObjects();
}
//==5== вывод тени
if (renderShadow) {
// выводим спроецированную тень
if (stencilShadow) {
// теперь рисуем только туда, где stencil>2 (т. е. 3 - пол)
// Установить stencil=2 там, где выводится тень, так что мы
// не перерисуем (и случайно "пересмешаем") тень
glStencilFunc(GL_LESS, 2, 0xffffffff);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
}
// чтобы избавится от глюков Z-буфера мы используем polygon offset
// для небольшого поднятия тени над плоскостью
if (offsetShadow) glEnable(GL_POLYGON_OFFSET_FILL);
// рисуем серую(50%) тень
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
glColor4f(0.0, 0.0, 0.0, 0.5);
ProjectShadows();
glDisable(GL_BLEND);
glEnable(GL_LIGHTING);
if (offsetShadow) glDisable(GL_POLYGON_OFFSET_FILL);
if (stencilShadow) glDisable(GL_STENCIL_TEST);
}
ShowLights();
glPopMatrix();
fog.afDoFinalFogPass(0, 0, cx, cy); // выводим программный туман
glFlush();
SwapBuffers(wglGetCurrentDC());
}
// уравнение плоскости для заданных трёх точек
void CScene::findPlane(GLfloat plane[4],
GLfloat v0[3],
GLfloat v1[3],
GLfloat v2[3])
{
GLfloat vec0[3], vec1[3];
// по трём точкам вычисляем векторы
vec0[X] = v1[X] - v0[X];
vec0[Y] = v1[Y] - v0[Y];
vec0[Z] = v1[Z] - v0[Z];
vec1[X] = v2[X] - v0[X];
vec1[Y] = v2[Y] - v0[Y];
vec1[Z] = v2[Z] - v0[Z];
// векторное произведение
plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]);
plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
}