Dark Bit Factory & Gravity

PROGRAMMING => Freebasic => Topic started by: Stonemonkey on August 09, 2007

Title: Another raytracer.
Post by: Stonemonkey on August 09, 2007
Since the discussion in blitz about raytracing i've given it a little go. It's not got shadows yet and still needs some work on shading.

Code: [Select]
option explicit

const magic=6755399441055744.0

union float_to_int_conversion
    _in as double
    _out as integer
end union

union recip_sqr_conversion
    _float as single
    _int as integer
end union

type sphere_struct
    x as single
    y as single
    z as single
    rad as single
    rad_recip as single
    rad_recip2 as single
    red as single
    gre as single
    blu as single
    reflective as single
end type

type light_struct
    x as single
    y as single
    z as single
    red as single
    gre as single
    blu as single
end type

type vector_struct
    x as single
    y as single
    z as single
end type

type raytrace_struct
    x as single
    y as single
    z as single
    vx as single
    vy as single
    vz as single
    affect as single
    red as single
    gre as single
    blu as single
    sphere_list as sphere_struct pointer
    sphere_count as integer
    current_sphere as sphere_struct pointer
    light_list as light_struct pointer
    light_count as integer
    display as integer pointer
    vectors as vector_struct pointer
end type



sub raytrace (ray as raytrace_struct pointer)

    dim as sphere_struct pointer sphere,this_sphere
    dim as light_struct pointer light
    dim as single current_distance=1000000.0,vx,vy,vz,d,d2,nx,ny,nz
    dim as integer i,j
    dim as recip_sqr_conversion dd
    for i=0 to ray->sphere_count-1
        sphere=@ray->sphere_list[i]
        if ray->current_sphere<>sphere then
            vx=sphere->x-ray->x
            vy=sphere->y-ray->y
            vz=sphere->z-ray->z
            d=vx*ray->vx+vy*ray->vy+vz*ray->vz
            If (d>0.0)and((d-sphere->rad)<current_distance) Then
                vx-=ray->vx*d
                vy-=ray->vy*d
                vz-=ray->vz*d
                d2=vx*vx+vy*vy+vz*vz
                If d2<=sphere->rad*sphere->rad Then
                    d-=Sqr(sphere->rad*sphere->rad-d2)
                    if d<current_distance then
                        this_sphere=sphere
                        current_distance=d
                    end if
                end if
            end if
        end if
    next

    if this_sphere then
       
        ray->x+=ray->vx*current_distance
        ray->y+=ray->vy*current_distance
        ray->z+=ray->vz*current_distance
        vx=ray->x-this_sphere->x
        vy=ray->y-this_sphere->y
        vz=ray->z-this_sphere->z
        d=(ray->vx*vx+ray->vy*vy+ray->vz*vz)*this_sphere->rad_recip2
        ray->vx-=vx*d
        ray->vy-=vy*d
        ray->vz-=vz*d
       
        nx=-(this_sphere->x-ray->x)*this_sphere->rad_recip
        ny=-(this_sphere->y-ray->y)*this_sphere->rad_recip
        nz=-(this_sphere->z-ray->z)*this_sphere->rad_recip
       
        for j=0 to ray->light_count-1
            light=@ray->light_list[j]
           
            vx=light->x-ray->x
            vy=light->y-ray->y
            vz=light->z-ray->z
            dd._float=vx*vx+vy*vy+vz*vz
            d2=0.5*dd._float
            dd._int=&h5f3759df-(dd._int shr 1)
            dd._float=dd._float*(1.5-d2*dd._float*dd._float)
            vx*=dd._float
            vy*=dd._float
            vz*=dd._float
           
           
            'test if point facing away from light
            i=0
            while(i<ray->sphere_count-1)
                'test for shadow, if in shadow then i=ray->sphere_count
                i+=1
            wend
            if i=ray->sphere_count then
                'do lighting(no shadow)
            end if
           
            d=vx*ray->vx+vy*ray->vy+vz*ray->vz
            if d>0.0 then
                d=d*d*d*ray->affect
                ray->red+=light->red*d
                ray->gre+=light->gre*d
                ray->blu+=light->blu*d
            end if
       
        next
    d=vx*nx+vy*ny+vz*nz
    if d>0.0 then
        ray->red+=this_sphere->red*ray->affect*light->red*d
        ray->gre+=this_sphere->gre*ray->affect*light->gre*d
        ray->blu+=this_sphere->blu*ray->affect*light->blu*d
    end if
        ray->affect*=this_sphere->reflective
        ray->current_sphere=this_sphere
        if ray->affect>0.01 then raytrace(ray)
       
    end if

End sub

