pouët.net

light ray effect

category: code [glöplog]
so it's an old effect but i've never tried implementing it before. i remember reading some demoscene text describing a fast way to do it where you can use log(n) passes to get light rays of length n pixels. apparently i don't remember the details properly as mine turned out ugly. :( does anyone remember this text and where it's from or remember the proper algorithm / math?

here you can see my failed attempt: http://www.flickr.com/photos/blackpawn/4400683472/
added on the 2010-03-02 06:43:12 by blackpawn blackpawn
zoomblur :)
http://www.romancortes.com/ficheros/radial-blur.html

In this case, it is done with 1 pass.

To do it with log(n) passes, you can zoom a texture 100% and 101% and mix them at 50% opacity.

Then, get all and zoom it 102% and mix 50% opacity.

Then get all and zoom it 104% and mix 50% opacity and so on...
added on the 2010-03-02 07:20:53 by texel texel
* In the current hardware, I suppose it is only possible with log(n) passes
added on the 2010-03-02 07:21:18 by texel texel
thanks texel! yeah that's the technique i was talking about though unfortunately none of the details gone into there. what i was hoping to find is the math to get the feedback "weights" correct and still have falloff.

here's the fixed version of my attempt and i found by using a different falloff that what i thought was bugged is actually correct but ugly shapes caused by the letters. (those horns the 'o' and some letters can get yuck)

http://www.flickr.com/photos/blackpawn/4400042443/
added on the 2010-03-02 07:49:14 by blackpawn blackpawn
there is a thread about it with detailed explanations by ryg afair.
added on the 2010-03-02 09:25:45 by raer raer
added on the 2010-03-02 09:41:13 by raer raer
Read the explanations by ryg this thread.
added on the 2010-03-02 09:58:33 by spinor spinor
blackpawn: What's wrong with the first "failed attempt"? IMO it looks kinda kick-ass... Is it that it don't use log(n) passes or something? That second link looks quite ugly, though...
added on the 2010-03-02 10:08:05 by kusma kusma
this is how I do it:

take image, subsample blur it then run a shader which "smears" pixels radially and outwards. You run that for maybe 10-12 pixels. Add this on top of original image.
added on the 2010-03-02 10:26:54 by Navis Navis
depending on the hardware and platform, I remember we did this on Amiga by drawing lines from the center and out to the screen border. Start color was 255 white, and every pixel you move, you subtract, lets say 1 from the color, and every time you move and hit the logo/mask, you subtract ex. 3 from the color - works like a charm and fast on Amiga :)
added on the 2010-03-02 11:10:07 by maytz maytz
I second kusma on this one. good quality on that one!
added on the 2010-03-02 11:18:40 by quisten quisten
lightrays in shockwave

Here is a version I did in shockwave, many years ago - blame shockwaves pixel operations for the speed :)
added on the 2010-03-02 11:18:57 by maytz maytz
in software rendering i divide the screen into four sections (as suggested by kusma).

in gpu rendering i blur along a ray heading towards the center of the screen the further away from center the lighter weight.
Below is my first fragment program / pixel shader :)

