''bluatigro 1 mrt 2015
''rebild of internet raytracer
nomainwin
'constants to access the point arrays
global X,Y,Z,R,clr,Xp,Yp,DistToScreen,maxObjects
X = 0
Y = 1
Z = 2
R = 3
clr = 4
global sptel
'the world is 24x24 units
'0,0 is centre of the world
'-12 far left 12 far right
'12 top -12 bottom
'objects are specified in world units
'rays are specified in world units
'pixel coordinates are converted by
'dividing the world width by the screen width
'and the world height by the screen height
'this gives a per pixel world increment value
global ScrXleft , ScrXRight , ScrYTop , ScrYBottom
ScrXLeft=-12
ScrXRight=12
ScrYTop=-12
ScrYBottom=12
global ImgX , ImgY , ScrX , Scry
ImgX=512 'pixel width of screen
ImgY=512 'pixel height of screen
ScrX=2*ScrXRight/ImgX 'per pixel world increment value
ScrY=2*ScrYBottom/ImgY
'Calculate how much space windows borders take
'Anatoly's tip
WindowWidth = 200
WindowHeight = 200
open "Ajusting..." for graphics_nsb as #1
#1, "home ; down ; posxy w h"
w=200-2*w : h = 200-2*h
'w and h now contain the number of pixels
'the Windows scheme/theme takes
close #1
WindowWidth = ImgX+w
WindowHeight = ImgY+h
UpperLeftX = int((DisplayWidth-WindowWidth)/2)
UpperLeftY = int((DisplayHeight-WindowHeight)/2)
open "Raytrace" for graphics_nsb as #1
#1 "down ; fill black ; trapclose [quit]"
global red , green , blue , gray
red = rgb( 255 , 0 , 0 )
green = rgb( 0 , 255 , 0 )
blue = rgb( 0 , 0 , 255 )
gray = rgb( 200 , 200 , 200 )
'sphere data x,y,z,radius,color
'three spheres ,red green and blue
'evenly spaced around centre of screen
'z order front to back blue, green, red
'there are five massive spheres positioned
'all round the edges and to the rear to create walls
maxObjects = 10
dim sp( maxObjects , 4 )
call sphere 0,0,5 , 4 , red
call sphere 0,5,2 , 2 , green
call sphere 6,0,2 , 2 , blue
call sphere -1e5-12,20,26 , 1e5 , gray
call sphere 1e5+12,20,26 , 1e5 , gray
call sphere 0,-1e5-12,26 , 1e5 , gray
call sphere 0,1e5+12,26 , 1e5 , gray
call sphere 0,0,1e5+12 , 1e5 , gray
'point origin of camera
'centre screen, 24 world units back from the image plane
cam(X) = 0
cam(Y) = 0
cam(Z) = -24
'point origin of light source
'centre top, on the image plane
lit(X) = -11
lit(Y) = -11
lit(Z) = -11
call render
notice "ready !!"
wait
sub render
'now cast a ray through every pixel on the screen
for Xp=0 to ImgX
for Yp=0 to ImgY
scan
'point0, the camera x,y,z
o(X) = cam(X) 'world x camera 0
o(Y) = cam(Y) 'world y camera 0
o(Z) = cam(Z) 'world z camera -10
'point1, the screen x,y,z
'which is ScrXLeft (-12) + .5 * ScrX, the per pixel world increment value
'to which we add xp * ScrX to give world x in world units
r(X) = ScrXLeft + 0.5 * ScrX + Xp * ScrX 'world x
r(Y) = ScrYTop + 0.5 * ScrY + Yp * ScrY 'world y
r(Z) = 0 'world z image plane 0
'flat parrallell ray no perspective
'o(X)=r(X)
'o(Y)=r(Y)
'o(Z)=-10
'subtract point0 from point1 to get vector direction d()
d(X) = r(X) - o(X)
d(Y) = r(Y) - o(Y)
d(Z) = r(Z) - o(Z)
'normalize it by dividing by its length to make a unit vector ie it sums to 1
l = sqr( d(X) * d(X) + d(Y) * d(Y) + d(Z) * d(Z) )
d(X) = d(X) / l
d(Y) = d(Y) / l
d(Z) = d(Z) / l
DistToScreen = l
'go look for an intersect
null = raySphereIntersect()
next 'y
next 'x
end sub
[quit]
close #1
end
function nr$( no , digits )
nr$ = right$( "0000000000" ; no , digits )
end function
sub savescreen a$
#1 "getbmp screen 0 0 " ; ImgX ; " " ; ImgY
bmpsave "screen" , a$ + ".bmp"
end sub
function rgb( a , b , c )
rgb = a + b * 256 + c * 256 ^ 2
end function
function clr.r( kl )
clr.r = kl and 255
end function
function clr.g( kl )
clr.g = int( kl / 256 ) and 255
end function
function clr.b( kl )
clr.b = int( kl / 256 ^ 2 ) and 255
end function
sub sphere a , b , c , d , kl
if sptel >= maxObjects then exit sub
sp( sptel , X ) = a
sp( sptel , Y ) = b
sp( sptel , Z ) = c
sp( sptel , R ) = d
sp( sptel , clr ) = kl
sptel = sptel + 1
end sub
function Sphere.dist( o )
uit = 1e7
b = 2 * d(X) * ( o(X) - sp(o,X)) _
+ 2 * d(Y) * ( o(Y) - sp(o,Y)) _
+ 2 * d(Z) * ( o(Z) - sp(o,Z))
c = ( o(X) - sp(o,X) ) ^ 2 _
+ ( o(Y) - sp(o,Y) ) ^ 2 _
+ ( o(Z) - sp(o,Z) ) ^ 2 - sp(o,R) ^ 2
d = b * b - 4 * c
if d > 0 then
uit = ( b * -1 - sqr( b * b - 4 * c ) ) / 2
end if
Sphere.dist = uit
end function
function raySphereIntersect()
maxdist = 1e7
for o = 0 to sptel
t = Sphere.dist( o )
if t > DistToScreen then
'store the shortest intersect of all sphere intersects
if t < maxdist then maxdist = t : id = o
end if
next
if maxdist < 1e7 then
'establish the sphere surface intersect point
i(X) = o(X) + d(X) * maxdist
i(Y) = o(Y) + d(Y) * maxdist
i(Z) = o(Z) + d(Z) * maxdist
'get unit normal vector from sphere centre to surface intersect
n(X) = ( i(X) - sp(id,X) ) / sp(id,R)
n(Y) = ( i(Y) - sp(id,Y) ) / sp(id,R)
n(Z) = ( i(Z) - sp(id,Z) ) / sp(id,R)
'get the unit normal vector from sphere surface intersect to the light
l(X) = lit(X) - i(X)
l(Y) = lit(Y) - i(Y)
l(Z) = lit(Z) - i(Z)
l = sqr( l(X) * l(X) + l(Y) * l(Y) + l(Z) * l(Z) )
l(X) = l(X) / l
l(Y) = l(Y) / l
l(Z) = l(Z) / l
'the dot product of these vectors gives an indication of the light
color = n(X) * l(X) + n(Y) * l(Y) + n(Z) * l(Z)
'cast a ray from intersect to the light to check for shadow
'we have done most of the ray prep
'point0 is the intersect point1 is the light
'so l() is our ray direction, point1-point0, normalized
shadow = 1
o(X) = i(X)
o(Y) = i(Y)
o(Z) = i(Z)
d(X) = l(X)
d(Y) = l(Y)
d(Z) = l(Z)
for o = 0 to sptel
if o <> id then
'check a ll other spheres not the one we are currently considering
t = Sphere.dist( o )
if t > 0 and t < 1e7 then shadow = 0.5 : exit function
end if
next
'add some ambient light
if color < .3 then color = .3
kl = sp( id , clr )
'color the pixel
#1 "color " ; clr.r( kl ) * color * shadow _
; " " ; clr.g( kl ) * color * shadow _
; " " ; clr.b( kl ) * color * shadow
#1 "set " ; Xp ; " " ; Yp
else
#1 "color black"
#1 "set " ; Xp ; " " ; Yp
end if
end function