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/
here you can see my failed attempt: http://www.flickr.com/photos/blackpawn/4400683472/
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...
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...
* In the current hardware, I suppose it is only possible with log(n) passes
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/
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/
there is a thread about it with detailed explanations by ryg afair.
And this maybe.
Read the explanations by ryg this thread.
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...
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.
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.
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 :)
I second kusma on this one. good quality on that one!
lightrays in shockwave
Here is a version I did in shockwave, many years ago - blame shockwaves pixel operations for the speed :)
Here is a version I did in shockwave, many years ago - blame shockwaves pixel operations for the speed :)
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.
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)
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.
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);
}
}
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.
I think the best you can do in hw is logn, anyway.
back in the day, I was quite satisfied with my mmx-zoomblur :)
burra's radial blur is the best zoomblur ever!
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
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
blackspawn: it looks damn nice.
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.
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.
ossom!