Code: !!ARBfp1.0 # this has to be the largest and ugliest zoomblur ever TEMP dir,d,d0,d1,d2,odir,c,czoom,dist; PARAM t[]={-0.08,-0.05,-0.03,-0.02,-0.01,0.01,0.02,0.03,0.05,0.08}; # odir = dir = { dir.x-0.5, dir.y-0.5 } SUB dir, fragment.texcoord, 0.5; MOV odir, dir; # dir.x *= dir.x; dir.y *= dir.y; MUL dir, dir, dir; # d.x = dir.x; d.x += dir.y; d.x = 1.0f/sqrt(d.x); ADD dist.x, dir.x, dir.y; RSQ dist.x, dist.x; # normalize MUL dist.x, dist.x, 0.2; MAX dist.x, 0, dist.x; MIN dist.x, 1, dist.x; # dir.x = odir.x * d.x; dir.y = odir.y * d.x; MUL dir.x, odir.x, dist.x; MUL dir.y, odir.y, dist.x; # czoom = pixel( uv+dir*t[0] ) MUL d, dir, t[0]; ADD d, d, fragment.texcoord; TEX c, d, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[1] ) MUL d, dir, t[1]; ADD d, d, fragment.texcoord; TEX c, d, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[2] ) MUL d, dir, t[2]; ADD d, d, fragment.texcoord; TEX c, d, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d0, dir, t[3]; ADD d0, d0, fragment.texcoord; TEX c, d0, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d0, dir, t[3]; ADD d0, d0, fragment.texcoord; TEX c, d0, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d0, dir, t[4]; ADD d0, d0, fragment.texcoord; TEX c, d0, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d1, dir, t[5]; ADD d1, d1, fragment.texcoord; TEX c, d1, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d1, dir, t[6]; ADD d1, d1, fragment.texcoord; TEX c, d1, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d1, dir, t[7]; ADD d1, d1, fragment.texcoord; TEX c, d1, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d2, dir, t[8]; ADD d2, d2, fragment.texcoord; TEX c, d2, texture, 2D; ADD czoom, czoom, c; # czoom = pixel( uv+dir*t[3] ) MUL d2, dir, t[9]; ADD d2, d2, fragment.texcoord; TEX c, d2, texture, 2D; ADD czoom, czoom, c; # first pixel TEX c, fragment.texcoord, texture, 2D; # rgb mul MOV dist.y, dist.x; MOV dist.z, dist.x; MUL c, c, dist; # invert dist and blend SUB dist.x, 1, dist.x; MOV dist.y, dist.x; MOV dist.z, dist.x; MUL czoom, czoom, dist; MUL czoom, czoom, 0.125; # combine and return ADD result.color, c, czoom; END
best version I've seen is in live-evil (software)
added on the 2010-03-02 12:29:50 by Navis Navis
what kusma and macaw said.

i found this on my hd. it renders one block if you divide the screen in four sections. may probably optimize even further. im not sure if it works though, didnt have time to test it.