sub render_world(ray as raytrace_struct pointer)
    dim as integer w,h,x,y
    dim as float_to_int_conversion red,gre,blu
    dim as single d
    screeninfo(w,h)
    dim as integer pointer display=ray->display
    dim as vector_struct pointer vector=ray->vectors
    for y=0 to h-1
        for x=0 to w-1
            ray->x=0.0
            ray->y=0.0
            ray->z=0.0
            ray->red=0.0
            ray->gre=0.0
            ray->blu=0.0
            ray->vx=vector->x
            ray->vy=vector->y
            ray->vz=vector->z
            ray->current_sphere=0
            ray->affect=1.0
            raytrace(ray)
            if ray->red>1.0 then ray->red=1.0
            if ray->gre>1.0 then ray->gre=1.0
            if ray->blu>1.0 then ray->blu=1.0
            red._in=ray->red*255.0+magic
            gre._in=ray->gre*255.0+magic
            blu._in=ray->blu*255.0+magic
            *display=(red._out shl 16)or(gre._out shl 8)or blu._out
            display+=1
            vector+=1
        next
    next
end sub

sub set_screen_vectors(ray as raytrace_struct pointer,z as single=200.0)
    dim as integer w,h,x,y
    dim as single d
    screeninfo(w,h)
    ray->vectors=callocate(len(vector_struct)*w*h)
    dim as vector_struct pointer vector=ray->vectors
    for y=0 to h-1
        for x=0 to w-1
            vector->x=x-(w shr 1)
            vector->y=(h shr 1)-y
            vector->z=z
            d=1.0/sqr(vector->x^2+vector->y^2+vector->z^2)
            vector->x*=d
            vector->y*=d
            vector->z*=d
            vector+=1
        next
    next
end sub

sub set_sphere(ray as raytrace_struct pointer,index as integer,_
                x as single,y as single,z as single,radius as single,_
                red as single,green as single,blue as single,reflective as single)
    dim as sphere_struct pointer sphere=@ray->sphere_list[index]
    sphere->x=x
    sphere->y=y
    sphere->z=z
    sphere->rad=radius
    sphere->rad_recip=1.0/radius
    sphere->rad_recip2=sphere->rad_recip*sphere->rad_recip*2.0
    sphere->red=red
    sphere->gre=green
    sphere->blu=blue
    sphere->reflective=reflective
end sub

sub set_light(ray as raytrace_struct pointer,index as integer,_
                x as single,y as single,z as single,_
                red as single,green as single,blue as single)
    dim  as light_struct pointer light=@ray->light_list[index]
    light->x=x
    light->y=y
    light->z=z
    light->red=red
    light->gre=green
    light->blu=blue
end sub

sub main()
   
    screenres 320,240,32,2
    screenset 1,0
   
    dim as raytrace_struct pointer ray=callocate(len(raytrace_struct))
    ray->display=screenptr()
    set_screen_vectors(ray)
    ray->sphere_count=4
    ray->light_count=1
    ray->sphere_list=callocate(len(sphere_struct)*ray->sphere_count)
    ray->light_list=callocate(len(light_struct)*ray->light_count)

    set_sphere(ray,0,50.0,20.0,300.0,50.0,.9,.0,.0,.5)
    set_sphere(ray,1,0.0,20.0,200.0,50.0,.0,.0,.2,.5)
    set_sphere(ray,2,.0,-5040.0,.0,5000.0,.2,.2,.2,.24)
    set_sphere(ray,3,.0,5080,.0,5000.0,.2,.2,.2,.1)
   
    set_light(ray,0,100.0,20.0,.0,1.0,1.0,0.5)
   
    dim as single angle=0
    do
       
        ray->sphere_list[0]->z=200.0+100*sin(angle)
        ray->sphere_list[0]->x=0.0+100*cos(angle)
       
        ray->sphere_list[1]->z=200.0-30*sin(angle)
        ray->sphere_list[1]->x=0.0-30*cos(angle)
       
        angle+=0.1
       
        render_world(ray)
       
        flip
        cls
       
    loop while inkey$=""
   
end sub

main()
Title: Re: Another raytracer.
Post by: Shockwave on August 10, 2007
Is this FB 1.6 mate?

I can't run it here on my opld version of Freebasic.. Any chance of an exe please ? :) Cheers.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 10, 2007
It's .17b I've got but this also doesn't work with the latest version of the compiler :( there seems to have been a fair bit of messing around with the types over the last few builds.

Here's the exe.
Title: Re: Another raytracer.
Post by: Shockwave on August 10, 2007
Messed about code you may say but that looks very good to me. Thanks for posting the exe.
Title: Re: Another raytracer.
Post by: rain_storm on August 10, 2007
Excellent stuff its runnin smooth here. unfinished you say? well i cant wait to see the finished one :D
Title: Re: Another raytracer.
Post by: Stonemonkey on August 11, 2007
Fixed up the shadows and lighting a bit, It's pertty slow but it doesn't use any interpolation or anything to keep the speed up so I'm not too bothered about that.
It only does spheres and has no refraction.

