1 /* glblur --- radial blur using GL textures
2 * Copyright (c) 2002-2014 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
12 * This program draws a box and a few line segments, and generates a flowing
13 * radial blur outward from it -- this causes flowing field effects.
14 * It does this by rendering the scene into a small texture, then repeatedly
15 * rendering increasingly-enlarged and increasingly-transparent versions of
16 * that texture onto the frame buffer.
18 * As such, it's quite graphics intensive -- don't bother trying to run this
19 * if you don't have hardware-accelerated texture support.
21 * Inspired by Dario Corno's Radial Blur tutorial:
22 * http://nehe.gamedev.net/tutorials/lesson.asp?l=36
25 #define DEFAULTS "*delay: 10000 \n" \
26 "*showFPS: False \n" \
27 "*fpsSolid: True \n" \
28 "*suppressRotationAnimation: True\n" \
30 # define refresh_glblur 0
31 # define release_glblur 0
33 #define countof(x) (sizeof((x))/sizeof((*x)))
36 #define ABS(n) ((n)<0?-(n):(n))
38 #define SIGNOF(n) ((n)<0?-1:1)
40 #include "xlockmore.h"
43 #include "gltrackball.h"
46 #ifdef USE_GL /* whole file */
48 #define DEF_SPIN "XYZ"
49 #define DEF_WANDER "True"
50 #define DEF_BLUR_SIZE "15"
52 typedef struct metaball metaball;
56 GLXContext *glx_context;
58 trackball_state *trackball;
61 GLuint obj_dlist0; /* east-west cube faces */
62 GLuint obj_dlist1; /* north-south cube faces */
63 GLuint obj_dlist2; /* up-down cube faces */
64 GLuint obj_dlist3; /* spikes coming out of the cube's corners */
65 GLuint scene_dlist1; /* the cube, rotated and translated */
66 GLuint scene_dlist2; /* the spikes, rotated and translated */
67 int scene_polys1; /* polygons in scene, not counting texture overlay */
68 int scene_polys2; /* polygons in scene, not counting texture overlay */
71 unsigned int *tex_data;
84 } glblur_configuration;
86 static glblur_configuration *bps = NULL;
89 static Bool do_wander;
92 static XrmOptionDescRec opts[] = {
93 { "-spin", ".spin", XrmoptionSepArg, 0 },
94 { "+spin", ".spin", XrmoptionNoArg, "" },
95 { "-blursize", ".blurSize", XrmoptionSepArg, 0 },
96 { "-wander", ".wander", XrmoptionNoArg, "True" },
97 { "+wander", ".wander", XrmoptionNoArg, "False" },
100 static argtype vars[] = {
101 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
102 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
103 {&blursize, "blurSize","BlurSize", DEF_BLUR_SIZE, t_Int},
106 ENTRYPOINT ModeSpecOpt glblur_opts = {countof(opts), opts, countof(vars), vars, NULL};
109 /* Window management, etc
112 reshape_glblur (ModeInfo *mi, int width, int height)
114 GLfloat h = (GLfloat) height / (GLfloat) width;
116 glViewport (0, 0, (GLint) width, (GLint) height);
118 glMatrixMode(GL_PROJECTION);
120 gluPerspective (30.0, 1/h, 1.0, 100.0);
122 glMatrixMode(GL_MODELVIEW);
124 gluLookAt( 0.0, 0.0, 8.0,
128 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
130 int o = (int) current_device_rotation();
131 if (o != 0 && o != 180 && o != -180)
132 glScalef (1/h, 1/h, 1/h);
136 glClear(GL_COLOR_BUFFER_BIT);
141 /* Objects in the scene
145 generate_object (ModeInfo *mi)
147 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
148 Bool wire = MI_IS_WIREFRAME (mi);
151 bp->scene_polys1 = 0;
152 bp->scene_polys2 = 0;
154 glNewList (bp->obj_dlist0, GL_COMPILE);
155 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* front */
156 glNormal3f (0, 0, 1);
157 glTexCoord2f(1, 0); glVertex3f ( 0.5, -0.5, 0.5);
158 glTexCoord2f(0, 0); glVertex3f ( 0.5, 0.5, 0.5);
159 glTexCoord2f(0, 1); glVertex3f (-0.5, 0.5, 0.5);
160 glTexCoord2f(1, 1); glVertex3f (-0.5, -0.5, 0.5);
164 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* back */
165 glNormal3f (0, 0, -1);
166 glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
167 glTexCoord2f(0, 1); glVertex3f (-0.5, 0.5, -0.5);
168 glTexCoord2f(1, 1); glVertex3f ( 0.5, 0.5, -0.5);
169 glTexCoord2f(1, 0); glVertex3f ( 0.5, -0.5, -0.5);
174 glNewList (bp->obj_dlist1, GL_COMPILE);
175 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* left */
176 glNormal3f (-1, 0, 0);
177 glTexCoord2f(1, 1); glVertex3f (-0.5, 0.5, 0.5);
178 glTexCoord2f(1, 0); glVertex3f (-0.5, 0.5, -0.5);
179 glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
180 glTexCoord2f(0, 1); glVertex3f (-0.5, -0.5, 0.5);
184 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* right */
185 glNormal3f (1, 0, 0);
186 glTexCoord2f(1, 1); glVertex3f ( 0.5, -0.5, -0.5);
187 glTexCoord2f(1, 0); glVertex3f ( 0.5, 0.5, -0.5);
188 glTexCoord2f(0, 0); glVertex3f ( 0.5, 0.5, 0.5);
189 glTexCoord2f(0, 1); glVertex3f ( 0.5, -0.5, 0.5);
194 glNewList (bp->obj_dlist2, GL_COMPILE);
195 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* top */
196 glNormal3f (0, 1, 0);
197 glTexCoord2f(0, 0); glVertex3f ( 0.5, 0.5, 0.5);
198 glTexCoord2f(0, 1); glVertex3f ( 0.5, 0.5, -0.5);
199 glTexCoord2f(1, 1); glVertex3f (-0.5, 0.5, -0.5);
200 glTexCoord2f(1, 0); glVertex3f (-0.5, 0.5, 0.5);
204 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* bottom */
205 glNormal3f (0, -1, 0);
206 glTexCoord2f(1, 0); glVertex3f (-0.5, -0.5, 0.5);
207 glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
208 glTexCoord2f(0, 1); glVertex3f ( 0.5, -0.5, -0.5);
209 glTexCoord2f(1, 1); glVertex3f ( 0.5, -0.5, 0.5);
214 glNewList (bp->obj_dlist3, GL_COMPILE);
217 glVertex3f(-s, 0, 0); glVertex3f(s, 0, 0); /* face spikes */
218 glVertex3f(0, -s, 0); glVertex3f(0, s, 0);
219 glVertex3f(0, 0, -s); glVertex3f(0, 0, s);
220 bp->scene_polys2 += 3;
225 glVertex3f(-s, -s, -s); glVertex3f( s, s, s); /* corner spikes */
226 glVertex3f(-s, -s, s); glVertex3f( s, s, -s);
227 glVertex3f(-s, s, -s); glVertex3f( s, -s, s);
228 glVertex3f( s, -s, -s); glVertex3f(-s, s, s);
229 bp->scene_polys2 += 4;
233 check_gl_error ("object generation");
238 init_texture (ModeInfo *mi)
240 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
242 if (bp->tex_data) free (bp->tex_data);
246 bp->tex_data = (unsigned int *)
247 malloc (bp->tex_w * bp->tex_h * 4 * sizeof (unsigned int));
249 glGenTextures (1, &bp->texture);
250 glBindTexture (GL_TEXTURE_2D, bp->texture);
251 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
252 bp->tex_w, bp->tex_h, 0,
254 /* GL_UNSIGNED_BYTE, */
255 GL_UNSIGNED_INT_8_8_8_8_REV,
257 check_gl_error ("texture generation");
258 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
259 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
264 render_scene_to_texture (ModeInfo *mi)
266 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
268 glViewport (0, 0, bp->tex_w, bp->tex_h);
270 glCallList (bp->scene_dlist1);
271 glCallList (bp->scene_dlist2);
273 glBindTexture (GL_TEXTURE_2D, bp->texture);
274 glCopyTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0,
275 bp->tex_w, bp->tex_h, 0);
276 check_gl_error ("texture2D");
278 glViewport (0, 0, MI_WIDTH(mi), MI_HEIGHT(mi));
282 overlay_blur_texture (ModeInfo *mi)
284 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
285 int w = MI_WIDTH (mi);
286 int h = MI_HEIGHT (mi);
287 int times = blursize;
289 GLfloat inc = 0.02 * (25.0 / times);
291 GLfloat spost = 0; /* starting texture coordinate offset */
292 GLfloat alpha_inc; /* transparency fade factor */
293 GLfloat alpha = 0.2; /* initial transparency */
295 glEnable (GL_TEXTURE_2D);
296 glDisable (GL_DEPTH_TEST);
297 glBlendFunc (GL_SRC_ALPHA,GL_ONE);
299 glBindTexture (GL_TEXTURE_2D, bp->texture);
302 /* switch to orthographic projection, saving both previous matrixes
303 on their respective stacks.
305 glMatrixMode (GL_PROJECTION);
308 glOrtho (0, w, h, 0, -1, 1);
309 glMatrixMode (GL_MODELVIEW);
314 alpha_inc = alpha / times;
316 mi->polygon_count = bp->scene_polys1 + bp->scene_polys2;
319 for (i = 0; i < times; i++)
321 glColor4f (1, 1, 1, alpha);
322 glTexCoord2f (0+spost, 1-spost); glVertex2f (0, 0);
323 glTexCoord2f (0+spost, 0+spost); glVertex2f (0, h);
324 glTexCoord2f (1-spost, 0+spost); glVertex2f (w, h);
325 glTexCoord2f (1-spost, 1-spost); glVertex2f (w, 0);
332 /* Switch back to perspective projection, restoring the saved matrixes
334 glMatrixMode (GL_PROJECTION);
336 glMatrixMode (GL_MODELVIEW);
339 glEnable (GL_DEPTH_TEST);
340 glDisable (GL_BLEND);
341 glDisable (GL_TEXTURE_2D);
342 glBindTexture (GL_TEXTURE_2D, 0);
347 /* Startup initialization
351 glblur_handle_event (ModeInfo *mi, XEvent *event)
353 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
355 if (gltrackball_event_handler (event, bp->trackball,
356 MI_WIDTH (mi), MI_HEIGHT (mi),
365 init_glblur (ModeInfo *mi)
367 glblur_configuration *bp;
368 int wire = MI_IS_WIREFRAME(mi);
370 MI_INIT (mi, bps, NULL);
372 bp = &bps[MI_SCREEN(mi)];
374 bp->glx_context = init_GL(mi);
376 reshape_glblur (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
377 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
381 GLfloat gamb[4]= {0.2, 0.2, 0.2, 1.0};
382 GLfloat pos[4] = {0.0, 5.0, 10.0, 1.0};
383 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
384 GLfloat dif[4] = {0.3, 0.3, 0.3, 1.0};
385 GLfloat spc[4] = {0.8, 0.8, 0.8, 1.0};
388 glEnable(GL_LIGHTING);
391 glEnable(GL_DEPTH_TEST);
392 glEnable(GL_CULL_FACE);
393 glEnable(GL_NORMALIZE);
394 glShadeModel(GL_SMOOTH);
396 glLightModelfv (GL_LIGHT_MODEL_AMBIENT, gamb);
398 glLightfv(GL_LIGHT0, GL_POSITION, pos);
399 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
400 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
401 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
403 glEnable(GL_LIGHTING);
406 glMaterialf(GL_FRONT, GL_SHININESS, shiny);
410 Bool spinx=False, spiny=False, spinz=False;
411 double spin_speed = 0.9;
412 double wander_speed = 0.06;
417 if (*s == 'x' || *s == 'X') spinx = True;
418 else if (*s == 'y' || *s == 'Y') spiny = True;
419 else if (*s == 'z' || *s == 'Z') spinz = True;
420 else if (*s == '0') ;
424 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
431 bp->rot = make_rotator (spinx ? spin_speed : 0,
432 spiny ? spin_speed : 0,
433 spinz ? spin_speed : 0,
435 do_wander ? wander_speed : 0,
437 bp->trackball = gltrackball_init (True);
440 if (blursize < 0) blursize = 0;
441 if (blursize > 200) blursize = 200;
444 bp->colors0 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
445 bp->colors1 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
446 bp->colors2 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
447 bp->colors3 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
448 make_smooth_colormap (0, 0, 0, bp->colors0, &bp->ncolors, False, 0, False);
449 make_smooth_colormap (0, 0, 0, bp->colors1, &bp->ncolors, False, 0, False);
450 make_smooth_colormap (0, 0, 0, bp->colors2, &bp->ncolors, False, 0, False);
451 make_smooth_colormap (0, 0, 0, bp->colors3, &bp->ncolors, False, 0, False);
454 bp->obj_dlist0 = glGenLists (1);
455 bp->obj_dlist1 = glGenLists (1);
456 bp->obj_dlist2 = glGenLists (1);
457 bp->obj_dlist3 = glGenLists (1);
458 bp->scene_dlist1 = glGenLists (1);
459 bp->scene_dlist2 = glGenLists (1);
463 generate_object (mi);
470 draw_glblur (ModeInfo *mi)
472 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
473 Display *dpy = MI_DISPLAY(mi);
474 Window window = MI_WINDOW(mi);
476 GLfloat color0[4] = {0.0, 0.0, 0.0, 1.0};
477 GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
478 GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
479 GLfloat color3[4] = {0.0, 0.0, 0.0, 1.0};
480 GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
486 if (!bp->glx_context)
489 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
491 /* Decide what we're drawing
493 if (0 == (random() % 30))
495 bp->show_cube_p = (0 == (random() % 10));
496 bp->show_spikes_p = (0 == (random() % 20));
499 /* Select new colors for the various objects
501 color0[0] = bp->colors0[bp->ccolor].red / 65536.0;
502 color0[1] = bp->colors0[bp->ccolor].green / 65536.0;
503 color0[2] = bp->colors0[bp->ccolor].blue / 65536.0;
505 color1[0] = bp->colors1[bp->ccolor].red / 65536.0;
506 color1[1] = bp->colors1[bp->ccolor].green / 65536.0;
507 color1[2] = bp->colors1[bp->ccolor].blue / 65536.0;
509 color2[0] = bp->colors2[bp->ccolor].red / 65536.0;
510 color2[1] = bp->colors2[bp->ccolor].green / 65536.0;
511 color2[2] = bp->colors2[bp->ccolor].blue / 65536.0;
513 color3[0] = bp->colors3[bp->ccolor].red / 65536.0;
514 color3[1] = bp->colors3[bp->ccolor].green / 65536.0;
515 color3[2] = bp->colors3[bp->ccolor].blue / 65536.0;
518 if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
521 get_position (bp->rot, &px, &py, &pz, !bp->button_down_p);
522 get_rotation (bp->rot, &rx, &ry, &rz, !bp->button_down_p);
531 /* Generate scene_dlist1, which contains the box (not spikes),
532 rotated into position.
534 glNewList (bp->scene_dlist1, GL_COMPILE);
536 glMatrixMode (GL_MODELVIEW);
538 glTranslatef (px, py, pz);
539 gltrackball_rotate (bp->trackball);
540 glRotatef (rx, 1.0, 0.0, 0.0);
541 glRotatef (ry, 0.0, 1.0, 0.0);
542 glRotatef (rz, 0.0, 0.0, 1.0);
544 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
546 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
547 glCallList (bp->obj_dlist0);
549 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
550 glCallList (bp->obj_dlist1);
552 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
553 glCallList (bp->obj_dlist2);
555 glMatrixMode (GL_MODELVIEW);
561 /* Generate scene_dlist2, which contains the spikes (not box),
562 rotated into position.
564 glNewList (bp->scene_dlist2, GL_COMPILE);
566 glMatrixMode (GL_MODELVIEW);
568 glTranslatef (px, py, pz);
569 gltrackball_rotate (bp->trackball);
570 glRotatef (rx, 1.0, 0.0, 0.0);
571 glRotatef (ry, 0.0, 1.0, 0.0);
572 glRotatef (rz, 0.0, 0.0, 1.0);
574 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color3);
575 glCallList (bp->obj_dlist3);
577 glMatrixMode (GL_MODELVIEW);
583 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
585 render_scene_to_texture (mi);
587 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
589 if (bp->show_cube_p || bp->button_down_p)
591 glCallList (bp->scene_dlist1);
592 extra_polys += bp->scene_polys1;
594 if (bp->show_spikes_p || bp->button_down_p)
596 glCallList (bp->scene_dlist2);
597 extra_polys += bp->scene_polys2;
600 overlay_blur_texture (mi);
601 mi->polygon_count += extra_polys;
605 if (mi->fps_p) do_fps (mi);
608 glXSwapBuffers(dpy, window);
611 XSCREENSAVER_MODULE ("GLBlur", glblur)