Code: //fixedpoint version void block(scrbuffer tobuffer, int cx, int cy, int dx, int dy, double scalefactor) { int x,y,fx,fy; iVector cv; int ax,ay,ax1,ay1; int px,py,px1,py1; int paddr,aaddr; g_irgb p,p1,p8,p9,a,a1; //Fixedpoint fixed fx_scale; iVector fx_cv, fr_cv; fx_scale=DTOFIX(scalefactor); //finn ut hvilket fortegn variabelen skal bruke i loopen if (cx<dx) fx=1; else fx=-1; if (cy<dy) fy=1; else fy=-1; for (y=cy; fy<0?y>=dy:y<dy; y+=fy) for (x=cx; fx<0?x>=dx:x<dx; x+=fx) { //vektoren som peker mot midten av radial bluren kaller vi cv cv.x=cx-x; cv.y=cy-y; fx_cv.x=ITOFIX(cv.x); fx_cv.y=ITOFIX(cv.y); //skaler vektoren // cv.x=(int)(((double)cv.x)*scalefactor); // cv.y=(int)(((double)cv.y)*scalefactor); fx_cv.x=fixed_mul(fx_cv.x, fx_scale); fx_cv.y=fixed_mul(fx_cv.y, fx_scale); cv.x=FIXTOI(fx_cv.x); cv.y=FIXTOI(fx_cv.y); //finn fraksjonsdelen til vektoren (NY) fr_cv.x=(((fx_cv.x)>>8)&0xff); fr_cv.y=(((fx_cv.y)>>8)&0xff); //pixelen vi vil skrive til kaller vi for a. //og dens koordinater kaller vi ax og ay. ax=x; ay=y; ax1=x-1; ay1=y-1; px=ax-cv.x; py=ay-cv.y; px1=ax1-cv.x; py1=ay1-cv.y; //blend p(x,y) -> p(x+1,y) med fraksjonsparten til x if (px>=0 && px<WIDTH && py>=0 && py<HEIGHT) paddr=((px+yaddr[py])<<2); p.r=tobuffer.cbuffer[2+paddr]; p.g=tobuffer.cbuffer[1+paddr]; p.b=tobuffer.cbuffer[paddr]; if (px1>=0 && px1<WIDTH && py>=0 && py<HEIGHT) paddr=((px1+yaddr[py])<<2); p1.r=tobuffer.cbuffer[2+paddr]; p1.g=tobuffer.cbuffer[1+paddr]; p1.b=tobuffer.cbuffer[paddr]; p8.r=p.r+(((p1.r-p.r)*fr_cv.x)>>8); p8.g=p.g+(((p1.g-p.g)*fr_cv.x)>>8); p8.b=p.b+(((p1.b-p.b)*fr_cv.x)>>8); //blend p(x,y+1) -> p(x+1,y+1) med fraksjonsparten til x if (px>=0 && px<WIDTH && py1>=0 && py1<HEIGHT) paddr=((px+yaddr[py1])<<2); p.r=tobuffer.cbuffer[2+paddr]; p.g=tobuffer.cbuffer[1+paddr]; p.b=tobuffer.cbuffer[paddr]; if (px1>=0 && px1<WIDTH && py1>=0 && py1<HEIGHT) paddr=((px1+yaddr[py1])<<2); p1.r=tobuffer.cbuffer[2+paddr]; p1.g=tobuffer.cbuffer[1+paddr]; p1.b=tobuffer.cbuffer[paddr]; p9.r=p.r+(((p1.r-p.r)*fr_cv.x)>>8); p9.g=p.g+(((p1.g-p.g)*fr_cv.x)>>8); p9.b=p.b+(((p1.b-p.b)*fr_cv.x)>>8); //blend sÕ p8 og p9 med fraksjonsparten til y p.r=p8.r+(((p9.r-p8.r)*fr_cv.y)>>8); p.g=p8.g+(((p9.g-p8.g)*fr_cv.y)>>8); p.b=p8.b+(((p9.b-p8.b)*fr_cv.y)>>8); //bland fargen til pixel p med a tobuffer.buffer[ax+yaddr[ay]]=RGB32(p.r, p.g, p.b); } }
added on the 2010-03-02 14:24:21 by rudi rudi
I used to do it in software in 1 pass with IIR filtering (four to eight different filters, depending on the direction).
I think the best you can do in hw is logn, anyway.
added on the 2010-03-02 21:10:42 by pan pan
back in the day, I was quite satisfied with my mmx-zoomblur :)
burra's radial blur is the best zoomblur ever!
added on the 2010-03-02 22:42:16 by spite spite
thanks for all the responses guys! i'll have to read through all the references after work tonight.

here's where i left it last night: http://www.flickr.com/photos/blackpawn/4400089309/ i'm happy with the look after i realized the wacky horns on the 'o' and such were actually the right thing. it's not doing the log N type action though so i imagine it's quite a bit slower than it could be.

wow that thread http://pouet.net/topic.php?which=6117 looks epic! can't wait to read through it all. XD
added on the 2010-03-02 23:39:17 by blackpawn blackpawn
blackspawn: it looks damn nice.
added on the 2010-03-03 00:12:56 by rudi rudi
Maybe you can get rid of the horns by using HDR lighting (i.e. exposure function), so the more bright parts don't just go all the way up to white and then hit the ceiling.
added on the 2010-03-03 00:47:16 by yesso yesso
the horns are simply what you get when you normalize the direction. just don't do it. if you just use center+(pos-center)*scale to determine the sampling position, the problem disappears.
added on the 2010-03-03 01:03:58 by ryg ryg
ossom!
added on the 2010-03-03 01:30:59 by pan pan

login