Source and exe included:
Title: Re: Another raytracer.
Post by: Shockwave on August 11, 2007
"Finished" was running at a couple of frames a second, "Finished_mod" was a frame every few seconds but like you said, it's not something to be bothered about as the rendering is really nice quality.

Now if that was my code I would leave it running during a demo during static text displays and precalculate a nice animation for the end part :)

Lovely stuff Stonemonkey and source included too, have some Karma :)
Title: Re: Another raytracer.
Post by: Stonemonkey on August 11, 2007
Quote
"Finished" was running at a couple of frames a second, "Finished_mod" was a frame every few seconds

Strange, it's the other way round for me.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 11, 2007
Here's another edit, should work in the latest version of FB with or without -lang deprecated:
Title: Re: Another raytracer.
Post by: Jim on August 12, 2007
It looks great!

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
Thanks, just a shame it's not a bit faster. Maybe of use for some animated textures or something though.
Title: Re: Another raytracer.
Post by: Jim on August 12, 2007
Is it possible to create 2 (or more) threads to do it?  That would double the speed on an P4 HT or Core2.

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
Should be, what sort of thing would you recommend? dividing the screen up and letting each thread deal with a section of it?
Title: Re: Another raytracer.
Post by: Jim on August 12, 2007
Yeah, how I'd do it is start by dividing the screen into 2 (or more) horizontal strips and render each on a different thread.

Then you could consider dividing it into many, many strips (scanlines?) and allocating them to any thread that's finished its work already.

Then you could do the same with 2d blocks instead of strips.

This way, if the top half of the screen is empty, that thread should render really fast and complete quickly, so that thread should apply itself to another part of the screen.

I'm working on something similar for a Mandelbrot renderer.  Simple multithread easily doubles the speed.

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
Divided the screen into 4 areas each rendered with a different thread, as you say though some threads will finish quicker than others but I'm not sure if FB can detect whether a thread has finished other than waiting with threadwait.

doesn't work with -lang deprecated.
Title: Re: Another raytracer.
Post by: Jim on August 12, 2007
I reckon that's 2-3x faster than finished_mod.exe.  Nice work, but still a bit of a slideshow :D

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
Yep it is, still might find some use for it though.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
Here's the code for one I did some time ago, takes a min or 2 to do the render.
Title: Re: Another raytracer.
Post by: taj on August 12, 2007
Stonemonkey,

cant you just use a variable? When the thread finishes it sets the variable to "done" value that the main thread (task manager as used to call them) reads?

Chris
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
Hmm, global? or maybe another field in the world type 'world->thread_busy' or something?
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
something liek this:

Code: [Select]
sub render_thread(world as world_struct pointer)
    dim as integer x,y
    dim as single d
    dim as integer pointer display=world->display+(world->y_start*world->wwidth)
    dim as vector_struct pointer vector=world->vectors+(world->y_start*world->wwidth)
    for y=world->y_start to world->y_end
        for x=0 to world->wwidth-1
            *display=raytrace(world,vector)
            display+=1
            vector+=1
        next
    next
    world->busy=0
end sub

sub render_world(world as world_struct pointer)
   
    dim as integer y=0,i=0
   
    dim world_thread(0 to 3) as world_struct
    dim as any pointer thread(0 to 3)
    world_thread(0)=*world
    world_thread(1)=*world
    world_thread(2)=*world
    world_thread(3)=*world
   
    while y<world->height
        i=(i+1)and 3
        if (@world_thread(i))->busy=0 then
            (@world_thread(i))->y_start=y
            (@world_thread(i))->y_end=y+9
            (@world_thread(i))->busy=1
            thread(i)=threadcreate(@render_thread,@world_thread(i))
            y+=10
        end if
    wend
   
    threadwait(thread(0))
    threadwait(thread(1))
    threadwait(thread(2))
    threadwait(thread(3))
   
end sub

with the field   'busy as integer'   added to the world_struct type.

Does it in blocks of 10 rows at a time, the current values need the screen size to be some multiple of 10 in height.
Title: Re: Another raytracer.
Post by: Jim on August 12, 2007
You have to watch out for that - the while loop that is checking for busy is very tight - it has no sleep in it so it will take up 100% cpu doing very little.  The correct way to do this is to use semaphores, but I'm not sure FB supports them directly - you might have to use Windows API.
eg. Windows has a function that says
Sleep until one or more of the threads completes and then tell me which one(s) it was.

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 12, 2007
yep, i was wondering about that, i don't think fb can do that and sleep wouldn't be much help either.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 13, 2007
Is it something like this:

Code: [Select]
WaitForMultipleObjectsEx(4,@thread(0),0,1,0)

