Использование OpenGL

ПРИЛОЖЕНИЕ А

Программа генерирования теней и отражений

#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]);
}