Программа эмуляции эффекта тумана
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);
}
}