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

ПРИЛОЖЕНИЕ В

Программа эмуляции эффекта тумана

class CFog : public CObject  
{
	DECLARE_SERIAL(CFog)
	enum {LEN=256};
private:
	GLfloat alpha_scale, alpha_bias;
	GLfloat fog_map[LEN];
	GLfloat *buffer;
	int last_width, last_height;
// защищённые данные
public:
	GLfloat eye_near, eye_far;
	GLfloat fog_start, fog_end;
	GLenum fog_mode;
	GLfloat fog_density;
	union {
		struct {GLfloat fog_red, fog_green, fog_blue, fog_alpha;};
		GLfloat fogColor[4];
	};
	BOOL valid; // состояние не изменялось (не пересчитывать карты, etc)
	BYTE af;	// 0 -> OFF; 1->GL_FOG; 2->AF_FOG
// интерфейс для установки параметров программного тумана
public:
	void GLFog();
	inline void SetAFType(BYTE type=2) {af=type; valid=FALSE;}
	inline BYTE GetAFType() {return af;}
	inline void afEyeNearFar(GLfloat fnear, GLfloat ffar)
	{	eye_near = fnear;	eye_far = ffar;	valid = FALSE;	}
	inline void afFogStartEnd(GLfloat start, GLfloat end)
	{	fog_start = start;	fog_end = end;	valid = FALSE;	}
	inline void afFogMode(GLenum mode)
	{	fog_mode = mode; valid = FALSE;	}
	inline void afFogDensity(GLfloat density)
	{	fog_density = density;	valid = FALSE;	}
	inline void afFogColor(GLfloat red, GLfloat green, GLfloat blue)
	{	fog_red = red;	fog_green = green;	fog_blue = blue;	}
	void afDoFinalFogPass(GLint x, GLint y, GLsizei width, GLsizei height);
	CFog();
	virtual ~CFog();
	void Serialize(CArchive &ar);
};

#include "stdafx.h"
#include "GL.h"
#include "Fog.h"

CFog::CFog()
{
	eye_near    = 0.0; eye_far = 1.0;
	fog_start   = 0.0; fog_end = 1.0;
	fog_mode    = GL_EXP; fog_density = 1.0;
	valid = FALSE; af = TRUE;
	buffer = 0;	fog_alpha = 1.0;
}

CFog::~CFog(){	if (buffer) delete [] buffer;	}

void CFog::afDoFinalFogPass(GLint x, GLint y, GLsizei width, GLsizei height)
{
	int i;
	switch (af){
		case 0: glDisable(GL_FOG); return; // туман не нужен
		case 1:	GLFog(); return;		   // туман GL
		case 2:	glDisable(GL_FOG); break;  // программный туман
		default: return; // этого не может быть!
	};
	if (width * height != last_width * last_height) {
		if (buffer) delete [] buffer;
		buffer = new GLfloat[width * height];
		last_width = width;
		last_height = height;
	}
	if (!valid) {
		switch (fog_mode) {
		case GL_LINEAR:
	// вычислить alpha для линейного тумана, начиная с "f = (e-z)/(e-s)"
		 alpha_scale = (eye_far - eye_near) / (fog_end - fog_start);
		 alpha_bias = (eye_near - fog_start) / (fog_end - fog_start);
		 break;
		case GL_EXP:
			// установить fog_map: "f = exp(-d*z)"
			for (i = 0; i < LEN; i += 1) {
				float fi, z, dz;
				fi = i * 1.0 / (LEN - 1);
				z = eye_near + fi * (eye_far - eye_near);
				dz = fog_density * z;
				fog_map[i] = 1.0 - exp(-dz);
			}
			break;
		case GL_EXP2:
			// установить fog_map: "f = exp(-d*z)^2)"
			for (i = 0; i < LEN; i += 1) {
				float fi, z, dz;
				fi = i * 1.0 / (LEN - 1);
				z = eye_near + fi * (eye_far - eye_near);
				dz = fog_density * z;
				fog_map[i] = 1.0 - exp(-dz * dz);
			}
			break;
		default:;
		}
		valid = TRUE;
	}
	// afDoFinalFogPass не пытается сохранить эти переменные !
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glPixelStorei(GL_PACK_ALIGNMENT, 1);

	// сохранить текущую позицию растра, окно вывода, состояние матриц,
	// функцию затенения, ...
	glPushAttrib(GL_PIXEL_MODE_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT
 | GL_TRANSFORM_BIT | GL_VIEWPORT_BIT | GL_CURRENT_BIT);
	// перенести текущую позицию растра в (x,y)
	glMatrixMode(GL_MODELVIEW);
	glViewport(x-1, y-1, 2, 2);
	glPushMatrix();
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glRasterPos2i(0, 0);
	// точно не нужно, чтобы туман или Z-буфер были разрешены
	glDisable(GL_FOG);
	glDisable(GL_DEPTH_TEST);
	// альфа = "1 - f", где f - фактор затенения
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

	switch (fog_mode) {
		case GL_LINEAR:
		// установить RGB = цвету тумана
		glPixelTransferf(GL_RED_SCALE, 0);
		glPixelTransferf(GL_GREEN_SCALE, 0);
		glPixelTransferf(GL_BLUE_SCALE, 0);
		glPixelTransferf(GL_RED_BIAS, fog_red);
		glPixelTransferf(GL_GREEN_BIAS, fog_green);
		glPixelTransferf(GL_BLUE_BIAS, fog_blue);
		glPixelTransferf(GL_ALPHA_SCALE, alpha_scale);
		glPixelTransferf(GL_ALPHA_BIAS, alpha_bias);
		break;
		case GL_EXP:
		case GL_EXP2:
		// установить RGB = цвету тумана
		glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 1, &fog_red);
		glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 1, &fog_green);
		glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 1, &fog_blue);
		glPixelMapfv(GL_PIXEL_MAP_A_TO_A, LEN, fog_map);
		glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
		break;
	}
	// читаем Z-буфер
	glReadPixels(x, y, width, height,
		GL_DEPTH_COMPONENT, GL_FLOAT, buffer);
	// и записывем его обратно вместо альфа
	glDrawPixels(width, height, GL_ALPHA,
		GL_FLOAT, buffer);
	// восстанавливаем установки GL
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	glPopAttrib();
}

void CFog::GLFog()
{
	glEnable(GL_FOG);
	if (!valid) {
		valid = TRUE;
		glFogi (GL_FOG_MODE, fog_mode);
		glHint (GL_FOG_HINT, GL_NICEST); // попиксельный туман
		glFogf (GL_FOG_START, fog_start);
		glFogf (GL_FOG_END, fog_end);
		glFogf(GL_FOG_DENSITY, fog_density);
	    glFogfv (GL_FOG_COLOR, fogColor);
	}
}