Forum archive
EvalDraw
- [Ken, there's a small bug in EvalDraw's filename handling. If you load a picture with Ctrl-P, it uses the picture filename as the name of the program. If you load a picture, then save the program, you could inadvertantly overwrite the picture]
I had a bit of spare time on my hands, and I've had a go at writing a simple raytracing program in EvalDraw, showing a metallic sphere on a patterned floor. It's interesting to zoom in on the reflection in the sphere and see it in more detail.
I've tried to make efficient, but it's quite slow; the faster your computer, the better.(x,y,&r,&g,&b)
if(x>4||x<-4||y>3||y<-3)r=0,g=0,b=0,return 0;
rx=0;ry=1.3;rz=-.999; //ray position vector (set to focal point)
rdx=x; rdy=y; rdz=5; //ray direction
rdl=sqr(rdx^2+rdy^2+rdz^2); //find length
rdx/=rdl; rdy/=rdl; rdz/=rdl; //normalize
sr=1; sx=0; sy=sr+.01; sz=4; //sphere position, radius
fy=0; // floor height
ri=1; //ray intensity
rcr=0;rcg=0;rcb=0; //ray color
while (ri > 1/256) //trace ray until too dim
{
//distance to floor
fdist=0;
if (sgn(rdy) && sgn(rdy)==sgn(fy-ry))
{
fdist=(fy-ry)/rdy;
}
//distance to sphere
dx=sx-rx; dy=sy-ry; dz=sz-rz;
rddotd=rdx*dx+rdy*dy+rdz*dz;
sdist=0;
if (rddotd > 0) //ray pointing roughly toward sphere
{
tosqr=sr^2-(dx^2+dy^2+dz^2)+rddotd^2;
if (tosqr>0)
{
sdist=rddotd-sqr(tosqr);
}
}
if (sdist > 0 && (sdist < fdist || fdist <= 0)) //ray hits sphere first
{
rx+=rdx*sdist; //move ray to sphere
ry+=rdy*sdist;
rz+=rdz*sdist;
dx=sx-rx; dy=sy-ry; dz=sz-rz;
rddotd=rdx*dx+rdy*dy+rdz*dz;
dll=dx^2+dy^2+dz^2;
mult=2*rddotd/dll;
rdx-=dx*mult;
rdy-=dy*mult;
rdz-=dz*mult;
}
else if (fdist > 0) //ray hits floor first
{
rx+=rdx*fdist; //move ray to floor
ry=fy;
rz+=rdz*fdist;
rdy=-rdy; //reflect ray in floor
i=.75; //floor 'opacity'
iri=i*ri;
if(0) //floor color: 1=checkerboard, 0=pic
{
c=255*((floor(rx)+floor(rz))%2);
ciri=c*iri;
rcr=(1-iri)*rcr+ciri;
rcg=(1-iri)*rcg+ciri;
rcb=(1-iri)*rcb+ciri;
}
else
{
p=pic(-rx*32,-rz*32);
cb=p%256;
cg=(p/256)%256;
cr=(p/65536);
rcr=(1-iri)*rcr+iri*cr;
rcg=(1-iri)*rcg+iri*cg;
rcb=(1-iri)*rcb+iri*cb;
}
ri*=(1-i);
}
else // ray goes into sky
{
cr=127+128*rdy;
cg=191+64*rdy;
cb=223+32*rdy;
rcr+=ri*(cr-rcr);
rcg+=ri*(cg-rcg);
rcb+=ri*(cb-rcb);
ri=0;
}
}
r=rcr;g=rcg;b=rcb;
return 0; - Thanks for the bug report - I have updated EVALDRAW on my website with a fix.
Nice code! Here is some optimization advice:Remove: (no need for this)
if(x>4||x<-4||y>3||y<-3)r=0,g=0,b=0,return 0;
Change: (divides are much slower than multiplies)
rdl=sqr(rdx^2+rdy^2+rdz^2); //find length
rdx/=rdl; rdy/=rdl; rdz/=rdl; //normalize
To:
rdl=1/sqr(rdx^2+rdy^2+rdz^2); //find length
rdx*=rdl; rdy*=rdl; rdz*=rdl; //normalize
Change: (no need to test before entering the loop)
while (ri > 1/256) //trace ray until too dim
{
...
}
To:
do
{
...
} while (ri > 1/256); //trace ray until too dim
Change:
fdist=0;
if (sgn(rdy) && sgn(rdy)==sgn(fy-ry))
{
fdist=(fy-ry)/rdy;
}
To:
if (fy < ry) fdist=(fy-ry)/rdy; else fdist = 0;
Change: (put signs before constants to avoid extra negation)
p=pic(-rx*32,-rz*32);
To:
p=pic(rx*32,rz*-32); //Note: I made x negative from yours - more sense this way : )
Change: (mod is slow; I use a little trick instead : )
cb=p%256;
cg=(p/256)%256;
cr=(p/65536);
To:
cr = p/65536; k = 3*2^59;
cb = p-(p-127.998+k-k); p /= 256;
cg = p-(p-127.998+k-k);
(someday, I'll add support for pic(x,y,&r,&g,&b), in which case the above mess won't be necessary : )
Change:
rcr=(1-iri)*rcr+iri*cr;
rcg=(1-iri)*rcg+iri*cg;
rcb=(1-iri)*rcb+iri*cb;
To:
rcr+=(cr-rcr)*iri;
rcg+=(cg-rcg)*iri;
rcb+=(cb-rcb)*iri; - It is a little bit different from the original thread theme, but i was wondering if it could be possible to directly to render spheres, with correct deep map(z-buffer) without using tessellation. Or to make it much more complicate by using scanline algorithm.
- Spheres are rendered quite easily and accurately with raytracing. With raytracing, you don't need a depth buffer - you simply pick the closest intersection for each stage of a ray. You are probably asking whether it's possible to do this in realtime. If it was 10 years ago, the answer would be no, not really. Today, however, most computers are fast enough to do it in real-time, thanks to built-in FPUs, SIMD, HyperThread support, etc. If you want an example, check out this engine:
http://www.virtualray.ru/
EVALDRAW can be used for real-time raytracing too. It's not specifically optimized for it, but it works ok for quick tests. If you want to have some fun, try changing counting_pine's script at the top of this thread to "(x,y,t,&r,&g,&b)" mode and add some cos/sin to make things move around. : ) - I already knew virtualray. It is quit cool, but i think of another approach. Well i better gotta do more research on my own. I will let you knew if i found something.