1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
|
/* Copyright (c) Mark J. Kilgard, 1997. */
/* This program is freely distributable without licensing fees and is
provided without guarantee or warrantee expressed or implied. This
program is -not- in the public domain. */
/* addfog.c is a set of routines for adding OpenGL-style depth attenuated fog
to a scene as a final rendering pass. This may be useful if you are doing
multipass algorithms where attempting to enable fog within the passes
would screw up the rendering effect.
The approach is to read back the depth buffer, then do an in-place draw
pixels of the depth buffer values back into the frame buffer as alpha.
OpenGL's standard pixel path is used to "blend in" fog. The Red, Green,
and Blue components are forced to the fog color; alpha is either scaled &
biased (for GL_LINEAR style fog) or remapped with OpenGL's "map color"
capability (for GL_EXP or GL_EXP style fog). The result is blended with
the current frame buffer contents. The result is almost identical to
OpenGL's fog.
With this fogging technique, the fog pass time is always proportional to
the number of pixels in the window. This is in contrast to fogging the
entire scene standard OpenGL fog during rendering. With standard OpenGL
fog rendering, a scene with a depth complexity greater than 1 may end up
fogging many more pixels than are visible. If fog is not supported for
free in hardware and you are rendering a scene with high enough depth
complexity, this fogging technique could be faster than standard OpenGL
fog.
As mentioned earlier, the technique may also be appropriate in multi-pass
rendering algorithms to avoid fog improperly interferring with the various
passes. Examples: reflections or shadows.
A more sophisticated version of this technique could be used to simulate
"eye distance" attenuated fog instead of OpenGL's "depth" attenuated fog.
The technique could perform the depth to alpha read/write in tiles with a
pixel map set up to attenuate based on eye distance instead of simply
depth.
This approach could be made more efficient with an extension to
glCopyPixels to support a copy of one frame buffer type to another
(specifically, depth to alpha).
One side-effect of this approach is that it trashes your destination
alpha buffer (if you even have one). */
/** Using the "addfog" routines:
1) Given your near and far ranges (generally from glOrtho, glFrustum, or
gluPerspective), call:
afEyeNearFar(near, far);
Careful since "near" and "far" are reserved words on PC compilers.
2) Instead calling glFog to set fog parameters, use the corresponding
"addfog" routines as shown below:
Instead of:
glFogf(GL_FOG_START, start); glFogf(GL_FOG_END, end);
Use:
afFogStartEnd(start, end);
Instead of:
glFogi(GL_FOG_MODE, mode);
Use:
afFogMode(mode);
Instead of:
glFogf(GL_FOG_DENSITY, density);
Use:
afFogDensity(density);
Instead of:
GLfloat fogcolor[4] = { red, green, blue, alpha };
glFogfv(GL_FOG_COLOR, fog_color);
Use:
afFogColor(red, green, blue);
3) Draw you scene *without* OpenGL fog enabled.
4) Assuming you want to fog the entire window of size width by
height, call:
afDoFinalFogPass(0, 0, width, height);
Note: x & y are OpenGL-style lower-left hand window coordinates.
5) Call glFinish or do a buffer swap.
That's it. View your fogged scene. */
#ifdef __sgi /* SGI has a good alloca; many other machines don't. */
#define HAS_ALLOCA
#endif
#include <stdlib.h>
#ifdef HAS_ALLOCA
#include <alloca.h>
#endif
#ifdef _WIN32
#include <windows.h>
#pragma warning (disable:4244) /* disable bogus conversion warnings */
#endif
#include <GL/gl.h>
#include <math.h>
static GLfloat eye_near = 0.0, eye_far = 1.0;
static GLfloat fog_start = 0.0, fog_end = 1.0;
static GLenum fog_mode = GL_EXP;
static GLfloat fog_density = 1.0;
static int valid = 0;
static GLfloat fog_red, fog_green, fog_blue;
void
afEyeNearFar(GLfloat fnear, GLfloat ffar)
{
eye_near = fnear;
eye_far = ffar;
valid = 0;
}
void
afFogStartEnd(GLfloat start, GLfloat end)
{
fog_start = start;
fog_end = end;
valid = 0;
}
void
afFogMode(GLenum mode)
{
fog_mode = mode;
valid = 0;
}
void
afFogDensity(GLfloat density)
{
fog_density = density;
valid = 0;
}
void
afFogColor(GLfloat red, GLfloat green, GLfloat blue)
{
fog_red = red;
fog_green = green;
fog_blue = blue;
}
#define LEN 256
void
afDoFinalFogPass(GLint x, GLint y, GLsizei width, GLsizei height)
{
static GLfloat alpha_scale, alpha_bias;
static GLfloat fog_map[LEN];
int i;
#ifdef HAS_ALLOCA
void *buffer = alloca((unsigned int) sizeof(GLushort) * width * height);
#else
static void *buffer = NULL;
static int last_width, last_height;
if (width * height != last_width * last_height) {
buffer = realloc(buffer, sizeof(GLushort) * width * height);
last_width = width;
last_height = height;
}
#endif
if (!valid) {
switch (fog_mode) {
case GL_LINEAR:
/* Figure out linear fog blending from "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:
/* Setup fog_map to be "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:
/* Setup fog_map to be "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:;
/* Mesa makes GLenum an actual enumerant. Have a default
case to avoid all the gcc warnings from all the other
GLenum values that we are not handling. */
}
}
/* XXX Careful, afDoFinalFogPass makes no attempt to preserve your
pixel store state and assumes the initial pixel store state! */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
/* Preserve the current raster position, viewport, matrix mode,
blend function, enable state, and pixel path state. */
/* XXX This is pretty expensive. A real application should just
"know" to reload all the OpenGL state mucked with by afDoFinalFogPass
and then you could get rid of all this glPushAttrib and glPopMatrix
garbage. */
glPushAttrib(GL_PIXEL_MODE_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT |
GL_TRANSFORM_BIT | GL_VIEWPORT_BIT | GL_CURRENT_BIT);
/* Reposition the current raster position as location (x,y). */
glMatrixMode(GL_MODELVIEW);
glViewport(x-1, y-1, 2, 2);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glRasterPos2i(0, 0);
/* Definitely don't want fog or depth enabled. */
glDisable(GL_FOG);
glDisable(GL_DEPTH_TEST);
/* The alpha on the glDrawPixels after the pixel path transformation
will be "1 - f" where f is the blending factor described in Section
3.9 of the OpenGL 1.1 specification. */
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
switch (fog_mode) {
case GL_LINEAR:
/* Force red, green, and blue to the fog color. */
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:
/* Force red, green, and blue to the fog color. */
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;
}
/* Read out the depth buffer... */
glReadPixels(x, y, width, height,
GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, buffer);
/* ... and write it back as alpha. */
glDrawPixels(width, height, GL_ALPHA,
GL_UNSIGNED_SHORT, buffer);
/* Restore state saved earlier. */
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glPopAttrib();
}
|