added into the loop?
Title: Re: Another raytracer.
Post by: Jim on August 13, 2007
Yes, something very like that - then check the return code (or the busy flags of all threads) to see which one to start next.  Is it known whether FB's thread handles are compatible with Windows ones?

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 13, 2007
I have no idea and i'm just trying to make some sense of the standard access rights although i'm just looking at it kind of blankly atm.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 13, 2007
Something I'm not to sure about is bAlertable, the description is a little confusing.
Title: Re: Another raytracer.
Post by: Jim on August 13, 2007
Set it to false.  There are many things that can be waited on - thread and process handles, semaphores, events, and overlapped IO, etc.  Overlapped IO is where you tell Windows to go and read or write a load of data, and then go to sleep until the read or write is completed.

You want to set the timeout to INFINITE.

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 13, 2007
Now, if i put in the for loop to test each thread, it crashes/closes. doesn't do that if i just loop i in the while wend loop though. (not just with putting in infinite etc. was the same just before that too)
Title: Re: Another raytracer.
Post by: Stonemonkey on August 13, 2007
ok, found why it crashes, the for loop was letting it draw beyond the screen boundary.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 13, 2007
I think I'm getting there now:
Title: Re: Another raytracer.
Post by: taj on August 13, 2007
any chance of an exe for the C weenies??
Title: Re: Another raytracer.
Post by: Jim on August 14, 2007
->Fryer, it might be interesting as an experiment to colour code which areas are rendered by which cpu/thread.  I see you've made each thread do 10 lines at a time.  Good for debugging :)

->chris, it's exactly the same as one of the previous exes, except it's now using 4 concurrent threads to do the rendering.  This is making it 2-3x faster on my Core2, and it should help all the P4 HT people, and anyone lucky enough to have a Core2 Quad!

Jim
Title: Re: Another raytracer.
Post by: Clyde on August 14, 2007
Stonemonkey dude, it crashes my end.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 14, 2007
Nice idea to see what goes on Jim, I'll give that a go.

Sorry Clyde, I take it you're not running the latest version of FB atm?
Title: Re: Another raytracer.
Post by: Shockwave on August 14, 2007
Won't run here either (FB1.5) so cant give feedback on speed and such, sorry :) I bet it looks nice though.
Title: Re: Another raytracer.
Post by: Stonemonkey on August 14, 2007
Sorry about that, it still just looks the same as before. The only thing is now it's multithreaded so it'd hopefully run a bit better on muti core/cpu machines although mine isn't. It's just turnde into a bit of an experiment tbh, here's how it stands atm with colour banding showing up which thread is rendering.

.exe
Title: Re: Another raytracer.
Post by: Clyde on August 14, 2007
Doh! Silly me I'll need to bung on V17b.
And Mate thats mega impressive welldone indeed dude.
 :o
Title: Re: Another raytracer.
Post by: Shockwave on August 14, 2007
looks great and seems to run quicker on dual core too :)
Title: Re: Another raytracer.
Post by: Stonemonkey on August 14, 2007
Cool and thanks.

Another thing I'm wondering about this is sharing the data between the threads, there's stuff about locking memory (with mutexlock). I can see the point in doing that when a thread is writing to memory but in this case I don't write to any of the shared memory so to me that's not really an issue, is that right or is there anything else that should be taken care of?
Title: Re: Another raytracer.
Post by: Rbz on August 15, 2007
@Stonemonkey: I've tried your exe and it run very slow here, it took around 7 seconds to render one frame :( , probably because I've only one processor core ???  (Pentium 4 2.8Ghz)


Title: Re: Another raytracer.
Post by: Jim on August 15, 2007
->rbraz - don't worry, it only does about 2fps on my Core2 Duo 2.66GHz!

->fryer - you almost certainly don't have to worry about data locking in this program - multiple threads can share the model data with no problems, and since they're each writing to different bits of the screen it's OK too.

You need the locking when one thread writes some data and another thread needs to read it back.  Usually you create a 'semaphore' (not a mutex, which is slightly different).  One thread then 'locks' the semaphore, does the writes and unlocks it again.  If the other thread tries to lock the semaphore while the first thread still has it locked, it will block until the semaphore is released.  Clever eh? :)

Jim
Title: Re: Another raytracer.
Post by: Stonemonkey on August 15, 2007
Thanks Jim.

rbraz, that does seem a bit slow to me. I have a single core amd64 @ 2.1GHz and get somewhere around 1fps
Title: Re: Another raytracer.
Post by: Paul on August 15, 2007
i've got a amd athlon xp 2600+ 1.9 ghz and running a little more than 1 fps
Title: Re: Another raytracer.
Post by: Rbz on August 19, 2007
Thanks Jim.

rbraz, that does seem a bit slow to me. I have a single core amd64 @ 2.1GHz and get somewhere around 1fps
Might be my graphics board then, I've always got bad FPS with software rendered pixel.