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);
371 bps = (glblur_configuration *)
372 calloc (MI_NUM_SCREENS(mi), sizeof (glblur_configuration));
374 fprintf(stderr, "%s: out of memory\n", progname);
379 bp = &bps[MI_SCREEN(mi)];
381 bp->glx_context = init_GL(mi);
383 reshape_glblur (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
384 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
388 GLfloat gamb[4]= {0.2, 0.2, 0.2, 1.0};
389 GLfloat pos[4] = {0.0, 5.0, 10.0, 1.0};
390 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
391 GLfloat dif[4] = {0.3, 0.3, 0.3, 1.0};
392 GLfloat spc[4] = {0.8, 0.8, 0.8, 1.0};
395 glEnable(GL_LIGHTING);
398 glEnable(GL_DEPTH_TEST);
399 glEnable(GL_CULL_FACE);
400 glEnable(GL_NORMALIZE);
401 glShadeModel(GL_SMOOTH);
403 glLightModelfv (GL_LIGHT_MODEL_AMBIENT, gamb);
405 glLightfv(GL_LIGHT0, GL_POSITION, pos);
406 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
407 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
408 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
410 glEnable(GL_LIGHTING);
413 glMaterialf(GL_FRONT, GL_SHININESS, shiny);
417 Bool spinx=False, spiny=False, spinz=False;
418 double spin_speed = 0.9;
419 double wander_speed = 0.06;
424 if (*s == 'x' || *s == 'X') spinx = True;
425 else if (*s == 'y' || *s == 'Y') spiny = True;
426 else if (*s == 'z' || *s == 'Z') spinz = True;
427 else if (*s == '0') ;
431 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
438 bp->rot = make_rotator (spinx ? spin_speed : 0,
439 spiny ? spin_speed : 0,
440 spinz ? spin_speed : 0,
442 do_wander ? wander_speed : 0,
444 bp->trackball = gltrackball_init (True);
447 if (blursize < 0) blursize = 0;
448 if (blursize > 200) blursize = 200;
451 bp->colors0 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
452 bp->colors1 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
453 bp->colors2 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
454 bp->colors3 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
455 make_smooth_colormap (0, 0, 0, bp->colors0, &bp->ncolors, False, 0, False);
456 make_smooth_colormap (0, 0, 0, bp->colors1, &bp->ncolors, False, 0, False);
457 make_smooth_colormap (0, 0, 0, bp->colors2, &bp->ncolors, False, 0, False);
458 make_smooth_colormap (0, 0, 0, bp->colors3, &bp->ncolors, False, 0, False);
461 bp->obj_dlist0 = glGenLists (1);
462 bp->obj_dlist1 = glGenLists (1);
463 bp->obj_dlist2 = glGenLists (1);
464 bp->obj_dlist3 = glGenLists (1);
465 bp->scene_dlist1 = glGenLists (1);
466 bp->scene_dlist2 = glGenLists (1);
470 generate_object (mi);
477 draw_glblur (ModeInfo *mi)
479 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
480 Display *dpy = MI_DISPLAY(mi);
481 Window window = MI_WINDOW(mi);
483 GLfloat color0[4] = {0.0, 0.0, 0.0, 1.0};
484 GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
485 GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
486 GLfloat color3[4] = {0.0, 0.0, 0.0, 1.0};
487 GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
493 if (!bp->glx_context)
496 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
498 /* Decide what we're drawing
500 if (0 == (random() % 30))
502 bp->show_cube_p = (0 == (random() % 10));
503 bp->show_spikes_p = (0 == (random() % 20));
506 /* Select new colors for the various objects
508 color0[0] = bp->colors0[bp->ccolor].red / 65536.0;
509 color0[1] = bp->colors0[bp->ccolor].green / 65536.0;
510 color0[2] = bp->colors0[bp->ccolor].blue / 65536.0;
512 color1[0] = bp->colors1[bp->ccolor].red / 65536.0;
513 color1[1] = bp->colors1[bp->ccolor].green / 65536.0;
514 color1[2] = bp->colors1[bp->ccolor].blue / 65536.0;
516 color2[0] = bp->colors2[bp->ccolor].red / 65536.0;
517 color2[1] = bp->colors2[bp->ccolor].green / 65536.0;
518 color2[2] = bp->colors2[bp->ccolor].blue / 65536.0;
520 color3[0] = bp->colors3[bp->ccolor].red / 65536.0;
521 color3[1] = bp->colors3[bp->ccolor].green / 65536.0;
522 color3[2] = bp->colors3[bp->ccolor].blue / 65536.0;
525 if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
528 get_position (bp->rot, &px, &py, &pz, !bp->button_down_p);
529 get_rotation (bp->rot, &rx, &ry, &rz, !bp->button_down_p);
538 /* Generate scene_dlist1, which contains the box (not spikes),
539 rotated into position.
541 glNewList (bp->scene_dlist1, GL_COMPILE);
543 glMatrixMode (GL_MODELVIEW);
545 glTranslatef (px, py, pz);
546 gltrackball_rotate (bp->trackball);
547 glRotatef (rx, 1.0, 0.0, 0.0);
548 glRotatef (ry, 0.0, 1.0, 0.0);
549 glRotatef (rz, 0.0, 0.0, 1.0);
551 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
553 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
554 glCallList (bp->obj_dlist0);
556 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
557 glCallList (bp->obj_dlist1);
559 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
560 glCallList (bp->obj_dlist2);
562 glMatrixMode (GL_MODELVIEW);
568 /* Generate scene_dlist2, which contains the spikes (not box),
569 rotated into position.
571 glNewList (bp->scene_dlist2, GL_COMPILE);
573 glMatrixMode (GL_MODELVIEW);
575 glTranslatef (px, py, pz);
576 gltrackball_rotate (bp->trackball);
577 glRotatef (rx, 1.0, 0.0, 0.0);
578 glRotatef (ry, 0.0, 1.0, 0.0);
579 glRotatef (rz, 0.0, 0.0, 1.0);
581 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color3);
582 glCallList (bp->obj_dlist3);
584 glMatrixMode (GL_MODELVIEW);
590 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
592 render_scene_to_texture (mi);
594 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
596 if (bp->show_cube_p || bp->button_down_p)
598 glCallList (bp->scene_dlist1);
599 extra_polys += bp->scene_polys1;
601 if (bp->show_spikes_p || bp->button_down_p)
603 glCallList (bp->scene_dlist2);
604 extra_polys += bp->scene_polys2;
607 overlay_blur_texture (mi);
608 mi->polygon_count += extra_polys;
612 if (mi->fps_p) do_fps (mi);
615 glXSwapBuffers(dpy, window);
618 XSCREENSAVER_MODULE ("GLBlur", glblur)