File: addfog.c

package info (click to toggle)
glut 3.7-14
  • links: PTS
  • area: main
  • in suites: woody
  • size: 12,556 kB
  • ctags: 45,170
  • sloc: ansic: 148,716; makefile: 35,208; ada: 2,062; yacc: 473; fortran: 290; lex: 131; csh: 51; sed: 49; sh: 33
file content (296 lines) | stat: -rw-r--r-- 8,704 bytes parent folder | download | duplicates (3)
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();
}