Raytracing in One Weekend

Everything related to 3D programming
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Hi @pjay

Yes it is a bit slow cause the 3D Rendering use the sample for antialiasing, without the sample it is more fast to render in window thought :)

Again yes I have tried the startdrawing / stopdrawing as you have suggested, working but doesn't display as real time than setpixel_() ... :cry:

Someone know hot to render as specific in any OS (Linux and Mac OS) ?

Thanks
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Hi

Ok I am rewriting from scratch of my source code in hope to make more easier and faster than before :mrgreen:

So it is right now a wip :wink: (Not finished but it is coming...)

See my new code :

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Writed and adapted in Purebasic by threedslider


;; Rewrited by threedslider Jun 10 2024


aspect_ratio.f = 16.0 / 9.0
image_width = 800

image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Vector
Structure Vec3
  x.f
  y.f
  z.f
EndStructure


; Vector 3D Macros
Macro rt_zero(v)
  v\x = 0.0
  v\y = 0.0
  v\z = 0.0
EndMacro

Macro rt_clamp(v, m1, m2)
  v\x = clamp(v\x, m1, m2)
  v\y = clamp(v\y, m1, m2)
  v\z = clamp(v\z, m1, m2)
EndMacro

Macro rt_surround(x, m1, m2)
  surrounds(x, m1, m2)
EndMacro

Macro rt_norm(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x * n
    v\y * n
    v\z * n
EndMacro
  
Macro rt_dot(v1, v2)
  (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z)
EndMacro

Macro rt_add(v, v1, v2)
  v\x = v1\x + v2\x
  v\y = v1\y + v2\y
  v\z = v1\z + v2\z
EndMacro

Macro rt_sub(v, v1, v2)
  v\x = v1\x - v2\x
  v\y = v1\y - v2\y
  v\z = v1\z - v2\z
EndMacro

Macro rt_mul(v, v1, v2)
  v\x = v1\x * v2\x
  v\y = v1\y * v2\y
  v\z = v1\z * v2\z
EndMacro

Macro rt_scale(v, v1, s)
  v\x = v1\x * s
  v\y = v1\y * s
  v\z = v1\z * s
EndMacro  

Macro rt_neg(v, v1)
  v\x = -v1\x
  v\y = -v1\y
  v\z = -v1\z
EndMacro


; Ray
Structure Ray
  orig.Vec3
  dir.Vec3
EndStructure


; Sphere
Structure Sphere
  center.Vec3
  radius.f
  color.Vec3
EndStructure


; Clamp
Procedure.f clamp(x.f, min.f, max.f)
  If(x < min)
    ProcedureReturn min
  EndIf
  If(x > max)
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure


; Surrounds
Procedure surrounds(x.f, min.f, max.f)
  ProcedureReturn Bool( min < x And x < max)
EndProcedure

; Create Sphere primitve
Macro m_Sphere(id, center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  scene(id)\center\x = center_x
  scene(id)\center\y = center_y
  scene(id)\center\z = center_z
  scene(id)\radius = radius_
  scene(id)\color\x = color_x
  scene(id)\color\y = color_y
  scene(id)\color\z = color_z
EndMacro


Global Dim scene.Sphere(0)
m_Sphere(0, 0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
;m_Sphere(1, 0.0, -100.0, -1.0, 100, 1.0, 1.0, 1.0)


; Hit for 3D objects
Procedure.f hit( *_s.Sphere, *_r.Ray)
  
  Protected oc.Vec3
  root.f = 0.0
  min.f = 0.0
  max.f = 0.999
  
  rt_sub(oc, *_r\orig, *_s\center) 
   
  a.f = rt_dot(*_r\dir, *_r\dir)
  b.f = rt_dot(oc,*_r\dir)
  c.f = rt_dot(oc,oc) - *_s\radius *  *_s\radius
  
  discriminant.f = b*b - a*c
  
  
  If discriminant < 0.0
    ProcedureReturn 0.0
  EndIf

    root = (-b - Sqr(discriminant)) / (a)    
    
    If (Not rt_surround(root, min, max))
      root = (-b + Sqr(discriminant)) / (a)
         
     If ( Not rt_surround(root, min, max))
       ProcedureReturn 0.0   
    EndIf
  
    
    EndIf 
        
    ProcedureReturn root
  
EndProcedure


; Hit for intersction
Procedure.b intersect( *_r.Ray, *t.Float, *id.Integer )
  Protected d.f, inf.f = 0.999
  Protected i.i = ArraySize(scene())
  *t\f = inf
  While i >= 0
    d = hit( scene(i), *_r )
    If d And d<*t\f
      *t\f  = d
      *id\i = i
    EndIf
    i - 1
  Wend
  ProcedureReturn Bool( *t\f < inf )
EndProcedure


Procedure.f ray_color(*_r.Ray)
EndProcedure


; Render engine
Procedure render()
  
Protected min.f = 0.0
Protected max.f = 0.999
  
Shared image_width
Shared image_height

focal_length.f = 1.0
viewport_height.f = 2.0

i_w.f = image_width
i_h.f = image_height
viewport_width.f = viewport_height * (i_w/i_h)

point3.Vec3

point3\x = 0.0
point3\y = 0.0
point3\z = 0.0

ray_direction.Vec3

ray_direction\x = 0
ray_direction\y = 0
ray_direction\z = 0

viewport_u.Vec3

viewport_u\x = viewport_width
viewport_u\y = 0
viewport_u\z = 0

viewport_v.Vec3

viewport_v\x = 0
viewport_v\y = -viewport_height
viewport_v\z = 0

pixel_delta_u.Vec3

pixel_delta_u\x = viewport_u\x / i_w
pixel_delta_u\y = viewport_u\y / i_w
pixel_delta_u\z = viewport_u\z / i_w

pixel_delta_v.Vec3

pixel_delta_v\x = viewport_v\x / i_h
pixel_delta_v\y = viewport_v\y / i_h
pixel_delta_v\z = viewport_v\z / i_h

viewport_upper_left.Vec3

v_f.Vec3
v_f\x = 0.0
v_f\y = 0.0
v_f\z = focal_length

viewport_upper_left\x = point3\x - v_f\x - viewport_u\x / 2 - viewport_v\x / 2
viewport_upper_left\y = point3\y - v_f\y - viewport_u\y / 2 - viewport_v\y / 2
viewport_upper_left\z = point3\z - v_f\z - viewport_u\z / 2 - viewport_v\z / 2

pixel_100_loc.Vec3

pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)

sample_per_pixel.i = 10
pixel_sample_scale.f = 1.0 / sample_per_pixel

depth.i = 50

t.f = 0.0
id.i = 0

If StartDrawing(ScreenOutput())

For j = 0 To image_height-1
  For i = 0 To image_width-1
      
      ii.f = i
      jj.f = j
      
      pixel_center.Vec3
            
      pixel_center\x = pixel_100_loc\x + (ii * pixel_delta_u\x) + (jj * pixel_delta_v\x)
      pixel_center\y = pixel_100_loc\y + (ii * pixel_delta_u\y) + (jj * pixel_delta_v\y)
      pixel_center\z = pixel_100_loc\z + (ii * pixel_delta_u\z) + (jj * pixel_delta_v\z)
      
      
      ray_direction\x = pixel_center\x - point3\x
      ray_direction\y = pixel_center\y - point3\y
      ray_direction\z = pixel_center\z - point3\z
           
      Define.Ray r
      
      r\orig = point3
      r\dir = ray_direction          
    
      
      If intersect(r, @t, @id)
        Plot(i, j, RGB(255, 0, 0))
      Else
        Plot(i, j, RGB(100, 100, 255))
      EndIf
       
        
      Next
    
    Next
    
EndIf
  
StopDrawing() 

EndProcedure




InitSprite()
 
 OpenWindow(0, 0, 0, image_width, image_height, "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
 OpenWindowedScreen(WindowID(0), 0, 0, image_width, image_height)
  
  ;ClearScreen(RGB(0,0,0))
  
 render()
    
 Repeat
   Event = WindowEvent()
   
 Until  Event = #PB_Event_CloseWindow
   
 End 
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Yay ! It runs smoothly and more faster than before !! :mrgreen:

Thanks by pjay for his hint and tips and I take some from him his code so thank you :D

Here the last code I done so far :

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider



;; Rewritten by threedslider Jun 10 2024
;; Added to color for sphere and background Jun 11 2024


; Vector
Structure Vec3
  x.f
  y.f
  z.f
EndStructure


; Vector for 3D Macros
Macro rt_zero(v)
  v\x = 0.0
  v\y = 0.0
  v\z = 0.0
EndMacro

Macro rt_clamp(v, m1, m2)
  v = clamp(v, m1, m2)
EndMacro

Macro rt_surround(x, m1, m2)
  surrounds(x, m1, m2)
EndMacro

Macro rt_norm(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x * n
    v\y * n
    v\z * n
  EndIf
  
EndMacro
  
Macro v_length(v)
  Sqr(v\dir\x * v\dir\x + v\dir\y * v\dir\y + v\dir\z * v\dir\z)
EndMacro
  
Macro rt_dot(v1, v2)
  (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z)
EndMacro

Macro rt_copy(v, v1)
  v\x = v1\x
  v\y = v1\y
  v\z = v1\z
EndMacro

Macro rt_add(v, v1, v2)
  v\x = v1\x + v2\x
  v\y = v1\y + v2\y
  v\z = v1\z + v2\z
EndMacro

Macro rt_sub(v, v1, v2)
  v\x = v1\x - v2\x
  v\y = v1\y - v2\y
  v\z = v1\z - v2\z
EndMacro

Macro rt_mul(v, v1, v2)
  v\x = v1\x * v2\x
  v\y = v1\y * v2\y
  v\z = v1\z * v2\z
EndMacro

Macro rt_scale(v, v1, s)
  v\x = v1\x * s
  v\y = v1\y * s
  v\z = v1\z * s
EndMacro  

Macro rt_neg(v, v1)
  v\x = -v1\x
  v\y = -v1\y
  v\z = -v1\z
EndMacro

; Ray
Structure Ray
  orig.Vec3
  dir.Vec3
EndStructure


; Sphere
Structure Sphere
  center.Vec3
  radius.f
  color.Vec3
EndStructure

; Clamp
Procedure.f clamp(x.f, min.f, max.f)
  If(x < min)
    ProcedureReturn min
  EndIf
  If(x > max)
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

; Random
Procedure.f random_float()   
    r.f = Random(#MAXDWORD) / #MAXDWORD  
    ProcedureReturn r
EndProcedure

; Surrounds
Procedure surrounds(x.f, min.f, max.f)
  ProcedureReturn Bool( min < x And x < max)
EndProcedure

; Create Sphere primitve
Macro m_Sphere(id, center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  scene(id)\center\x = center_x
  scene(id)\center\y = center_y
  scene(id)\center\z = center_z
  scene(id)\radius = radius_
  scene(id)\color\x = color_x
  scene(id)\color\y = color_y
  scene(id)\color\z = color_z
EndMacro


Global Dim scene.Sphere(3)
m_Sphere(0, 0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
m_Sphere(1, -1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(2, 1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(3, 0.0, -100.0, -1.0, 100.0, 1.0, 1.0, 1.0)


; Hit for 3D objects
Procedure.f hit( *_s.Sphere, *_r.Ray, mx.f)
  
  Protected oc.Vec3
  Protected root.f = 0.0
  Protected min.f = 0.0
  Protected max.f = mx
  
  rt_sub(oc, *_r\orig, *_s\center) 
   
  Protected a.f = rt_dot(*_r\dir, *_r\dir)
  Protected b.f = rt_dot(oc,*_r\dir)
  Protected c.f = rt_dot(oc,oc) - *_s\radius *  *_s\radius
  
  Protected discriminant.f = b*b - a*c
  
  
  If discriminant < 0.0
    ProcedureReturn 0.0
  EndIf

    root = (-b - Sqr(discriminant)) / (a)    
    
    If (Not rt_surround(root, min, max))
      root = (-b + Sqr(discriminant)) / (a)
         
     If ( Not rt_surround(root, min, max))
       ProcedureReturn 0.0   
    EndIf
  
    
    EndIf 
        
    ProcedureReturn root
  
EndProcedure


; Hit for intersection
Procedure.b intersect( *_r.Ray, *t.Float, *id.Integer )
  Protected d.f, inf.f = 0.999
  Protected i.i = ArraySize(scene())
  *t\f = inf
  While i >= 0
    d = hit( scene(i), *_r, inf )
    If d 
      *t\f  = d
      *id\i = i
    EndIf
    i - 1
  Wend
  ProcedureReturn Bool( *t\f < inf )
EndProcedure

Procedure.f unit_vector_y(*vy.Ray)
  Protected len.f
  Static y.f : y.f = 0
  len = v_length(*vy)
  If Not len = 0 
    y = *vy\dir\y / len 
   EndIf
  ProcedureReturn y
EndProcedure

Procedure.f ray_color(*rr.Ray, *in.Vec3)
  Protected a.f
  Static color1.Vec3, color2.Vec3, unit_direction.Vec3
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
  
  unit_direction\x = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\x + 1.0) 
  *in\x =  (1.0 - a) * color1\x + a * color2\x 
  
  unit_direction\y = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\y + 1.0)
  *in\y =  (1.0 - a) * color1\y + a * color2\y
  
  unit_direction\z = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\z + 1.0)
  *in\z =  (1.0 - a) * color1\z + a * color2\z
  
EndProcedure  

Procedure.f ray_color_hit(*_r.Ray, *in.Vec3)
  Protected x.Vec3
  Protected ntemp.f
  Protected t.f = 0.0
  Protected id.i = 0
  
    
  If intersect(*_r, @t, @id)
    Protected *obj.Sphere = scene(id)
    
    ; Point at parameter
    x\x = *_r\orig\x + t * *_r\dir\x
    x\y = *_r\orig\y + t * *_r\dir\y
    x\z = *_r\orig\z + t * *_r\dir\z
    
    ; Norm
    Protected n.Vec3
    rt_sub( n, x, *obj\center)
    rt_norm( n, ntemp )
    
    ; Set to normal
    Protected nl.Vec3
    If rt_dot( n, *_r\dir ) < 0.0
      rt_copy( nl, n )
    Else
      rt_neg( nl, n )
    EndIf
    
    *in\x = 0.5 * (nl\x + 1.0)
    *in\y = 0.5 * (nl\y + 1.0)
    *in\z = 0.5 * (nl\z + 1.0)
    
  Else
    
    ray_color(*_r, *in)
    
  EndIf


    
    
  
  

EndProcedure


Define aspect_ratio.f = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
   
Shared image_width
Shared image_height

Static focal_length.f = 1.0
Static viewport_height.f = 2.0

Protected i_w.f = image_width
Protected i_h.f = image_height
Protected viewport_width.f = viewport_height * (i_w/i_h)

Protected point3.Vec3

point3\x = 0.0
point3\y = 0.0
point3\z = 0.0

Protected ray_direction.Vec3

ray_direction\x = 0
ray_direction\y = 0
ray_direction\z = 0

Protected viewport_u.Vec3

viewport_u\x = viewport_width
viewport_u\y = 0
viewport_u\z = 0

Protected viewport_v.Vec3

viewport_v\x = 0
viewport_v\y = -viewport_height
viewport_v\z = 0

Protected pixel_delta_u.Vec3

pixel_delta_u\x = viewport_u\x / i_w
pixel_delta_u\y = viewport_u\y / i_w
pixel_delta_u\z = viewport_u\z / i_w

Protected pixel_delta_v.Vec3

pixel_delta_v\x = viewport_v\x / i_h
pixel_delta_v\y = viewport_v\y / i_h
pixel_delta_v\z = viewport_v\z / i_h

Protected viewport_upper_left.Vec3

Protected v_f.Vec3

v_f\x = 0.0
v_f\y = 0.0
v_f\z = focal_length

viewport_upper_left\x = point3\x - v_f\x - viewport_u\x / 2 - viewport_v\x / 2
viewport_upper_left\y = point3\y - v_f\y - viewport_u\y / 2 - viewport_v\y / 2
viewport_upper_left\z = point3\z - v_f\z - viewport_u\z / 2 - viewport_v\z / 2

Protected pixel_100_loc.Vec3

pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)

Protected sample_per_pixel.i = 10
Protected pixel_sample_scale.f = 1.0 / sample_per_pixel

Protected depth.i = 10

Protected x.f, y.f, z.f, v.vec3

Static offset.Vec3, pixel_sample.Vec3

If StartDrawing(ScreenOutput())

For j = 0 To image_height-1
  For i = 0 To image_width-1
      
      ii.f = i
      jj.f = j
      
      x = 0.0
      y = 0.0
      z = 0.0
      
      ;Protected pixel_center.Vec3
      
      For sample = 0 To sample_per_pixel
            
;       pixel_center\x = pixel_100_loc\x + (ii * pixel_delta_u\x) + (jj * pixel_delta_v\x)
;       pixel_center\y = pixel_100_loc\y + (ii * pixel_delta_u\y) + (jj * pixel_delta_v\y)
;       pixel_center\z = pixel_100_loc\z + (ii * pixel_delta_u\z) + (jj * pixel_delta_v\z)
;       
;       
;       ray_direction\x = pixel_center\x - point3\x
;       ray_direction\y = pixel_center\y - point3\y
;       ray_direction\z = pixel_center\z - point3\z
        
      offset\x = random_float() -0.5 
      offset\y = random_float() -0.5 
      offset\z = 0.0
      
      
      pixel_sample.Vec3
      
      pixel_sample\x = pixel_100_loc\x + ( (ii+offset\x) * pixel_delta_u\x) + ( (jj+offset\y) * pixel_delta_v\x)
      pixel_sample\y = pixel_100_loc\y + ( (ii+offset\x) * pixel_delta_u\y) + ( (jj+offset\y) * pixel_delta_v\y)
      pixel_sample\z = pixel_100_loc\z + ( (ii+offset\x) * pixel_delta_u\z) + ( (jj+offset\y) * pixel_delta_v\z)
      
      ray_direction\x = pixel_sample\x - point3\x
      ray_direction\y = pixel_sample\y - point3\y
      ray_direction\z = pixel_sample\z - point3\z
           
      Define.Ray r
      
      r\orig = point3
      r\dir = ray_direction          
    
      ray_color_hit(r, @v) 
      
      x = x + v\x
      y = y + v\y
      z = z + v\z
      
      Next
      
        Plot(i, j, RGB(Int(255.999 * clamp(pixel_sample_scale * x, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale*  y, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale * z, 0.0, 0.999))))
     
       
        
      Next
    
    Next
    
EndIf
  
StopDrawing() 

EndProcedure




InitSprite()
 
 OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
 OpenWindowedScreen(WindowID(0), 0, 0, image_width , image_height )
  
  ;ClearScreen(RGB(0,0,0))
  
Define time = ElapsedMilliseconds()
render()
time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

 Repeat
   Event = WindowEvent()
   
 Until  Event = #PB_Event_CloseWindow
   
 End 

threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

I have been working to Pathtracing system as beta but dunno what it is his problem :shock: :cry:

It doesn't generate the random properly :? ... However it runs smoothly but strange :?:

If someone can help me this problem ? It will be super cool by your help :mrgreen:

So as always it is wip :

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider


;; Rewritten by threedslider Jun 10 2024
;; Added to color for sphere and background Jun 11 2024
;; Added to Pathtracing system (WIP) Jun 11 2024

; Vector
Structure Vec3
  x.f
  y.f
  z.f
EndStructure


; Vector for 3D Macros
Macro rt_zero(v)
  v\x = 0.0
  v\y = 0.0
  v\z = 0.0
EndMacro

Macro rt_clamp(v, m1, m2)
  v = clamp(v, m1, m2)
EndMacro

Macro rt_surround(x, m1, m2)
  surrounds(x, m1, m2)
EndMacro

Macro rt_norm(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x * n
    v\y * n
    v\z * n
  EndIf
  
EndMacro

Macro rt_norm2(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x = v\x * n
    v\y = v\y * n
    v\z = v\z * n
  EndIf
  
EndMacro

Macro rt_r_length(v)
  Sqr(v\dir\x * v\dir\x + v\dir\y * v\dir\y + v\dir\z * v\dir\z)
EndMacro

Macro rt_v_length_squared(v)
  (v\x * v\x + v\y * v\y + v\z * v\z)
EndMacro  
  
Macro rt_dot(v1, v2)
  (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z)
EndMacro

Macro rt_copy(v, v1)
  v\x = v1\x
  v\y = v1\y
  v\z = v1\z
EndMacro

Macro rt_add(v, v1, v2)
  v\x = v1\x + v2\x
  v\y = v1\y + v2\y
  v\z = v1\z + v2\z
EndMacro

Macro rt_sub(v, v1, v2)
  v\x = v1\x - v2\x
  v\y = v1\y - v2\y
  v\z = v1\z - v2\z
EndMacro

Macro rt_mul(v, v1, v2)
  v\x = v1\x * v2\x
  v\y = v1\y * v2\y
  v\z = v1\z * v2\z
EndMacro

Macro rt_scale(v, v1, s)
  v\x = v1\x * s
  v\y = v1\y * s
  v\z = v1\z * s
EndMacro  

Macro rt_neg(v, v1)
  v\x = -v1\x
  v\y = -v1\y
  v\z = -v1\z
EndMacro

; Ray
Structure Ray
  orig.Vec3
  dir.Vec3
EndStructure


; Sphere
Structure Sphere
  center.Vec3
  radius.f
  color.Vec3
EndStructure

; Clamp
Procedure.f clamp(x.f, min.f, max.f)
  If(x < min)
    ProcedureReturn min
  EndIf
  If(x > max)
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

; Random
Procedure.f random_float()   
    r.f = Random(#MAXDWORD) / #MAXDWORD  
    ProcedureReturn r
EndProcedure

Procedure.f random_min_max_float(min.f, max.f)     
    ProcedureReturn min + (max-min) * random_float()
EndProcedure

Procedure.f random_vec(*vx.Vec3)
  *vx\x = random_float()
  *vx\y = random_float()
  *vx\z = random_float()
  
EndProcedure

Procedure.f random_min_max_vec(min.f, max.f, axe)     
  
  If axe = 1
     x.f = random_min_max_float(min, max)
    ProcedureReturn x
  EndIf
  
  If axe = 2
    y.f = random_min_max_float(min, max)
    ProcedureReturn y
  EndIf
  
  If axe = 3
    z.f = random_min_max_float(min, max)
    ProcedureReturn z
  EndIf
  
  
EndProcedure

Procedure.f unit_vector_vec(*v.Vec3, axe)

  Protected n.f = 0.0
   
  rt_norm(*v, n)
   
  If axe = 1
    ProcedureReturn *v\x
  EndIf
  
  If axe = 2
    ProcedureReturn *v\y
  EndIf
  
  If axe = 2
    ProcedureReturn *v\z
  EndIf
  
EndProcedure  

Procedure.f random_in_unit_sphere(axe)
  Protected p.Vec3
  
   
  While #True
    
    p\x = random_min_max_vec(-1.0, 1.0, 1)
    p\x = random_min_max_vec(-1.0, 1.0, 2)
    p\x = random_min_max_vec(-1.0, 1.0, 3)

    
    r.f = rt_v_length_squared(p)
    If r < 1.0
       If axe = 1
             
          ProcedureReturn p\x
     
        EndIf
          
   
          
       If axe = 2
    
          ProcedureReturn p\y
     
        EndIf
      
      
      
       If axe = 3
      
         ProcedureReturn p\z    
         
       EndIf
       
     EndIf
     
            
  Wend
  
EndProcedure

Procedure.f random_unit_vector(axe)
  Protected p.Vec3
  
  p\x = random_in_unit_sphere(1)
  p\y = random_in_unit_sphere(2)
  p\z = random_in_unit_sphere(3)
  
  
  ProcedureReturn unit_vector_vec(p, axe)
  
EndProcedure

Procedure.f random_on_hemisphere(*in.Vec3, *out.Vec3)
  Protected on_unit_sphere.Vec3
  
  on_unit_sphere\x = random_unit_vector(1)
  on_unit_sphere\y = random_unit_vector(2)
  on_unit_sphere\z = random_unit_vector(3)
  
  If rt_dot(on_unit_sphere, *in) > 0.0
   
    rt_copy(*out, on_unit_sphere)
    
  Else
    
    rt_neg(*out, on_unit_sphere)
    
    
  EndIf     
  
EndProcedure

; Surrounds
Procedure surrounds(x.f, min.f, max.f)
  ProcedureReturn Bool( min < x And x < max)
EndProcedure

; Create Sphere primitve
Macro m_Sphere(id, center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  scene(id)\center\x = center_x
  scene(id)\center\y = center_y
  scene(id)\center\z = center_z
  scene(id)\radius = radius_
  scene(id)\color\x = color_x
  scene(id)\color\y = color_y
  scene(id)\color\z = color_z
EndMacro


Global Dim scene.Sphere(3)
m_Sphere(0, 0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
m_Sphere(1, -1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(2, 1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(3, 0.0, -100.0, -1.0, 100.0, 1.0, 1.0, 1.0)


; Hit for 3D objects
Procedure.f hit( *_s.Sphere, *_r.Ray, mx.f)
  
  Protected oc.Vec3
  Protected root.f = 0.0
  Protected min.f = 0.0
  Protected max.f = mx
  
  rt_sub(oc, *_r\orig, *_s\center) 
   
  Protected a.f = rt_dot(*_r\dir, *_r\dir)
  Protected b.f = rt_dot(oc,*_r\dir)
  Protected c.f = rt_dot(oc,oc) - *_s\radius *  *_s\radius
  
  Protected discriminant.f = b*b - a*c
  
  
  If discriminant < 0.0
    ProcedureReturn 0.0
  EndIf

    root = (-b - Sqr(discriminant)) / (a)    
    
    If (Not rt_surround(root, min, max))
      root = (-b + Sqr(discriminant)) / (a)
         
     If ( Not rt_surround(root, min, max))
       ProcedureReturn 0.0   
    EndIf
  
    
  EndIf 
  
        
    ProcedureReturn root
  
EndProcedure


; Hit for intersection
Procedure.b intersect( *_r.Ray, *t.Float, *id.Integer )
  Protected d.f, inf.f = 0.999
  Protected i.i = ArraySize(scene())
  *t\f = inf
  While i >= 0
    d = hit( scene(i), *_r, inf )
    If d 
      *t\f  = d
      *id\i = i
    EndIf
    i - 1
  Wend
  ProcedureReturn Bool( *t\f < inf )
EndProcedure

Procedure.f unit_vector_y(*vy.Ray)
  Protected len.f
  Static y.f : y.f = 0
  len = rt_r_length(*vy)
  If Not len = 0 
    y = *vy\dir\y / len 
   EndIf
  ProcedureReturn y
EndProcedure

Procedure.f ray_color(*rr.Ray, *in.Vec3)
  Protected a.f
  Static color1.Vec3, color2.Vec3, unit_direction.Vec3
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
  
  unit_direction\x = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\x + 1.0) 
  *in\x =  (1.0 - a) * color1\x + a * color2\x 
  
  unit_direction\y = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\y + 1.0)
  *in\y =  (1.0 - a) * color1\y + a * color2\y
  
  unit_direction\z = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\z + 1.0)
  *in\z =  (1.0 - a) * color1\z + a * color2\z
  
EndProcedure  

Procedure.f ray_color_hit(*_r.Ray, *in.Vec3, depth)
  Protected x.Vec3
  Protected p.Vec3
  Protected ntemp.f
  Protected t.f = 0.0
  Protected id.i = 0
  
  If depth <= 0
    *in\x = 0.0
    *in\y = 0.0
    *in\z = 0.0
  EndIf  
  
    
  If intersect(*_r, @t, @id)
    Protected *obj.Sphere = scene(id)
    
    ; Point at parameter
    x\x = *_r\orig\x + t * *_r\dir\x
    x\y = *_r\orig\y + t * *_r\dir\y
    x\z = *_r\orig\z + t * *_r\dir\z
    
    ; Norm
    Protected n.Vec3
    rt_sub( n, x, *obj\center)
    rt_norm( n, ntemp )
    
    ; Set to normal
    Protected nl.Vec3
    If rt_dot( n, *_r\dir ) < 0.0
      rt_copy( nl, n )
    Else
      rt_neg( nl, n )
    EndIf
    
;     *in\x = 0.5 * (nl\x + 1.0)
;     *in\y = 0.5 * (nl\y + 1.0)
;     *in\z = 0.5 * (nl\z + 1.0)
    
    random_on_hemisphere(nl, p)
    
    *_r\dir\x = p\x - *_r\orig\x
    *_r\dir\y = p\y - *_r\orig\y
    *_r\dir\z = p\z - *_r\orig\z
    
;     *in\x = 0.5 * (p\x + 1.0)
;     *in\y = 0.5 * (p\y + 1.0)
;     *in\z = 0.5 * (p\z + 1.0)
    
   ProcedureReturn 0.5 * ray_color_hit(*_r, *in, depth-1)
    

    
  Else
    
    ; Render to background
    ray_color(*_r, *in)
    
  EndIf
  
EndProcedure


Define aspect_ratio.f = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
   
Shared image_width
Shared image_height

Static focal_length.f = 1.0
Static viewport_height.f = 2.0

Protected i_w.f = image_width
Protected i_h.f = image_height
Protected viewport_width.f = viewport_height * (i_w/i_h)

Protected point3.Vec3

point3\x = 0.0
point3\y = 0.0
point3\z = 0.0

Protected ray_direction.Vec3

ray_direction\x = 0
ray_direction\y = 0
ray_direction\z = 0

Protected viewport_u.Vec3

viewport_u\x = viewport_width
viewport_u\y = 0
viewport_u\z = 0

Protected viewport_v.Vec3

viewport_v\x = 0
viewport_v\y = -viewport_height
viewport_v\z = 0

Protected pixel_delta_u.Vec3

pixel_delta_u\x = viewport_u\x / i_w
pixel_delta_u\y = viewport_u\y / i_w
pixel_delta_u\z = viewport_u\z / i_w

Protected pixel_delta_v.Vec3

pixel_delta_v\x = viewport_v\x / i_h
pixel_delta_v\y = viewport_v\y / i_h
pixel_delta_v\z = viewport_v\z / i_h

Protected viewport_upper_left.Vec3

Protected v_f.Vec3

v_f\x = 0.0
v_f\y = 0.0
v_f\z = focal_length

viewport_upper_left\x = point3\x - v_f\x - viewport_u\x / 2 - viewport_v\x / 2
viewport_upper_left\y = point3\y - v_f\y - viewport_u\y / 2 - viewport_v\y / 2
viewport_upper_left\z = point3\z - v_f\z - viewport_u\z / 2 - viewport_v\z / 2

Protected pixel_100_loc.Vec3

pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)

Protected sample_per_pixel.i = 10
Protected pixel_sample_scale.f = 1.0 / sample_per_pixel

Protected max_depth.i = 3

Protected x.f, y.f, z.f, v.vec3

Static offset.Vec3, pixel_sample.Vec3

If StartDrawing(ScreenOutput())

For j = 0 To image_height-1
  For i = 0 To image_width-1
      
      ii.f = i
      jj.f = j
      
      x = 0.0
      y = 0.0
      z = 0.0
   
      
      For sample = 0 To sample_per_pixel
        
      offset\x = random_float() -0.5 
      offset\y = random_float() -0.5 
      offset\z = 0.0
      
      
      pixel_sample\x = pixel_100_loc\x + ( (ii+offset\x) * pixel_delta_u\x) + ( (jj+offset\y) * pixel_delta_v\x)
      pixel_sample\y = pixel_100_loc\y + ( (ii+offset\x) * pixel_delta_u\y) + ( (jj+offset\y) * pixel_delta_v\y)
      pixel_sample\z = pixel_100_loc\z + ( (ii+offset\x) * pixel_delta_u\z) + ( (jj+offset\y) * pixel_delta_v\z)
      
      ray_direction\x = pixel_sample\x - point3\x
      ray_direction\y = pixel_sample\y - point3\y
      ray_direction\z = pixel_sample\z - point3\z
           
      Define.Ray r
      
      r\orig = point3
      r\dir = ray_direction          
    
      ray_color_hit(r, @v, max_depth) 
      
      x = x + v\x
      y = y + v\y
      z = z + v\z
      
      Next
      
        Plot(i, j, RGB(Int(255.999 * clamp(pixel_sample_scale * x, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale*  y, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale * z, 0.0, 0.999))))
     
       
        
      Next
    
    Next
    
EndIf
  
StopDrawing() 

EndProcedure




InitSprite()
 
 OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
 OpenWindowedScreen(WindowID(0), 0, 0, image_width , image_height )
  
  ;ClearScreen(RGB(0,0,0))
  
Define time = ElapsedMilliseconds()
render()
time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

 Repeat
   Event = WindowEvent()
   
 Until  Event = #PB_Event_CloseWindow
   
 End 
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Fixed some of my code and corrected some wrong typo, it is a bit better but still no good my pathtracer :cry:

I think it is programming language as C++ is OOP so hard to make this one in procedural programming, it is the part of color, it is the most tricky I ever see it lol so I don't know how to fix that :oops:

Dunno if it is stopped my path but I need to find other technical for 3D Rendering and not necessarily to Raytracing in one weekend ...

I have been inspired by someone (Grabiller) and that is his code I have been rewritten :) , maybe I will take his some code for diffuse pathtracer :mrgreen:
His link is : https://www.purebasic.fr/english/viewto ... 27&t=56189

Again my last code not finished :

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider


;; Rewritten by threedslider Jun 10 2024
;; Added to color for sphere and background Jun 11 2024
;; Added to Pathtracing system (WIP) Jun 11 2024
;; Corrected typo and fixed some of my code Jun 12 2024

; Vector
Structure Vec3
  x.f
  y.f
  z.f
EndStructure


; Vector for 3D Macros
Macro rt_zero(v)
  v\x = 0.0
  v\y = 0.0
  v\z = 0.0
EndMacro

Macro rt_clamp(v, m1, m2)
  v = clamp(v, m1, m2)
EndMacro

Macro rt_surround(x, m1, m2)
  surrounds(x, m1, m2)
EndMacro

Macro rt_norm(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x * n
    v\y * n
    v\z * n
  EndIf
  
EndMacro

Macro rt_norm2(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x = v\x * n
    v\y = v\y * n
    v\z = v\z * n
  EndIf
  
EndMacro

Macro rt_r_length(v)
  Sqr(v\dir\x * v\dir\x + v\dir\y * v\dir\y + v\dir\z * v\dir\z)
EndMacro

Macro rt_v_length_squared(v)
  (v\x * v\x + v\y * v\y + v\z * v\z)
EndMacro 

Macro rt_v_length(v)
  Sqr(rt_v_length_squared(v))
EndMacro

  
Macro rt_dot(v1, v2)
  (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z)
EndMacro

Macro rt_copy(v, v1)
  v\x = v1\x
  v\y = v1\y
  v\z = v1\z
EndMacro

Macro rt_add(v, v1, v2)
  v\x = v1\x + v2\x
  v\y = v1\y + v2\y
  v\z = v1\z + v2\z
EndMacro

Macro rt_sub(v, v1, v2)
  v\x = v1\x - v2\x
  v\y = v1\y - v2\y
  v\z = v1\z - v2\z
EndMacro

Macro rt_mul(v, v1, v2)
  v\x = v1\x * v2\x
  v\y = v1\y * v2\y
  v\z = v1\z * v2\z
EndMacro

Macro rt_scale(v, v1, s)
  v\x = v1\x * s
  v\y = v1\y * s
  v\z = v1\z * s
EndMacro  

Macro rt_neg(v, v1)
  v\x = -v1\x
  v\y = -v1\y
  v\z = -v1\z
EndMacro

; Ray
Structure Ray
  orig.Vec3
  dir.Vec3
EndStructure


; Sphere
Structure Sphere
  center.Vec3
  radius.f
  color.Vec3
EndStructure

; Hit record
Structure Hit_record
  p.Vec3
  normal.Vec3
  t.f
EndStructure

N.i = 3

Dim A_hit.Hit_record(N)

Procedure init_zero_hit_record()
  Shared A_hit.hit_record()
  
  For i=0 To N-1
    A_hit(i)\t = 0.0
    A_hit(i)\p\x = 0.0
    A_hit(i)\p\y = 0.0
    A_hit(i)\p\z = 0.0
    A_hit(i)\normal\x = 0.0
    A_hit(i)\normal\y = 0.0
    A_hit(i)\normal\z = 0.0
  Next
EndProcedure

; Clamp
Procedure.f clamp(x.f, min.f, max.f)
  If(x < min)
    ProcedureReturn min
  EndIf
  If(x > max)
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

; Random
Procedure.f random_float()   
    r.f = Random(#MAXDWORD) / #MAXDWORD  
    ProcedureReturn r
EndProcedure

Procedure.f random_min_max_float(min.f, max.f)     
    ProcedureReturn min + (max-min) * random_float()
EndProcedure

Procedure.f random_vec(*vx.Vec3)
  *vx\x = random_float()
  *vx\y = random_float()
  *vx\z = random_float()
  
EndProcedure

Procedure.f random_min_max_vec(min.f, max.f)     
  ProcedureReturn random_min_max_float(min, max)  
EndProcedure

Procedure.f unit_vector_vec(*v.Vec3, axe)

  Protected n.f = 0.0
  Protected p.Vec3
  
  rt_norm(*v, n)
  
  ;Debug *v\x
   
  If axe = 1
    ProcedureReturn *v\x
  EndIf
  
  If axe = 2
    ProcedureReturn *v\y
  EndIf
  
  If axe = 3
    ProcedureReturn *v\z
  EndIf
  
EndProcedure  

Procedure.f random_in_unit_sphere(axe)
  Protected p.Vec3
  
   
  While #True
    
    p\x = random_min_max_vec(-1.0, 1.0)
    p\y = random_min_max_vec(-1.0, 1.0)
    p\z = random_min_max_vec(-1.0, 1.0)

    
    r.f = rt_v_length_squared(p)
    If r < 1.0
       If axe = 1
             
          ProcedureReturn p\x
     
        EndIf
          
   
          
       If axe = 2
    
          ProcedureReturn p\y
     
        EndIf
      
      
      
       If axe = 3
      
         ProcedureReturn p\z    
         
       EndIf
       
     EndIf
     
            
  Wend
  
EndProcedure

Procedure.f random_unit_vector(axe)
  Protected p.Vec3
  
  p\x = random_in_unit_sphere(1)
  p\y = random_in_unit_sphere(2)
  p\z = random_in_unit_sphere(3)
  
  ProcedureReturn unit_vector_vec(p, axe)
  
EndProcedure

Procedure.f random_on_hemisphere(*in.Vec3, axe)
  Protected on_unit_sphere.Vec3
  
  on_unit_sphere\x = random_unit_vector(1)
  on_unit_sphere\y = random_unit_vector(2)
  on_unit_sphere\z = random_unit_vector(3)
  
  ;Debug on_unit_sphere\x
  
  If rt_dot(on_unit_sphere, *in) > 0.0
   
    ;rt_copy(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn on_unit_sphere\y
    EndIf
    
   If axe = 3
      ProcedureReturn on_unit_sphere\z
   EndIf
    
    
  Else
    
    ;rt_neg(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn -on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn -on_unit_sphere\y
    EndIf
    
   If axe = 3
      ProcedureReturn -on_unit_sphere\z
   EndIf
    
  EndIf     
  
EndProcedure

; Surrounds
Procedure surrounds(x.f, min.f, max.f)
  ProcedureReturn Bool( min < x And x < max)
EndProcedure

; Create Sphere primitve
Macro m_Sphere(id, center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  scene(id)\center\x = center_x
  scene(id)\center\y = center_y
  scene(id)\center\z = center_z
  scene(id)\radius = radius_
  scene(id)\color\x = color_x
  scene(id)\color\y = color_y
  scene(id)\color\z = color_z
EndMacro


Global Dim scene.Sphere(3)
m_Sphere(0, 0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
m_Sphere(1, -1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(2, 1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(3, 0.0, -100.0, -1.0, 100.0, 1.0, 1.0, 1.0)



; Hit for 3D objects
Procedure.f hit( *_s.Sphere, *_r.Ray, mx.f)
  
  Protected oc.Vec3
  Protected root.f = 0.0
  Protected min.f = 0.0
  Protected max.f = mx
  
  rt_sub(oc, *_r\orig, *_s\center) 
   
  Protected a.f = rt_dot(*_r\dir, *_r\dir)
  Protected b.f = rt_dot(oc,*_r\dir)
  Protected c.f = rt_dot(oc,oc) - *_s\radius *  *_s\radius
  
  Protected discriminant.f = b*b - a*c
  
  
  If discriminant < 0.0
    ProcedureReturn 0.0
  EndIf

    root = (-b - Sqr(discriminant)) / (a)    
    
    If (Not rt_surround(root, min, max))
      root = (-b + Sqr(discriminant)) / (a)
         
     If ( Not rt_surround(root, min, max))
       ProcedureReturn 0.0   
    EndIf
  
    
  EndIf 
  
        
    ProcedureReturn root
  
EndProcedure


; Hit for intersection
Procedure.b intersect( *_r.Ray, *t.Float, *id.Integer )
  Protected d.f, inf.f = 0.999
  Protected i.i = ArraySize(scene())
  *t\f = inf
  While i >= 0
    d = hit( scene(i), *_r, inf )
    If d 
      *t\f  = d
      *id\i = i
    EndIf
    i - 1
  Wend
  ProcedureReturn Bool( *t\f < inf )
EndProcedure

Procedure.f unit_vector_y(*vy.Ray)
  Protected len.f
  Static y.f : y.f = 0
  len = rt_r_length(*vy)
  If Not len = 0 
    y = *vy\dir\y / len 
   EndIf
  ProcedureReturn y
EndProcedure

Procedure.f ray_color(*rr.Ray, *in.Vec3)
  Protected a.f
  Static color1.Vec3, color2.Vec3, unit_direction.Vec3
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
  
  unit_direction\x = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\x + 1.0) 
  *in\x =  (1.0 - a) * color1\x + a * color2\x 
  
  unit_direction\y = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\y + 1.0)
  *in\y =  (1.0 - a) * color1\y + a * color2\y
  
  unit_direction\z = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\z + 1.0)
  *in\z =  (1.0 - a) * color1\z + a * color2\z  
EndProcedure  

Procedure.f ray_color_hit(*_r.Ray, *in.Vec3, depth)
  Protected x.Vec3
  Protected p.Vec3
  Protected ntemp.f
  Protected t.f = 0.0
  Protected id.i = 0
  Shared A_hit.Hit_record()
  Protected color.f 
  
  If depth <= 0
    *in\x = 0.0
    *in\y = 0.0
    *in\z = 0.0
  EndIf  
      
  If intersect(*_r, @t, @id)
    Protected *obj.Sphere = scene(id)
    
    ; Point at parameter
    x\x = *_r\orig\x + t * *_r\dir\x
    x\y = *_r\orig\y + t * *_r\dir\y
    x\z = *_r\orig\z + t * *_r\dir\z
    
    ; Norm
    Protected n.Vec3
    rt_sub( n, x, *obj\center)
    rt_norm( n, ntemp )
    
    ; Set to normal
    Protected nl.Vec3
    If rt_dot( n, *_r\dir ) < 0.0
      rt_copy( nl, n )
    Else
      rt_neg( nl, n )
    EndIf
    
      *_r\dir\x = nl\x + random_unit_vector(1) 
      *_r\dir\y = nl\y + random_unit_vector(2)  
      *_r\dir\z = nl\z + random_unit_vector(3) 
    
    
    
;     *in\x = 0.5 * (p\x + 1.0 )
;     *in\y = 0.5 * (p\y + 1.0 )
;     *in\z = 0.5 * (p\z + 1.0 )
      ;If axe = 1  
            
            
         ProcedureReturn 0.5 * ray_color_hit(*_r, *in, depth-1)

    
  Else
    
    ; Render to background
   ray_color(*_r, *in)
    
  EndIf
  
EndProcedure




Define aspect_ratio.f = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
  
Shared A_hit.Hit_record()  
  
Shared image_width
Shared image_height

Static focal_length.f = 1.0
Static viewport_height.f = 2.0

Protected i_w.f = image_width
Protected i_h.f = image_height
Protected viewport_width.f = viewport_height * (i_w/i_h)

Protected point3.Vec3

point3\x = 0.0
point3\y = 0.0
point3\z = 0.0

Protected ray_direction.Vec3

ray_direction\x = 0
ray_direction\y = 0
ray_direction\z = 0

Protected viewport_u.Vec3

viewport_u\x = viewport_width
viewport_u\y = 0
viewport_u\z = 0

Protected viewport_v.Vec3

viewport_v\x = 0
viewport_v\y = -viewport_height
viewport_v\z = 0

Protected pixel_delta_u.Vec3

pixel_delta_u\x = viewport_u\x / i_w
pixel_delta_u\y = viewport_u\y / i_w
pixel_delta_u\z = viewport_u\z / i_w

Protected pixel_delta_v.Vec3

pixel_delta_v\x = viewport_v\x / i_h
pixel_delta_v\y = viewport_v\y / i_h
pixel_delta_v\z = viewport_v\z / i_h

Protected viewport_upper_left.Vec3

Protected v_f.Vec3

v_f\x = 0.0
v_f\y = 0.0
v_f\z = focal_length

viewport_upper_left\x = point3\x - v_f\x - viewport_u\x / 2 - viewport_v\x / 2
viewport_upper_left\y = point3\y - v_f\y - viewport_u\y / 2 - viewport_v\y / 2
viewport_upper_left\z = point3\z - v_f\z - viewport_u\z / 2 - viewport_v\z / 2

Protected pixel_100_loc.Vec3

pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)

Protected sample_per_pixel.i = 10
Protected pixel_sample_scale.f = 1.0 / sample_per_pixel

Protected max_depth.i = 1

Protected x.f, y.f, z.f, v.vec3

Static offset.Vec3, pixel_sample.Vec3

init_zero_hit_record()

If StartDrawing(ScreenOutput())

For j = 0 To image_height-1
  For i = 0 To image_width-1
      
      ii.f = i
      jj.f = j
      
      x = 0.0
      y = 0.0
      z = 0.0
   
      
      For sample = 0 To sample_per_pixel
        
      offset\x = random_float() -0.5 
      offset\y = random_float() -0.5 
      offset\z = 0.0
      
      
      pixel_sample\x = pixel_100_loc\x + ( (ii+offset\x) * pixel_delta_u\x) + ( (jj+offset\y) * pixel_delta_v\x)
      pixel_sample\y = pixel_100_loc\y + ( (ii+offset\x) * pixel_delta_u\y) + ( (jj+offset\y) * pixel_delta_v\y)
      pixel_sample\z = pixel_100_loc\z + ( (ii+offset\x) * pixel_delta_u\z) + ( (jj+offset\y) * pixel_delta_v\z)
      
      ray_direction\x = pixel_sample\x - point3\x
      ray_direction\y = pixel_sample\y - point3\y
      ray_direction\z = pixel_sample\z - point3\z
           
      Define.Ray r
      
      r\orig = point3
      r\dir = ray_direction          
    
      ray_color_hit(r, @v, max_depth) 
      
      x = x + v\x
      y = y + v\y
      z = z + v\z
      
      Next
      
        Plot(i, j, RGB(Int(255.999 * clamp(pixel_sample_scale * x, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale*  y, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale * z, 0.0, 0.999))))
     
       
        
      Next
    
    Next
    
EndIf
  
StopDrawing() 

EndProcedure




InitSprite()
 
 OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
 OpenWindowedScreen(WindowID(0), 0, 0, image_width , image_height )
  
  ;ClearScreen(RGB(0,0,0))
  
Define time = ElapsedMilliseconds()
render()
time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

 Repeat
   Event = WindowEvent()
   
 Until  Event = #PB_Event_CloseWindow
   
 End 
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Fixed some of my ray for random, it looks nice but no shadow :-/

I need to fix my shadow right now ... :lol:

However it takes now more 24k ms to render :shock:

Here is my code as always and it is wip :

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider


;; Rewritten by threedslider Jun 10 2024
;; Added to color for sphere and background Jun 11 2024
;; Added to Pathtracing system (WIP) Jun 11 2024
;; Corrected typo and fixed some of my code Jun 12 2024
;; Fixed to Ray for random Jun 14 2024

; Vector
Structure Vec3
  x.f
  y.f
  z.f
EndStructure


; Vector for 3D Macros
Macro rt_zero(v)
  v\x = 0.0
  v\y = 0.0
  v\z = 0.0
EndMacro

Macro rt_near_zero(v)
  near_zero(v)
EndMacro

Macro rt_clamp(v, m1, m2)
  v = clamp(v, m1, m2)
EndMacro

Macro rt_surround(x, m1, m2)
  surrounds(x, m1, m2)
EndMacro

Macro rt_norm(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x * n
    v\y * n
    v\z * n
  EndIf
  
EndMacro

Macro rt_r_length(v)
  Sqr(v\dir\x * v\dir\x + v\dir\y * v\dir\y + v\dir\z * v\dir\z)
EndMacro

Macro rt_v_length_squared(v)
  (v\x * v\x + v\y * v\y + v\z * v\z)
EndMacro


  
Macro rt_dot(v1, v2)
  (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z)
EndMacro

Macro rt_copy(v, v1)
  v\x = v1\x
  v\y = v1\y
  v\z = v1\z
EndMacro

Macro rt_add(v, v1, v2)
  v\x = v1\x + v2\x
  v\y = v1\y + v2\y
  v\z = v1\z + v2\z
EndMacro

Macro rt_sub(v, v1, v2)
  v\x = v1\x - v2\x
  v\y = v1\y - v2\y
  v\z = v1\z - v2\z
EndMacro

Macro rt_mul(v, v1, v2)
  v\x = v1\x * v2\x
  v\y = v1\y * v2\y
  v\z = v1\z * v2\z
EndMacro

Macro rt_scale(v, v1, s)
  v\x = v1\x * s
  v\y = v1\y * s
  v\z = v1\z * s
EndMacro  

Macro rt_neg(v, v1)
  v\x = -v1\x
  v\y = -v1\y
  v\z = -v1\z
EndMacro

; Ray
Structure Ray
  orig.Vec3
  dir.Vec3
EndStructure

; Sphere
Structure Sphere
  center.Vec3
  radius.f
  color.Vec3
EndStructure

; Material
Structure Material
  albedo.Vec3
EndStructure


; Clamp
Procedure.f clamp(x.f, min.f, max.f)
  If(x < min)
    ProcedureReturn min
  EndIf
  If(x > max)
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

; Random
Procedure.f random_float()   
    r.f = Random(#MAXDWORD) / #MAXDWORD  
    ProcedureReturn r
EndProcedure

Procedure.f random_min_max_float(min.f, max.f)     
    ProcedureReturn min + (max-min) * random_float()
EndProcedure

Procedure.f random_vec(*vx.Vec3)
  *vx\x = random_float()
  *vx\y = random_float()
  *vx\z = random_float()
  
EndProcedure

Procedure.f random_min_max_vec(min.f, max.f)     
  ProcedureReturn random_min_max_float(min, max)  
EndProcedure

Procedure.f unit_vector_vec(*v.Vec3, axe)

  Protected n.f = 0.0
  Protected p.Vec3
  
  rt_norm(*v, n)
   
  If axe = 1
    ProcedureReturn *v\x
  EndIf
  
  If axe = 2
    ProcedureReturn *v\y
  EndIf
  
  If axe = 3
    ProcedureReturn *v\z
  EndIf
  
EndProcedure  

Procedure.f random_in_unit_sphere(axe)
  Protected p.Vec3
  
   
  While #True
    
    p\x = random_min_max_float(-1.0, 1.0)
    p\y = random_min_max_float(-1.0, 1.0)
    p\z = random_min_max_float(-1.0, 1.0)

    
    r.f = rt_v_length_squared(p)
    If r < 1.0
       If axe = 1
             
          ProcedureReturn p\x
     
        EndIf
          
   
          
       If axe = 2
    
          ProcedureReturn p\y
     
        EndIf
      
      
      
       If axe = 3
      
         ProcedureReturn p\z    
         
       EndIf
       
     EndIf
     
            
  Wend
  
EndProcedure

Procedure.f random_unit_vector(axe)
  Protected p.Vec3
  
  p\x = random_in_unit_sphere(1)
  p\y = random_in_unit_sphere(2)
  p\z = random_in_unit_sphere(3)
  
  ProcedureReturn unit_vector_vec(p, axe)
  
EndProcedure

Procedure.f random_on_hemisphere(*in.Vec3, axe)
  Protected on_unit_sphere.Vec3
  
  on_unit_sphere\x = random_unit_vector(1)
  on_unit_sphere\y = random_unit_vector(2)
  on_unit_sphere\z = random_unit_vector(3)
  
  ;Debug on_unit_sphere\x
  
  If rt_dot(on_unit_sphere, *in) > 0.0
   
    ;rt_copy(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn on_unit_sphere\y
    EndIf
    
   If axe = 3
      ProcedureReturn on_unit_sphere\z
   EndIf
    
    
  Else
    
    ;rt_neg(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn -on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn -on_unit_sphere\y
    EndIf
    
   If axe = 3
      ProcedureReturn -on_unit_sphere\z
   EndIf
    
  EndIf     
  
EndProcedure

; Surrounds
Procedure surrounds(x.f, min.f, max.f)
  ProcedureReturn Bool( min < x And x < max)
EndProcedure

; Procedure surrounds(x.f, min.f, max.f)
;   ProcedureReturn Bool( x <= min  Or  max <= x)
; EndProcedure

; Create Sphere primitve
Macro m_Sphere(id, center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  scene(id)\center\x = center_x
  scene(id)\center\y = center_y
  scene(id)\center\z = center_z
  scene(id)\radius = radius_
  scene(id)\color\x = color_x
  scene(id)\color\y = color_y
  scene(id)\color\z = color_z
EndMacro


Global Dim scene.Sphere(3)
m_Sphere(0, 0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
m_Sphere(1, -1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(2, 1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(3, 0.0, -100.0+0.005, -1.0, 100.0, 1.0, 1.0, 1.0)


;Near zero for pathtracing calculation
Procedure near_zero (*v.Vec3)
  Protected e.f = 1e-8
  ProcedureReturn Bool((Abs(*v\x) < e) And (Abs(*v\y) < e)And (Abs(*v\z) < e))
EndProcedure

; Init for vector
Procedure v_init(x.f, y.f, z.f, *v.Vec3)
  *v\x = x
  *v\y = y
  *v\z = z
EndProcedure


; Scattering randomly
Procedure.f scatter(*r.Ray, *rec.Vec3, *attenuation.Vec3)
  Protected scatter_direction.Vec3
  Protected ran_dir.Vec3
  

  ran_dir\x = random_unit_vector(1)
  ran_dir\y = random_unit_vector(2)
  ran_dir\z = random_unit_vector(3)
  
  rt_add(scatter_direction, *rec, ran_dir)
  
  If rt_near_zero(scatter_direction) 
    rt_copy(scatter_direction, *rec)
  EndIf
  
  
  *r\dir\x = unit_vector_vec(scatter_direction, 1)  
  *r\dir\y = unit_vector_vec(scatter_direction, 2)  
  *r\dir\z = unit_vector_vec(scatter_direction, 3)  
  
EndProcedure

Procedure.f diffuse_scatter(*v1.Vec3, *v2.Vec3)
EndProcedure

  

; Hit for 3D objects
Procedure.f hit( *_s.Sphere, *_r.Ray, mx.f)
  
  Protected oc.Vec3
  Protected root.f = 0.0
  Protected min.f = 0.0
  Protected max.f = mx
  
  rt_sub(oc, *_r\orig, *_s\center) 
   
  Protected a.f = rt_dot(*_r\dir, *_r\dir)
  Protected b.f = rt_dot(oc,*_r\dir)
  Protected c.f = rt_dot(oc,oc) - *_s\radius *  *_s\radius
  
  Protected discriminant.f = b*b - a*c
  
  
  If discriminant < 0.0
    ProcedureReturn 0.0
  EndIf
  
  root = (-b - Sqr(discriminant)) / (a)

    

    If (Not rt_surround(root, min, max))
      root = (-b + Sqr(discriminant)) / (2.0*a)
         
     If ( Not rt_surround(root, min, max))
       ProcedureReturn 0.0   
    EndIf
  
    
  EndIf 
  
        
    ProcedureReturn root
  
EndProcedure


; Hit for intersection
Procedure.b intersect( *_r.Ray, *t.Float, *id.Integer )
  Protected d.f, inf.f = 0.999
  Protected i.i = ArraySize(scene())
  *t\f = inf
  While i >= 0
    d = hit( scene(i), *_r, inf )
    If d 
      *t\f  = d
      *id\i = i
    EndIf
    i - 1
  Wend
  ProcedureReturn Bool( *t\f < inf )
EndProcedure

; Unit vector Y for background
Procedure.f unit_vector_y(*vy.Ray)
  Protected len.f
  Static y.f : y.f = 0
  len = rt_r_length(*vy)
  If Not len = 0 
    y = *vy\dir\y / len 
   EndIf
  ProcedureReturn y
EndProcedure

; Procedure.f ray_color(*rr.Ray, *in.Vec3)
;   Protected a.f
;   Static color1.Vec3, color2.Vec3, unit_direction.Vec3
;   
;   color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
;   color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
;   
;   unit_direction\x = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\x + 1.0) 
;   *in\x =  (1.0 - a) * color1\x + a * color2\x 
;   
;   unit_direction\y = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\y + 1.0)
;   *in\y =  (1.0 - a) * color1\y + a * color2\y
;   
;   unit_direction\z = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\z + 1.0)
;   *in\z =  (1.0 - a) * color1\z + a * color2\z  
; EndProcedure  

Procedure.f ray_color(*rr.Ray, *in.Vec3, axe)
  Protected a.f
  Static color1.Vec3, color2.Vec3, unit_direction.Vec3
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
  
  unit_direction\x = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\x + 1.0) 
  *in\x =  (1.0 - a) * color1\x + a * color2\x 
  
  unit_direction\y = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\y + 1.0)
  *in\y =  (1.0 - a) * color1\y + a * color2\y
  
  unit_direction\z = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\z + 1.0)
  *in\z =  (1.0 - a) * color1\z + a * color2\z  
  
  If axe = 1
    ProcedureReturn *in\x
  EndIf
  If axe = 2
    ProcedureReturn *in\y
  EndIf
  If axe = 3
    ProcedureReturn *in\z
  EndIf
  
EndProcedure  

; Procedure.f ray_color_hit(*_r.Ray, *in.Vec3, depth)
;   Protected x.Vec3
;   Protected p.Vec3
;   Protected ntemp.f
;   Protected t.f = 0.0
;   Protected id.i = 0
;   
;   If depth <= 0
;     v_init(0.0, 0.0, 0.0, p)
;   EndIf  
;       
;   If intersect(*_r, @t, @id)
;     Protected *obj.Sphere = scene(id)
;     
;     ; Point at parameter
;     x\x = *_r\orig\x + t * *_r\dir\x
;     x\y = *_r\orig\y + t * *_r\dir\y
;     x\z = *_r\orig\z + t * *_r\dir\z
;     
;     ; Norm
;     Protected n.Vec3
;     rt_sub( n, x, *obj\center)
;     rt_norm( n, ntemp )
;     
;     ; Set to normal
;     Protected nl.Vec3
;     If rt_dot( n, *_r\dir ) < 0.0
;       rt_copy( nl, n )
;     Else
;       rt_neg( nl, n )
;     EndIf
;     
;       *_r\dir\x = nl\x + random_unit_vector(1) 
;       *_r\dir\y = nl\y + random_unit_vector(2)  
;       *_r\dir\z = nl\z + random_unit_vector(3) 
;             
; ;       *in\x =  *in\x * ray_color_hit(*_r, *in, depth-1)
; ;       *in\y =  *in\y * ray_color_hit(*_r, *in, depth-1)
; ;       *in\z =  *in\z * ray_color_hit(*_r, *in, depth-1)
; ;     
;       
;       *in\x =  0.1
;       *in\y =  0.1
;       *in\z =  0.1
;       
;       rt_mul(*in, *in, *in)
;       ray_color_hit(*_r, *in , depth-1)
;     
;   Else
;     
;     ; Render to background
;    ray_color(*_r, *in)
;     
;   EndIf
;   
; EndProcedure

Procedure.f ray_color_hit(*_r.Ray, *in.Vec3, depth, axe)
  Protected x.Vec3
  Protected p.Vec3
  Protected ntemp.f
  Protected t.f = 0.0
  Protected id.i = 0
  Protected rr.Ray
  Protected unit_vector.Ray
  
  
  If depth = 0
    If axe = 1
      *in\x = 0.0
      ProcedureReturn *in\x
    EndIf
    
    If axe = 2
      *in\y = 0.0
      ProcedureReturn *in\y
    EndIf
    
    If axe = 1
      *in\z = 0.0
      ProcedureReturn *in\z
    EndIf
    
  EndIf  
      
  If intersect(*_r, @t, @id)
    Protected *obj.Sphere = scene(id)
    
    ; Point at parameter
    x\x = *_r\orig\x + t * *_r\dir\x
    x\y = *_r\orig\y + t * *_r\dir\y
    x\z = *_r\orig\z + t * *_r\dir\z
    
    ; Norm
    Protected n.Vec3
    rt_sub( n, x, *obj\center)
    rt_norm( n, ntemp )
    
    ; Set to normal
    Protected nl.Vec3
    If rt_dot( n, *_r\dir ) < 0.0
      rt_copy( nl, n )
    Else
      rt_neg( nl, n )
    EndIf
    
        ;;** VERY SLOW ** ;;    
;         rr\orig\x = x\x
;         rr\orig\y = x\y
;         rr\orig\z = x\z
        
        ;; QUITE FAST ;;
        rr\orig\x = *_r\orig\x
        rr\orig\y = *_r\orig\y
        rr\orig\z = *_r\orig\z
               
        rr\dir\x =  random_on_hemisphere(nl, 1)
        rr\dir\y =  random_on_hemisphere(nl, 2)
        rr\dir\z =  random_on_hemisphere(nl, 3)    
       
        p\x = 0
        p\y = 0
        p\z = 0
      
      If axe = 1
         
        *in\x = (0.5 * (ray_color_hit(rr, *in , depth-1, 1) + 0.1)) 
        ;rt_mul(*in, *in, *in)
        ;Debug *in\x
        ProcedureReturn *in\x 
      EndIf
      If axe = 2
        
        *in\y = (0.5 * (ray_color_hit(rr, *in , depth-1, 2) + 0.1)) 
        ;rt_mul(*in, *in, *in)
        ProcedureReturn *in\y 
      EndIf
      If axe = 3
        
        *in\z = (0.5 * (ray_color_hit(rr, *in , depth-1, 3) + 0.1))
        ;rt_mul(*in, *in, *in)
       
        ProcedureReturn *in\z
      EndIf
        
;       If axe = 1
;          
;         *in\x = 0.5 * (nl\x + 1.0)
;         ;rt_add(*in, p, *in)
;         ;Debug *in\x
;         ProcedureReturn *in\x 
;       EndIf
;       If axe = 2
;         
;         *in\y = 0.5 * (nl\y + 1.0)
;         ;rt_add(*in, p, *in)
;         ProcedureReturn *in\y 
;       EndIf
;       If axe = 3
;         
;         *in\z = 0.5 * (nl\z + 1.0)
;         ;rt_add(*in, p, *in)
;        
;         ProcedureReturn *in\z
;       EndIf
        
      
     
;       ray_color_hit(*_r, *in , depth-1)
    
  Else
    
    ; Render to background
    ;    ray_color(*_r, *in)
    
    unit_vector\dir\x = *_r\dir\x
    unit_vector\dir\y = *_r\dir\y
    unit_vector\dir\z = *_r\dir\z
    
    
    ProcedureReturn    ray_color(unit_vector, *in, axe)
    
  EndIf
  
EndProcedure



Define aspect_ratio.f = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
  
Shared image_width
Shared image_height

Static focal_length.f = 1.0
Static viewport_height.f = 2.0

Protected i_w.f = image_width
Protected i_h.f = image_height
Protected viewport_width.f = viewport_height * (i_w/i_h)

Protected point3.Vec3

point3\x = 0.0
point3\y = 0.0
point3\z = 0.0

Protected ray_direction.Vec3

ray_direction\x = 0
ray_direction\y = 0
ray_direction\z = 0

Protected viewport_u.Vec3

viewport_u\x = viewport_width
viewport_u\y = 0
viewport_u\z = 0

Protected viewport_v.Vec3

viewport_v\x = 0
viewport_v\y = -viewport_height
viewport_v\z = 0

Protected pixel_delta_u.Vec3

pixel_delta_u\x = viewport_u\x / i_w
pixel_delta_u\y = viewport_u\y / i_w
pixel_delta_u\z = viewport_u\z / i_w

Protected pixel_delta_v.Vec3

pixel_delta_v\x = viewport_v\x / i_h
pixel_delta_v\y = viewport_v\y / i_h
pixel_delta_v\z = viewport_v\z / i_h

Protected viewport_upper_left.Vec3

Protected v_f.Vec3

v_f\x = 0.0
v_f\y = 0.0
v_f\z = focal_length

viewport_upper_left\x = point3\x - v_f\x - viewport_u\x / 2 - viewport_v\x / 2
viewport_upper_left\y = point3\y - v_f\y - viewport_u\y / 2 - viewport_v\y / 2
viewport_upper_left\z = point3\z - v_f\z - viewport_u\z / 2 - viewport_v\z / 2

Protected pixel_100_loc.Vec3

pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)

Protected sample_per_pixel.i = 10
Protected pixel_sample_scale.f = 1.0 / sample_per_pixel

Protected max_depth.i = 5

Protected x.f, y.f, z.f, v.vec3

Static offset.Vec3, pixel_sample.Vec3

If StartDrawing(ScreenOutput())

For j = 0 To image_height-1
  For i = 0 To image_width-1
      
      ii.f = i
      jj.f = j
      
      x = 0.0
      y = 0.0
      z = 0.0
   
      
      For sample = 0 To sample_per_pixel
        
      offset\x = random_float() -0.5 
      offset\y = random_float() -0.5 
      offset\z = 0.0
      
      
      pixel_sample\x = pixel_100_loc\x + ( (ii+offset\x) * pixel_delta_u\x) + ( (jj+offset\y) * pixel_delta_v\x)
      pixel_sample\y = pixel_100_loc\y + ( (ii+offset\x) * pixel_delta_u\y) + ( (jj+offset\y) * pixel_delta_v\y)
      pixel_sample\z = pixel_100_loc\z + ( (ii+offset\x) * pixel_delta_u\z) + ( (jj+offset\y) * pixel_delta_v\z)
      
      ray_direction\x = pixel_sample\x - point3\x
      ray_direction\y = pixel_sample\y - point3\y
      ray_direction\z = pixel_sample\z - point3\z
           
      Define.Ray r
      
      r\orig = point3
      r\dir = ray_direction          
    
;       ray_color_hit(r, @v, max_depth) 
      
      x = x + ray_color_hit(r, @v, max_depth, 1)
      y = y + ray_color_hit(r, @v, max_depth, 2)
      z = z + ray_color_hit(r, @v, max_depth, 3)
      
      Next
      
        Plot(i, j, RGB(Int(255.999 * clamp(pixel_sample_scale * x, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale*  y, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale * z, 0.0, 0.999))))
     
       
        
      Next
    
    Next
    
EndIf
  
StopDrawing() 

EndProcedure




InitSprite()
 
 OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
 OpenWindowedScreen(WindowID(0), 0, 0, image_width , image_height )
  
  ;ClearScreen(RGB(0,0,0))
  
Define time = ElapsedMilliseconds()
render()
time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

 Repeat
   Event = WindowEvent()
   
 Until  Event = #PB_Event_CloseWindow
   
 End 

pjay
Enthusiast
Enthusiast
Posts: 252
Joined: Thu Mar 30, 2006 11:14 am

Re: Raytracing in One Weekend

Post by pjay »

It's slow due to you calculating everything far more times than necessary due to the axial seperation ('axe') method you're using.

I've only looked over your code briefly, but one of the issues you had previously was with this line:

Code: Select all

ProcedureReturn 0.5 * ray_color_hit(*_r, *in, depth-1)
Which I think should be:

Code: Select all

   *in\x * 0.5 : *in\y * 0.5 : *in\z * 0.5
    ray_color_hit(*_r, *in, depth-1)
User avatar
idle
Always Here
Always Here
Posts: 5896
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Raytracing in One Weekend

Post by idle »

see what pjay says above.
A few simple things to consider
draw to a sprite
replace some procedures with macros
and forget using floats they only slow you down.

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider


;; Rewritten by threedslider Jun 10 2024
;; Added to color for sphere and background Jun 11 2024
;; Added to Pathtracing system (WIP) Jun 11 2024
;; Corrected typo and fixed some of my code Jun 12 2024
;; Fixed to Ray for random Jun 14 2024

; Vector
Structure Vec3
  x.d
  y.d
  z.d
EndStructure

; Vector for 3D Macros
Macro rt_zero(v)
  v\x = 0.0
  v\y = 0.0
  v\z = 0.0
EndMacro

Macro rt_near_zero(v)
  near_zero(v)
EndMacro

Macro rt_clamp(v, m1, m2)
  v = clamp(v, m1, m2)
EndMacro

Macro rt_surround(x, m1, m2)
  Bool(m1 < x And x < m2)
EndMacro

Macro rt_norm(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x * n
    v\y * n
    v\z * n
  EndIf
  
EndMacro

Macro rt_r_length(v)
  Sqr(v\dir\x * v\dir\x + v\dir\y * v\dir\y + v\dir\z * v\dir\z)
EndMacro

Macro rt_v_length_squared(v)
  (v\x * v\x + v\y * v\y + v\z * v\z)
EndMacro

Macro rt_dot(v1, v2)
  (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z)
EndMacro

Macro rt_copy(v, v1)
  v\x = v1\x
  v\y = v1\y
  v\z = v1\z
EndMacro

Macro rt_add(v, v1, v2)
  v\x = v1\x + v2\x
  v\y = v1\y + v2\y
  v\z = v1\z + v2\z
EndMacro

Macro rt_sub(v, v1, v2)
  v\x = v1\x - v2\x
  v\y = v1\y - v2\y
  v\z = v1\z - v2\z
EndMacro

Macro rt_mul(v, v1, v2)
  v\x = v1\x * v2\x
  v\y = v1\y * v2\y
  v\z = v1\z * v2\z
EndMacro

Macro rt_scale(v, v1, s)
  v\x = v1\x * s
  v\y = v1\y * s
  v\z = v1\z * s
EndMacro  

Macro rt_neg(v, v1)
  v\x = -v1\x
  v\y = -v1\y
  v\z = -v1\z
EndMacro

; Ray
Structure Ray
  orig.Vec3
  dir.Vec3
EndStructure

; Sphere
Structure Sphere
  center.Vec3
  radius.d
  color.Vec3
EndStructure

; Material
Structure Material
  albedo.Vec3
EndStructure


; Clamp
Procedure.d clamp(x.d, min.d, max.d)
  If(x < min)
    ProcedureReturn min
  EndIf
  If(x > max)
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

; Random
Macro random_double()   
  Random(#MAXDWORD) / #MAXDWORD  
EndMacro 

Macro random_min_max_double(min, max)     
  (min + (max-min) * random_double())
EndMacro 

Procedure.d random_vec(*vx.Vec3)
  *vx\x = 1.0 * random_double()
  *vx\y = 1.0 * random_double()
  *vx\z = 1.0 * random_double()
  
EndProcedure

Macro random_min_max_vec(min, max)     
  (random_min_max_double(min, max))  
EndMacro 

Procedure.d unit_vector_vec(*v.Vec3, axe)
  
  Protected n.d = 0.0
  Protected p.Vec3
  
  rt_norm(*v, n)
  
  If axe = 1
    ProcedureReturn *v\x
  EndIf
  
  If axe = 2
    ProcedureReturn *v\y
  EndIf
  
  If axe = 3
    ProcedureReturn *v\z
  EndIf
  
EndProcedure  

Procedure.d random_in_unit_sphere(axe)
  Protected p.Vec3
  
  
  While #True
    
    p\x = random_min_max_double(-1.0, 1.0)
    p\y = random_min_max_double(-1.0, 1.0)
    p\z = random_min_max_double(-1.0, 1.0)
    
    
    r.d = rt_v_length_squared(p)
    If r < 1.0
      If axe = 1
        
        ProcedureReturn p\x
        
      EndIf
      
      
      
      If axe = 2
        
        ProcedureReturn p\y
        
      EndIf
      
      
      
      If axe = 3
        
        ProcedureReturn p\z    
        
      EndIf
      
    EndIf
    
    
  Wend
  
EndProcedure

Procedure.d random_unit_vector(axe)
  Protected p.Vec3
  
  p\x = random_in_unit_sphere(1)
  p\y = random_in_unit_sphere(2)
  p\z = random_in_unit_sphere(3)
  
  ProcedureReturn unit_vector_vec(p, axe)
  
EndProcedure

Procedure.d random_on_hemisphere(*in.Vec3, axe)
  Protected on_unit_sphere.Vec3
  
  on_unit_sphere\x = random_unit_vector(1)
  on_unit_sphere\y = random_unit_vector(2)
  on_unit_sphere\z = random_unit_vector(3)
  
  ;Debug on_unit_sphere\x
  
  If rt_dot(on_unit_sphere, *in) > 0.0
    
    ;rt_copy(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn on_unit_sphere\y
    EndIf
    
    If axe = 3
      ProcedureReturn on_unit_sphere\z
    EndIf
    
    
  Else
    
    ;rt_neg(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn -on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn -on_unit_sphere\y
    EndIf
    
    If axe = 3
      ProcedureReturn -on_unit_sphere\z
    EndIf
    
  EndIf     
  
EndProcedure

; Create Sphere primitve
Macro m_Sphere(id, center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  scene(id)\center\x = center_x
  scene(id)\center\y = center_y
  scene(id)\center\z = center_z
  scene(id)\radius = radius_
  scene(id)\color\x = color_x
  scene(id)\color\y = color_y
  scene(id)\color\z = color_z
EndMacro


Global Dim scene.Sphere(3)
m_Sphere(0, 0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
m_Sphere(1, -1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(2, 1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(3, 0.0, -100.0+0.005, -1.0, 100.0, 1.0, 1.0, 1.0)


;Near zero for pathtracing calculation
Procedure near_zero (*v.Vec3)
  Protected e.d = 1e-8
  ProcedureReturn Bool((Abs(*v\x) < e) And (Abs(*v\y) < e)And (Abs(*v\z) < e))
EndProcedure

; Init for vector
Procedure v_init(x.d, y.d, z.d, *v.Vec3)
  *v\x = x
  *v\y = y
  *v\z = z
EndProcedure


; Scattering randomly
Procedure.d scatter(*r.Ray, *rec.Vec3, *attenuation.Vec3)
  Protected scatter_direction.Vec3
  Protected ran_dir.Vec3
  
  
  ran_dir\x = random_unit_vector(1)
  ran_dir\y = random_unit_vector(2)
  ran_dir\z = random_unit_vector(3)
  
  rt_add(scatter_direction, *rec, ran_dir)
  
  If rt_near_zero(scatter_direction) 
    rt_copy(scatter_direction, *rec)
  EndIf
  
  
  *r\dir\x = unit_vector_vec(scatter_direction, 1)  
  *r\dir\y = unit_vector_vec(scatter_direction, 2)  
  *r\dir\z = unit_vector_vec(scatter_direction, 3)  
  
EndProcedure

Procedure.d diffuse_scatter(*v1.Vec3, *v2.Vec3)
EndProcedure



; Hit for 3D objects
Procedure.d hit( *_s.Sphere, *_r.Ray, mx.d)
  
  Protected oc.Vec3
  Protected root.d = 0.0
  Protected min.d = 0.0
  Protected max.d = mx
  
  rt_sub(oc, *_r\orig, *_s\center) 
  
  Protected a.d = rt_dot(*_r\dir, *_r\dir)
  Protected b.d = rt_dot(oc,*_r\dir)
  Protected c.d = rt_dot(oc,oc) - *_s\radius *  *_s\radius
  
  Protected discriminant.d = b*b - a*c
  
  
  If discriminant < 0.0
    ProcedureReturn 0.0
  EndIf
  
  root = (-b - Sqr(discriminant)) / (a)
  
  
  
  If Not rt_surround(root, min, max)
    root = (-b + Sqr(discriminant)) / (2.0*a)
    
    If ( Not rt_surround(root, min, max))
      ProcedureReturn 0.0   
    EndIf
    
    
  EndIf 
  
  
  ProcedureReturn root
  
EndProcedure


; Hit for intersection
Procedure.b intersect( *_r.Ray, *t.double, *id.Integer )
  Protected d.d, inf.d = 0.999
  Protected i.i = ArraySize(scene())
  *t\d = inf
  While i >= 0
    d = hit( scene(i), *_r, inf )
    If d 
      *t\d  = d
      *id\i = i
    EndIf
    i - 1
  Wend
  ProcedureReturn Bool( *t\d < inf )
EndProcedure

; Unit vector Y for background
Procedure.d unit_vector_y(*vy.Ray)
  Protected len.d
  Static y.d : y.d = 0
  len = rt_r_length(*vy)
  If Not len = 0 
    y = *vy\dir\y / len 
  EndIf
  ProcedureReturn y
EndProcedure

; Procedure.d ray_color(*rr.Ray, *in.Vec3)
;   Protected a.d
;   Static color1.Vec3, color2.Vec3, unit_direction.Vec3
;   
;   color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
;   color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
;   
;   unit_direction\x = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\x + 1.0) 
;   *in\x =  (1.0 - a) * color1\x + a * color2\x 
;   
;   unit_direction\y = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\y + 1.0)
;   *in\y =  (1.0 - a) * color1\y + a * color2\y
;   
;   unit_direction\z = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\z + 1.0)
;   *in\z =  (1.0 - a) * color1\z + a * color2\z  
; EndProcedure  

Procedure.d ray_color(*rr.Ray, *in.Vec3, axe)
  Protected a.d
  Static color1.Vec3, color2.Vec3, unit_direction.Vec3
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
  
  unit_direction\x = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\x + 1.0) 
  *in\x =  (1.0 - a) * color1\x + a * color2\x 
  
  unit_direction\y = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\y + 1.0)
  *in\y =  (1.0 - a) * color1\y + a * color2\y
  
  unit_direction\z = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\z + 1.0)
  *in\z =  (1.0 - a) * color1\z + a * color2\z  
  
  If axe = 1
    ProcedureReturn *in\x
  EndIf
  If axe = 2
    ProcedureReturn *in\y
  EndIf
  If axe = 3
    ProcedureReturn *in\z
  EndIf
  
EndProcedure  

; Procedure.d ray_color_hit(*_r.Ray, *in.Vec3, depth)
;   Protected x.Vec3
;   Protected p.Vec3
;   Protected ntemp.d
;   Protected t.d = 0.0
;   Protected id.i = 0
;   
;   If depth <= 0
;     v_init(0.0, 0.0, 0.0, p)
;   EndIf  
;       
;   If intersect(*_r, @t, @id)
;     Protected *obj.Sphere = scene(id)
;     
;     ; Point at parameter
;     x\x = *_r\orig\x + t * *_r\dir\x
;     x\y = *_r\orig\y + t * *_r\dir\y
;     x\z = *_r\orig\z + t * *_r\dir\z
;     
;     ; Norm
;     Protected n.Vec3
;     rt_sub( n, x, *obj\center)
;     rt_norm( n, ntemp )
;     
;     ; Set to normal
;     Protected nl.Vec3
;     If rt_dot( n, *_r\dir ) < 0.0
;       rt_copy( nl, n )
;     Else
;       rt_neg( nl, n )
;     EndIf
;     
;       *_r\dir\x = nl\x + random_unit_vector(1) 
;       *_r\dir\y = nl\y + random_unit_vector(2)  
;       *_r\dir\z = nl\z + random_unit_vector(3) 
;             
; ;       *in\x =  *in\x * ray_color_hit(*_r, *in, depth-1)
; ;       *in\y =  *in\y * ray_color_hit(*_r, *in, depth-1)
; ;       *in\z =  *in\z * ray_color_hit(*_r, *in, depth-1)
; ;     
;       
;       *in\x =  0.1
;       *in\y =  0.1
;       *in\z =  0.1
;       
;       rt_mul(*in, *in, *in)
;       ray_color_hit(*_r, *in , depth-1)
;     
;   Else
;     
;     ; Render to background
;    ray_color(*_r, *in)
;     
;   EndIf
;   
; EndProcedure

Procedure.d ray_color_hit(*_r.Ray, *in.Vec3, depth, axe)
  Protected x.Vec3
  Protected p.Vec3
  Protected ntemp.d
  Protected t.d = 0.0
  Protected id.i = 0
  Protected rr.Ray
  Protected unit_vector.Ray
  
  
  If depth = 0
    If axe = 1
      *in\x = 0.0
      ProcedureReturn *in\x
    EndIf
    
    If axe = 2
      *in\y = 0.0
      ProcedureReturn *in\y
    EndIf
    
    If axe = 1
      *in\z = 0.0
      ProcedureReturn *in\z
    EndIf
    
  EndIf  
  
  If intersect(*_r, @t, @id)
    Protected *obj.Sphere = scene(id)
    
    ; Point at parameter
    x\x = *_r\orig\x + t * *_r\dir\x
    x\y = *_r\orig\y + t * *_r\dir\y
    x\z = *_r\orig\z + t * *_r\dir\z
    
    ; Norm
    Protected n.Vec3
    rt_sub( n, x, *obj\center)
    rt_norm( n, ntemp )
    
    ; Set to normal
    Protected nl.Vec3
    If rt_dot( n, *_r\dir ) < 0.0
      rt_copy( nl, n )
    Else
      rt_neg( nl, n )
    EndIf
    
    ;;** VERY SLOW ** ;;    
    ;         rr\orig\x = x\x
    ;         rr\orig\y = x\y
    ;         rr\orig\z = x\z
    
    ;; QUITE FAST ;;
    rr\orig\x = *_r\orig\x
    rr\orig\y = *_r\orig\y
    rr\orig\z = *_r\orig\z
    
    rr\dir\x =  random_on_hemisphere(nl, 1)
    rr\dir\y =  random_on_hemisphere(nl, 2)
    rr\dir\z =  random_on_hemisphere(nl, 3)    
    
    p\x = 0
    p\y = 0
    p\z = 0
    
    If axe = 1
      
      *in\x = (0.5 * (ray_color_hit(rr, *in , depth-1, 1) + 0.1)) 
      ;rt_mul(*in, *in, *in)
      ;Debug *in\x
      ProcedureReturn *in\x 
    EndIf
    If axe = 2
      
      *in\y = (0.5 * (ray_color_hit(rr, *in , depth-1, 2) + 0.1)) 
      ;rt_mul(*in, *in, *in)
      ProcedureReturn *in\y 
    EndIf
    If axe = 3
      
      *in\z = (0.5 * (ray_color_hit(rr, *in , depth-1, 3) + 0.1))
      ;rt_mul(*in, *in, *in)
      
      ProcedureReturn *in\z
    EndIf
    
    ;       If axe = 1
    ;          
    ;         *in\x = 0.5 * (nl\x + 1.0)
    ;         ;rt_add(*in, p, *in)
    ;         ;Debug *in\x
    ;         ProcedureReturn *in\x 
    ;       EndIf
    ;       If axe = 2
    ;         
    ;         *in\y = 0.5 * (nl\y + 1.0)
    ;         ;rt_add(*in, p, *in)
    ;         ProcedureReturn *in\y 
    ;       EndIf
    ;       If axe = 3
    ;         
    ;         *in\z = 0.5 * (nl\z + 1.0)
    ;         ;rt_add(*in, p, *in)
    ;        
    ;         ProcedureReturn *in\z
    ;       EndIf
    
    
    
    ;       ray_color_hit(*_r, *in , depth-1)
    
  Else
    
    ; Render to background
    ;    ray_color(*_r, *in)
    
    unit_vector\dir\x = *_r\dir\x
    unit_vector\dir\y = *_r\dir\y
    unit_vector\dir\z = *_r\dir\z
    
    
    ProcedureReturn    ray_color(unit_vector, *in, axe)
    
  EndIf
  
EndProcedure



Define aspect_ratio.d = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render(sp)
  
  Shared image_width
  Shared image_height
  
  Static focal_length.d = 1.0
  Static viewport_height.d = 2.0
  
  Protected i_w.d = image_width
  Protected i_h.d = image_height
  Protected viewport_width.d = viewport_height * (i_w/i_h)
  
  Protected point3.Vec3
  
  point3\x = 0.0
  point3\y = 0.0
  point3\z = 0.0
  
  Protected ray_direction.Vec3
  
  ray_direction\x = 0
  ray_direction\y = 0
  ray_direction\z = 0
  
  Protected viewport_u.Vec3
  
  viewport_u\x = viewport_width
  viewport_u\y = 0
  viewport_u\z = 0
  
  Protected viewport_v.Vec3
  
  viewport_v\x = 0
  viewport_v\y = -viewport_height
  viewport_v\z = 0
  
  Protected pixel_delta_u.Vec3
  
  pixel_delta_u\x = viewport_u\x / i_w
  pixel_delta_u\y = viewport_u\y / i_w
  pixel_delta_u\z = viewport_u\z / i_w
  
  Protected pixel_delta_v.Vec3
  
  pixel_delta_v\x = viewport_v\x / i_h
  pixel_delta_v\y = viewport_v\y / i_h
  pixel_delta_v\z = viewport_v\z / i_h
  
  Protected viewport_upper_left.Vec3
  
  Protected v_f.Vec3
  
  v_f\x = 0.0
  v_f\y = 0.0
  v_f\z = focal_length
  
  viewport_upper_left\x = point3\x - v_f\x - viewport_u\x / 2 - viewport_v\x / 2
  viewport_upper_left\y = point3\y - v_f\y - viewport_u\y / 2 - viewport_v\y / 2
  viewport_upper_left\z = point3\z - v_f\z - viewport_u\z / 2 - viewport_v\z / 2
  
  Protected pixel_100_loc.Vec3
  
  pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
  pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
  pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)
  
  Protected sample_per_pixel.i = 10
  Protected pixel_sample_scale.d = 1.0 / sample_per_pixel
  
  Protected max_depth.i = 5
  
  Protected x.d, y.d, z.d, v.vec3
  
  Static offset.Vec3, pixel_sample.Vec3
  
  If StartDrawing(SpriteOutput(sp))
    
    image_height-1
    image_width-1
    
    For j = 0 To image_height
      For i = 0 To image_width
        
        ii.d = i
        jj.d = j
        
        x = 0.0
        y = 0.0
        z = 0.0
        
        
        For sample = 0 To sample_per_pixel
          
          offset\x = random_double() -0.5 
          offset\y = random_double() -0.5 
          offset\z = 0.0
          
          
          pixel_sample\x = pixel_100_loc\x + ( (ii+offset\x) * pixel_delta_u\x) + ( (jj+offset\y) * pixel_delta_v\x)
          pixel_sample\y = pixel_100_loc\y + ( (ii+offset\x) * pixel_delta_u\y) + ( (jj+offset\y) * pixel_delta_v\y)
          pixel_sample\z = pixel_100_loc\z + ( (ii+offset\x) * pixel_delta_u\z) + ( (jj+offset\y) * pixel_delta_v\z)
          
          ray_direction\x = pixel_sample\x - point3\x
          ray_direction\y = pixel_sample\y - point3\y
          ray_direction\z = pixel_sample\z - point3\z
          
          Define.Ray r
          
          r\orig = point3
          r\dir = ray_direction          
          
          ;       ray_color_hit(r, @v, max_depth) 
          
          x = x + ray_color_hit(r, @v, max_depth, 1)
          y = y + ray_color_hit(r, @v, max_depth, 2)
          z = z + ray_color_hit(r, @v, max_depth, 3)
          
        Next
        
        Protected red.a,green.a,blue.a
        
        red.a = Int(255.999 * clamp(pixel_sample_scale * x, 0.0, 0.999))
        green.a = Int(255.999 * clamp(pixel_sample_scale*  y, 0.0, 0.999))
        blue = Int(255.999 * clamp(pixel_sample_scale * z, 0.0, 0.999))
                       
        Plot(i, j, RGB(red,green,blue))
            
        
      Next
      
    Next
    
  EndIf
  
  StopDrawing() 
  
EndProcedure

InitSprite()

OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(0), 0, 0, image_width , image_height )
Define sp = CreateSprite(#PB_Any, image_width,image_height) 

Define time = ElapsedMilliseconds()
render(sp)
time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

Repeat
  Repeat
    Event = WindowEvent()
    Select Event 
      Case #PB_Event_CloseWindow
        End 
    EndSelect
  Until Event = 0
  
  FlipBuffers() 
  ClearScreen(RGB(0, 0, 0))
  DisplaySprite(sp, 0,0)
  Delay(1)
ForEver



threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

pjay wrote: Fri Jun 14, 2024 10:32 am It's slow due to you calculating everything far more times than necessary due to the axial seperation ('axe') method you're using.
Ok thank you for your suggestion, I do the correction but a bit slow somewhat :? However we see the shadow now ! I take a point at parameter into my ray to make ray colors to collide with object ... But there is some artifact to render too :evil:

Here my correction from my code :

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider


;; Rewritten by threedslider Jun 10 2024
;; Added to color for sphere and background Jun 11 2024
;; Added to Pathtracing system (WIP) Jun 11 2024
;; Corrected typo and fixed some of my code Jun 12 2024
;; Fixed to Ray for random Jun 14 2024

; [Note] Jun 14 2024
;; Take a point at parameter on Ray, nice but very slow and it looks like as artifact :/ but some shadow here too... :) 
;; Code inspired by pjay for fast rendering in OpenGL
;; Corrected from suggestion by pjay

; Vector
Structure Vec3
  x.f
  y.f
  z.f
EndStructure


; Vector for 3D Macros
Macro rt_zero(v)
  v\x = 0.0
  v\y = 0.0
  v\z = 0.0
EndMacro

Macro rt_near_zero(v)
  near_zero(v)
EndMacro

Macro rt_clamp(v, m1, m2)
  v = clamp(v, m1, m2)
EndMacro

Macro rt_surround(x, m1, m2)
  surrounds(x, m1, m2)
EndMacro

Macro rt_norm(v,n)
  n = Sqr(v\x * v\x + v\y * v\y + v\z * v\z)
  
  If n <> 0.0
    n = 1.0/n
    v\x * n
    v\y * n
    v\z * n
  EndIf
  
EndMacro

Macro rt_r_length(v)
  Sqr(v\dir\x * v\dir\x + v\dir\y * v\dir\y + v\dir\z * v\dir\z)
EndMacro

Macro rt_v_length_squared(v)
  (v\x * v\x + v\y * v\y + v\z * v\z)
EndMacro


  
Macro rt_dot(v1, v2)
  (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z)
EndMacro

Macro rt_copy(v, v1)
  v\x = v1\x
  v\y = v1\y
  v\z = v1\z
EndMacro

Macro rt_add(v, v1, v2)
  v\x = v1\x + v2\x
  v\y = v1\y + v2\y
  v\z = v1\z + v2\z
EndMacro

Macro rt_sub(v, v1, v2)
  v\x = v1\x - v2\x
  v\y = v1\y - v2\y
  v\z = v1\z - v2\z
EndMacro

Macro rt_mul(v, v1, v2)
  v\x = v1\x * v2\x
  v\y = v1\y * v2\y
  v\z = v1\z * v2\z
EndMacro

Macro rt_scale(v, v1, s)
  v\x = v1\x * s
  v\y = v1\y * s
  v\z = v1\z * s
EndMacro  

Macro rt_neg(v, v1)
  v\x = -v1\x
  v\y = -v1\y
  v\z = -v1\z
EndMacro

; Ray
Structure Ray
  orig.Vec3
  dir.Vec3
EndStructure

; Sphere
Structure Sphere
  center.Vec3
  radius.f
  color.Vec3
EndStructure

; Material
Structure Material
  albedo.Vec3
EndStructure


; Clamp
Procedure.f clamp(x.f, min.f, max.f)
  If(x < min)
    ProcedureReturn min
  EndIf
  If(x > max)
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

; Random
Macro random_float()   
   Random(#MAXDWORD) / #MAXDWORD  
EndMacro

Macro random_min_max_float(min, max)     
   (min + (max-min) * random_float())
EndMacro

; Procedure.f random_vec(*vx.Vec3)
;   *vx\x = random_float()
;   *vx\y = random_float()
;   *vx\z = random_float()
;   
; EndProcedure

Macro random_min_max_vec(min, max)     
  random_min_max_float(min, max)  
EndMacro

Procedure.f unit_vector_vec(*v.Vec3, axe)

  Protected n.f = 0.0
  Protected p.Vec3
  
  rt_norm(*v, n)
   
  If axe = 1
    ProcedureReturn *v\x
  EndIf
  
  If axe = 2
    ProcedureReturn *v\y
  EndIf
  
  If axe = 3
    ProcedureReturn *v\z
  EndIf
  
EndProcedure  

Procedure.f random_in_unit_sphere(axe)
  Protected p.Vec3
  
   
  While #True
    
    p\x = random_min_max_vec(-1.0, 1.0)
    p\y = random_min_max_vec(-1.0, 1.0)
    p\z = random_min_max_vec(-1.0, 1.0)

    
    r.f = rt_v_length_squared(p)
    If r < 1.0
       If axe = 1
             
          ProcedureReturn p\x
     
        EndIf
          
   
          
       If axe = 2
    
          ProcedureReturn p\y
     
        EndIf
      
      
      
       If axe = 3
      
         ProcedureReturn p\z    
         
       EndIf
       
     EndIf
     
            
  Wend
  
EndProcedure

Procedure.f random_unit_vector(axe)
  Protected p.Vec3
  
  p\x = random_in_unit_sphere(1)
  p\y = random_in_unit_sphere(2)
  p\z = random_in_unit_sphere(3)
  
  ProcedureReturn unit_vector_vec(p, axe)
  
EndProcedure

Procedure.f random_on_hemisphere(*in.Vec3, axe)
  Protected on_unit_sphere.Vec3
  
  on_unit_sphere\x = random_unit_vector(1)
  on_unit_sphere\y = random_unit_vector(2)
  on_unit_sphere\z = random_unit_vector(3)
  
  ;Debug on_unit_sphere\x
  
  If rt_dot(on_unit_sphere, *in) > 0.0
   
    ;rt_copy(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn on_unit_sphere\y
    EndIf
    
   If axe = 3
      ProcedureReturn on_unit_sphere\z
   EndIf
    
    
  Else
    
    ;rt_neg(*out, on_unit_sphere)
    If axe = 1
      ProcedureReturn -on_unit_sphere\x
    EndIf
    
    If axe = 2
      ProcedureReturn -on_unit_sphere\y
    EndIf
    
   If axe = 3
      ProcedureReturn -on_unit_sphere\z
   EndIf
    
  EndIf     
  
EndProcedure

; Surrounds
Procedure surrounds(x.f, min.f, max.f)
  ProcedureReturn Bool( min < x And x < max)
EndProcedure

; Procedure surrounds(x.f, min.f, max.f)
;   ProcedureReturn Bool( x <= min  Or  max <= x)
; EndProcedure

; Create Sphere primitve
Macro m_Sphere(id, center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  scene(id)\center\x = center_x
  scene(id)\center\y = center_y
  scene(id)\center\z = center_z
  scene(id)\radius = radius_
  scene(id)\color\x = color_x
  scene(id)\color\y = color_y
  scene(id)\color\z = color_z
EndMacro


Global Dim scene.Sphere(1)
m_Sphere(0, 0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
;m_Sphere(1, -1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
;m_Sphere(2, 1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
m_Sphere(1, 0.0, -100.0+0.005, -1.0, 100.0, 1.0, 1.0, 1.0)


;Near zero for pathtracing calculation
Procedure near_zero (*v.Vec3)
  Protected e.f = 1e-8
  ProcedureReturn Bool((Abs(*v\x) < e) And (Abs(*v\y) < e)And (Abs(*v\z) < e))
EndProcedure

; Init for vector
Procedure v_init(x.f, y.f, z.f, *v.Vec3)
  *v\x = x
  *v\y = y
  *v\z = z
EndProcedure


; Scattering randomly
Procedure.f scatter(*r.Ray, *rec.Vec3, *attenuation.Vec3)
  Protected scatter_direction.Vec3
  Protected ran_dir.Vec3
  

  ran_dir\x = random_unit_vector(1)
  ran_dir\y = random_unit_vector(2)
  ran_dir\z = random_unit_vector(3)
  
  rt_add(scatter_direction, *rec, ran_dir)
  
  If rt_near_zero(scatter_direction) 
    rt_copy(scatter_direction, *rec)
  EndIf
  
  
  *r\dir\x = unit_vector_vec(scatter_direction, 1)  
  *r\dir\y = unit_vector_vec(scatter_direction, 2)  
  *r\dir\z = unit_vector_vec(scatter_direction, 3)  
  
EndProcedure

Procedure.f diffuse_scatter(*v1.Vec3, *v2.Vec3)
EndProcedure

  

; Hit for 3D objects
Procedure.f hit( *_s.Sphere, *_r.Ray, mx.f)
  
  Protected oc.Vec3
  Protected root.f = 0.0
  Protected min.f = 0.0
  Protected max.f = mx
  
  rt_sub(oc, *_r\orig, *_s\center) 
   
  Protected a.f = rt_dot(*_r\dir, *_r\dir)
  Protected b.f = rt_dot(oc,*_r\dir)
  Protected c.f = rt_dot(oc,oc) - *_s\radius *  *_s\radius
  
  Protected discriminant.f = b*b - a*c
  
  
  If discriminant < 0.0
    ProcedureReturn 0.0
  EndIf
  
  root = (-b - Sqr(discriminant)) / (a)

    

    If (Not rt_surround(root, min, max))
      root = (-b + Sqr(discriminant)) / (2.0*a)
         
     If ( Not rt_surround(root, min, max))
       ProcedureReturn 0.0   
    EndIf
  
    
  EndIf 
  
        
    ProcedureReturn root
  
EndProcedure


; Hit for intersection
Procedure.b intersect( *_r.Ray, *t.Float, *id.Integer )
  Protected d.f, inf.f = 0.999
  Protected i.i = ArraySize(scene())
  *t\f = inf
  While i >= 0
    d = hit( scene(i), *_r, inf )
    If d
      *t\f  = d
      *id\i = i
    EndIf
    i - 1
  Wend
  ProcedureReturn Bool( *t\f < inf )
EndProcedure

; Unit vector Y for background
Procedure.f unit_vector_y(*vy.Ray)
  Protected len.f
  Static y.f : y.f = 0
  len = rt_r_length(*vy)
  If Not len = 0 
    y = *vy\dir\y / len 
   EndIf
  ProcedureReturn y
EndProcedure

Procedure.f ray_color(*rr.Ray, *in.Vec3)
  Protected a.f
  Static color1.Vec3, color2.Vec3, unit_direction.Vec3
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
  
  unit_direction\x = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\x + 1.0) 
  *in\x =  (1.0 - a) * color1\x + a * color2\x 
  
  unit_direction\y = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\y + 1.0)
  *in\y =  (1.0 - a) * color1\y + a * color2\y
  
  unit_direction\z = unit_vector_y(*rr)
  a = 0.5 * (unit_direction\z + 1.0)
  *in\z =  (1.0 - a) * color1\z + a * color2\z  
EndProcedure  

; Procedure.f ray_color(*rr.Ray, *in.Vec3, axe)
;   Protected a.f
;   Static color1.Vec3, color2.Vec3, unit_direction.Vec3
;   
;   color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
;   color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0
;   
;   unit_direction\x = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\x + 1.0) 
;   *in\x =  (1.0 - a) * color1\x + a * color2\x 
;   
;   unit_direction\y = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\y + 1.0)
;   *in\y =  (1.0 - a) * color1\y + a * color2\y
;   
;   unit_direction\z = unit_vector_y(*rr)
;   a = 0.5 * (unit_direction\z + 1.0)
;   *in\z =  (1.0 - a) * color1\z + a * color2\z  
;   
;   If axe = 1
;     ProcedureReturn *in\x
;   EndIf
;   If axe = 2
;     ProcedureReturn *in\y
;   EndIf
;   If axe = 3
;     ProcedureReturn *in\z
;   EndIf
;   
; EndProcedure  

; Procedure.f ray_color_hit(*_r.Ray, *in.Vec3, depth)
;   Protected x.Vec3
;   Protected p.Vec3
;   Protected ntemp.f
;   Protected t.f = 0.0
;   Protected id.i = 0
;   
;   If depth <= 0
;     v_init(0.0, 0.0, 0.0, p)
;   EndIf  
;       
;   If intersect(*_r, @t, @id)
;     Protected *obj.Sphere = scene(id)
;     
;     ; Point at parameter
;     x\x = *_r\orig\x + t * *_r\dir\x
;     x\y = *_r\orig\y + t * *_r\dir\y
;     x\z = *_r\orig\z + t * *_r\dir\z
;     
;     ; Norm
;     Protected n.Vec3
;     rt_sub( n, x, *obj\center)
;     rt_norm( n, ntemp )
;     
;     ; Set to normal
;     Protected nl.Vec3
;     If rt_dot( n, *_r\dir ) < 0.0
;       rt_copy( nl, n )
;     Else
;       rt_neg( nl, n )
;     EndIf
;     
;       *_r\dir\x = nl\x + random_unit_vector(1) 
;       *_r\dir\y = nl\y + random_unit_vector(2)  
;       *_r\dir\z = nl\z + random_unit_vector(3) 
;             
; ;       *in\x =  *in\x * ray_color_hit(*_r, *in, depth-1)
; ;       *in\y =  *in\y * ray_color_hit(*_r, *in, depth-1)
; ;       *in\z =  *in\z * ray_color_hit(*_r, *in, depth-1)
; ;     
;       
;       *in\x =  0.1
;       *in\y =  0.1
;       *in\z =  0.1
;       
;       rt_mul(*in, *in, *in)
;       ray_color_hit(*_r, *in , depth-1)
;     
;   Else
;     
;     ; Render to background
;    ray_color(*_r, *in)
;     
;   EndIf
;   
; EndProcedure

Procedure.f ray_color_hit(*_r.Ray, *in.Vec3, depth)
  Protected x.Vec3
  Protected p.Vec3
  Protected ntemp.f
  Protected t.f = 0.0
  Protected id.i = 0
  Protected rr.Ray
  Protected unit_vector.Ray
  
  
  If depth = 0
    ;If axe = 1
      *in\x = 0.0
      ;ProcedureReturn *in\x
    ;EndIf
    
   ; If axe = 2
      *in\y = 0.0
     ; ProcedureReturn *in\y
    ;EndIf
    
    ;If axe = 1
      *in\z = 0.0
     ; ProcedureReturn *in\z
    ;EndIf
    
  EndIf  
      
  If intersect(*_r, @t, @id)
    Protected *obj.Sphere = scene(id)
    
    ; Point at parameter
    x\x = *_r\orig\x + t * *_r\dir\x
    x\y = *_r\orig\y + t * *_r\dir\y
    x\z = *_r\orig\z + t * *_r\dir\z
    
    ; Norm
    Protected n.Vec3
    rt_sub( n, x, *obj\center)
    rt_norm( n, ntemp )
    
    ; Set to normal
    Protected nl.Vec3
    If rt_dot( n, *_r\dir ) < 0.0
      rt_copy( nl, n )
    Else
      rt_neg( nl, n )
    EndIf
    
        ;;** VERY SLOW ** ;;    
;         rr\orig\x = x\x
;         rr\orig\y = x\y
;         rr\orig\z = x\z
    
        rt_copy(rr\orig, x)
        
        ;; QUITE FAST ;;
;         rr\orig\x = *_r\orig\x
;         rr\orig\y = *_r\orig\y
;         rr\orig\z = *_r\orig\z
               
        rr\dir\x = nl\x + random_unit_vector(1) 
        rr\dir\y = nl\y + random_unit_vector(2)  
        rr\dir\z = nl\z + random_unit_vector(3)    
       
        p\x = 0
        p\y = 0
        p\z = 0
      
      ;If axe = 1
         
        ;*in\x = ((ray_color_hit(rr, *in , depth-1) + 0.1)) 
        
        ;rt_mul(*in, *in, *in)
        ;Debug *in\x
       ; ProcedureReturn *in\x 
     ; EndIf
      ;If axe = 2
        
       ; *in\y = ((ray_color_hit(rr, *in , depth-1) + 0.1)) 
        ;rt_mul(*in, *in, *in)
        ;ProcedureReturn *in\y 
     ; EndIf
     ; If axe = 3
        
       ; *in\z = ( (ray_color_hit(rr, *in , depth-1) + 0.1))
        ;rt_mul(*in, *in, *in)
       
       ; ProcedureReturn *in\z
       ;EndIf
        
        *in\x * 0.5 * (ray_color_hit(rr, *in , depth-1) + 0.1) : *in\y * 0.5 : *in\z * 0.5
        
         ;ray_color_hit(rr, *in , depth-1) + 0.1
        
;       If axe = 1
;          
;         *in\x = 0.5 * (nl\x + 1.0)
;         ;rt_add(*in, p, *in)
;         ;Debug *in\x
;         ProcedureReturn *in\x 
;       EndIf
;       If axe = 2
;         
;         *in\y = 0.5 * (nl\y + 1.0)
;         ;rt_add(*in, p, *in)
;         ProcedureReturn *in\y 
;       EndIf
;       If axe = 3
;         
;         *in\z = 0.5 * (nl\z + 1.0)
;         ;rt_add(*in, p, *in)
;        
;         ProcedureReturn *in\z
;       EndIf
        
      
     
;       ray_color_hit(*_r, *in , depth-1)
    
  Else
    
    ; Render to background
    ;    ray_color(*_r, *in)
    
    unit_vector\dir\x = *_r\dir\x
    unit_vector\dir\y = *_r\dir\y
    unit_vector\dir\z = *_r\dir\z
    
    
    ray_color(unit_vector, *in)
    
  EndIf
  
EndProcedure



Define aspect_ratio.f = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
  
Shared image_width
Shared image_height

Static focal_length.f = 1.0
Static viewport_height.f = 2.0

Protected i_w.f = image_width
Protected i_h.f = image_height
Protected viewport_width.f = viewport_height * (i_w/i_h)

Protected point3.Vec3

point3\x = 0.0
point3\y = 0.0
point3\z = 0.0

Protected ray_direction.Vec3

ray_direction\x = 0
ray_direction\y = 0
ray_direction\z = 0

Protected viewport_u.Vec3

viewport_u\x = viewport_width
viewport_u\y = 0
viewport_u\z = 0

Protected viewport_v.Vec3

viewport_v\x = 0
viewport_v\y = -viewport_height
viewport_v\z = 0

Protected pixel_delta_u.Vec3

pixel_delta_u\x = viewport_u\x / i_w
pixel_delta_u\y = viewport_u\y / i_w
pixel_delta_u\z = viewport_u\z / i_w

Protected pixel_delta_v.Vec3

pixel_delta_v\x = viewport_v\x / i_h
pixel_delta_v\y = viewport_v\y / i_h
pixel_delta_v\z = viewport_v\z / i_h

Protected viewport_upper_left.Vec3

Protected v_f.Vec3

v_f\x = 0.0
v_f\y = 0.0
v_f\z = focal_length

viewport_upper_left\x = point3\x - v_f\x - viewport_u\x / 2 - viewport_v\x / 2
viewport_upper_left\y = point3\y - v_f\y - viewport_u\y / 2 - viewport_v\y / 2
viewport_upper_left\z = point3\z - v_f\z - viewport_u\z / 2 - viewport_v\z / 2

Protected pixel_100_loc.Vec3

pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)

Protected sample_per_pixel.i = 10
Protected pixel_sample_scale.f = 1.0 / sample_per_pixel

Protected max_depth.i = 5

Protected x.f, y.f, z.f, v.vec3

Static offset.Vec3, pixel_sample.Vec3

;If StartDrawing(ScreenOutput())

  For j = 0 To image_height-1
    glBegin_(#GL_POINTS)
      For i = 0 To image_width-1
      
      ii.f = i
      jj.f = j
      
      x = 0.0
      y = 0.0
      z = 0.0
   
      
      For sample = 0 To sample_per_pixel
        
      offset\x = random_float() -0.5 
      offset\y = random_float() -0.5 
      offset\z = 0.0
      
      
      pixel_sample\x = pixel_100_loc\x + ( (ii+offset\x) * pixel_delta_u\x) + ( (jj+offset\y) * pixel_delta_v\x)
      pixel_sample\y = pixel_100_loc\y + ( (ii+offset\x) * pixel_delta_u\y) + ( (jj+offset\y) * pixel_delta_v\y)
      pixel_sample\z = pixel_100_loc\z + ( (ii+offset\x) * pixel_delta_u\z) + ( (jj+offset\y) * pixel_delta_v\z)
      
      ray_direction\x = pixel_sample\x - point3\x
      ray_direction\y = pixel_sample\y - point3\y
      ray_direction\z = pixel_sample\z - point3\z
           
      Define.Ray r
      
      r\orig = point3
      r\dir = ray_direction          
    
      ray_color_hit(r, @v, max_depth) 
      
      x = x + v\x
      y = y + v\y
      z = z + v\z
      
      Next
      
        ;Plot(i, j, RGB(Int(255.999 * clamp(pixel_sample_scale * x, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale*  y, 0.0, 0.999)),Int(255.999 * clamp(pixel_sample_scale * z, 0.0, 0.999))))
     
        glColor3f_(  clamp(pixel_sample_scale * x, 0.0, 0.999), clamp(pixel_sample_scale*  y, 0.0, 0.999), clamp(pixel_sample_scale * z, 0.0, 0.999)) : glVertex2s_(i,j)
        
        
      Next
    glEnd_() : glFlush_()
    Next
    
;EndIf
  
;StopDrawing() 

EndProcedure




InitSprite()
 
 OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
 ;OpenWindowedScreen(WindowID(0), 0, 0, image_width , image_height )
 OpenGLGadget(0,0,0,image_width / DesktopResolutionX(),image_height / DesktopResolutionY(),#PB_OpenGL_NoFlipSynchronization)
 glOrtho_(0,image_width,image_height,0,-1,1)
 glDrawBuffer_(#GL_FRONT)  
  ;ClearScreen(RGB(0,0,0))
  
Define time = ElapsedMilliseconds()
render()
time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

 Repeat
   Event = WindowEvent()
   
 Until  Event = #PB_Event_CloseWindow
   
 End 
pjay wrote: Fri Jun 14, 2024 10:32 am

Code: Select all

ProcedureReturn 0.5 * ray_color_hit(*_r, *in, depth-1)
This recursive function is needed to make complex as diffuse and shadow to take account for rendering ... But there is somewhere broken to this code ... As you see my recently code I do only for x axis once ...
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

@idle : Great ! But it is somewhat slow too as well :shock:

Thank you for your code !
pjay
Enthusiast
Enthusiast
Posts: 252
Joined: Thu Mar 30, 2006 11:14 am

Re: Raytracing in One Weekend

Post by pjay »

I refactored the code and made a handful of adjustments which has made it look more like it should, but i'm not positive it's correct.

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider

EnableExplicit

#Samples_per_pixel = 10
#Max_Depth = 10
#Sample_scale = 1.0 / #Samples_per_pixel

; Vector
Structure Vec3d : x.d : y.d : z.d : EndStructure
Procedure vec3_normalize(*v.vec3d)
  Protected n.d = Sqr(*v\x * *v\x + *v\y * *v\y + *v\z * *v\z)
  If n <> 0.0 : n = 1.0/n : *v\x * n : *v\y * n : *v\z * n : EndIf
EndProcedure

Macro vec3_zero(v) : v\x = 0.0 : v\y = 0.0 : v\z = 0.0 : EndMacro
Macro vec3_length(v) : Sqr(v\direction\x * v\direction\x + v\direction\y * v\direction\y + v\direction\z * v\direction\z) : EndMacro
Macro vec3_length_squared(v) : (v\x * v\x + v\y * v\y + v\z * v\z) : EndMacro
Macro vec3_dot(v1, v2) : (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z) : EndMacro
Macro vec3_copy(v, v1) : v\x = v1\x : v\y = v1\y : v\z = v1\z : EndMacro
Macro vec3_add(v, v1, v2) : v\x = v1\x + v2\x : v\y = v1\y + v2\y : v\z = v1\z + v2\z : EndMacro
Macro vec3_subtract(v, v1, v2) : v\x = v1\x - v2\x : v\y = v1\y - v2\y : v\z = v1\z - v2\z : EndMacro
Macro vec3_mul(v, v1, v2) : v\x = v1\x * v2\x : v\y = v1\y * v2\y : v\z = v1\z * v2\z : EndMacro
Macro vec3_scale(v, v1, s) : v\x = v1\x * s : v\y = v1\y * s : v\z = v1\z * s : EndMacro  
Macro vec3_set(v, vx, vy, vz) : v\x = vx : v\y = vy : v\z = vz : EndMacro
Macro vec3_negate(v, v1) : v\x = -v1\x : v\y = -v1\y : v\z = -v1\z : EndMacro
Macro random_double() : Random(#MAXDWORD) / #MAXDWORD : EndMacro
Macro random_min_max(min, max) : min + (max-min) * random_double() : EndMacro

Structure Ray ; Ray
  origin.vec3d : direction.vec3d
EndStructure
Structure Sphere ; Sphere
  center.vec3d
  color.vec3d
  radius.d
EndStructure
Structure Material ; Material
  albedo.vec3d
EndStructure

Procedure.d clamp(x.d, min.d, max.d)
  If x < min
    ProcedureReturn min
  ElseIf x > max
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure
Procedure unit_vector_vec3(*v.vec3d)
  vec3_normalize(*v)
EndProcedure  
Procedure random_in_unit_sphere(*v.vec3d)
  Repeat
    *v\x = random_min_max(-1.0, 1.0)
    *v\y = random_min_max(-1.0, 1.0)
    *v\z = random_min_max(-1.0, 1.0)
    If vec3_length_squared(*v) < 1.0 : ProcedureReturn : EndIf
  ForEver
EndProcedure
Procedure random_unit_vector(*v.vec3d)
  random_in_unit_sphere(*v)
  unit_vector_vec3(*v)
EndProcedure

Macro surrounds(x, min, max) : Bool(min <= x And x <= max) : EndMacro

; Create Sphere primitve
Macro m_Sphere(center_x, center_y, center_z, radius_, color_x, color_y, color_z)
  AddElement(scene())
  vec3_set(scene()\center,center_x,center_y,center_z)
  vec3_set(scene()\color,color_x,color_y,color_z)
  scene()\radius = radius_
EndMacro

Global NewList scene.Sphere()

m_Sphere(0.0, -100.5, -1.0, 100.0, 1.0, 1.0, 1.0); ; << - PJ240614 - pushed this down
m_Sphere(0.0, 0.0, -1.0, 0.5, 1.0, 1.0, 1.0)
; m_Sphere(-1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)
; m_Sphere(1.0, 0.0, -1.0, 0.35, 1.0, 1.0, 1.0)

Procedure near_zero (*v.vec3d) ;Near zero for pathtracing calculation
  Protected e.d = 1e-8
  ProcedureReturn Bool((Abs(*v\x) < e) And (Abs(*v\y) < e) And (Abs(*v\z) < e))
EndProcedure

; Hit for 3D objects
Procedure.d hit(*_s.Sphere, *ray.Ray, max.d)
  
  Protected oc.vec3d
  Protected root.d = 0.0
  Protected min.d = 0.0001
  
  vec3_subtract(oc, *_s\center,*ray\origin) 
  
  Protected a.d = vec3_length_squared(*ray\direction)
  Protected h.d = vec3_dot(*ray\direction,oc)
  Protected c.d = vec3_length_squared(oc) - *_s\radius * *_s\radius
  
  Protected discriminant.d = h * h - a * c
  
  If discriminant < 0.0 : ProcedureReturn #False : EndIf
  
  Protected sqrtd.d = Sqr(discriminant)
  
  root = (h - sqrtd) / a
  
  If Not surrounds(root, min, max)
    root = (h + sqrtd) / a
    If Not surrounds(root, min, max) : ProcedureReturn 0.0 : EndIf
  EndIf 

  ProcedureReturn root
  
EndProcedure

; Hit for intersection
Procedure.b intersect( *ray.Ray, *t.double, *id.Integer )
  Protected d.d, inf.d = 999.999
  *t\d = inf
  ForEach scene()
    d = hit(@scene(), *ray, *t\d)
    If d
      *t\d  = d
      *id\i = ListIndex(scene())
    EndIf
  Next
  ProcedureReturn Bool( *t\d < inf )
EndProcedure

; Unit vector Y for background
Procedure.d unit_vector_y(*vy.Ray)
  Protected len.d
  Static y.d : y.d = 0
  len = vec3_length(*vy)
  If len <> 0.0 : y = *vy\direction\y / len  : EndIf
  ProcedureReturn y
EndProcedure

Procedure.d ray_color(*rr.Ray, *in.vec3d)
  Protected a.d
  Static color1.vec3d, color2.vec3d, unit_directionection.vec3d
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0

  unit_directionection\y = unit_vector_y(*rr)
  a = 0.5 * (unit_directionection\y + 1.0) 
  *in\x + ((1.0 - a) * color1\x + a * color2\x)
  *in\y + ((1.0 - a) * color1\y + a * color2\y)
  *in\z + ((1.0 - a) * color1\z + a * color2\z)
EndProcedure  

Procedure.d ray_color_hit(*ray.Ray, *in.vec3d, depth)
  Protected.vec3d x, p
  Protected t.d = 0.0
  Protected id.i = 0
  Protected.ray rr, unit_vector
  
  If depth < 0
    vec3_set(*in,0.0,0.0,0.0) : ProcedureReturn
  EndIf
  Protected.vec3d ruv, n, nl
  If intersect(*ray, @t, @id)
    SelectElement(scene(),id)
    Protected *obj.Sphere = scene()
    
    ; Point at parameter
    x\x = *ray\origin\x + t * *ray\direction\x
    x\y = *ray\origin\y + t * *ray\direction\y
    x\z = *ray\origin\z + t * *ray\direction\z
    
    ; Norm
    vec3_subtract(n, x, *obj\center)
    vec3_normalize(n)
    
    ; Set to normal
    If vec3_dot(n, *ray\direction ) < 0.0
      vec3_copy(nl, n)
    Else
      vec3_negate(nl, n)
    EndIf

    vec3_copy(rr\origin, x)

    random_unit_vector(@ruv) 
    vec3_add(rr\direction,nl,ruv) 

    ray_color_hit(rr, *in , depth-1)
    *in\x * 0.5 : *in\y * 0.5 : *in\z * 0.5
  Else
    
    ray_color(*ray, *in)
    *in\x * 0.5 : *in\y * 0.5 : *in\z * 0.5
  EndIf
  
EndProcedure

Define aspect_ratio.d = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
  Shared image_width, image_height
  Static focal_length.d = 1.0, viewport_height.d = 2.0
  Protected i_w.d = image_width, i_h.d = image_height
  Protected viewport_width.d = viewport_height * (i_w/i_h)
  
  Protected.vec3d point3, ray_direction, viewport3_u, viewport3_v, pixel_delta_u, pixel_delta_v, v_f, viewport_upper_left, pixel_100_loc
  
  vec3_set(point3, 0,0,0)
  vec3_set(ray_direction, 0,0,0)
  vec3_set(viewport3_u, viewport_width,0,0)
  vec3_set(viewport3_v, 0,-viewport_height,0)
  vec3_set(pixel_delta_u, viewport3_u\x / i_w, viewport3_u\y / i_w, viewport3_u\z / i_w)
  vec3_set(pixel_delta_v, viewport3_v\x / i_h, viewport3_v\y / i_h, viewport3_v\z / i_h)
  vec3_set(v_f, 0,0,focal_length)
  vec3_set(viewport_upper_left,
           point3\x - v_f\x - viewport3_u\x / 2 - viewport3_v\x / 2,
           point3\y - v_f\y - viewport3_u\y / 2 - viewport3_v\y / 2,
           point3\z - v_f\z - viewport3_u\z / 2 - viewport3_v\z / 2)
  vec3_set(pixel_100_loc, 0,0,focal_length)

  pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
  pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
  pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)
  
  Protected x.d, y.d, z.d, v.vec3d
  Protected ray.ray
  Static offset.vec3d, pixel_sample.vec3d
  Protected j,i,jj.d, ii.d, sample
  
  For j = 0 To image_height-1
    glBegin_(#GL_POINTS)
    For i = 0 To image_width-1
      x = 0.0 : y = 0.0 : z = 0.0
      For sample = 1 To #Samples_per_pixel
        ii = i + random_double() - 0.5 
        jj = j + random_double() - 0.5 
        
        pixel_sample\x = pixel_100_loc\x + ii * pixel_delta_u\x +  jj * pixel_delta_v\x
        pixel_sample\y = pixel_100_loc\y + ii * pixel_delta_u\y +  jj * pixel_delta_v\y
        pixel_sample\z = pixel_100_loc\z + ii * pixel_delta_u\z +  jj * pixel_delta_v\z
        
        vec3_subtract(ray_direction,pixel_sample,point3)

        ray\origin = point3
        ray\direction = ray_direction          
        
        ray_color_hit(@ray, @v, #Max_Depth) 
        x = x + v\x : y = y + v\y : z = z + v\z
      Next
      
      glColor3f_(#Sample_scale * x, #Sample_scale * y, #Sample_scale * z) : glVertex2s_(i,j)

    Next
    glEnd_() : glFlush_()
  Next
  
EndProcedure

OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
OpenGLGadget(0,0,0,image_width / DesktopResolutionX(),image_height / DesktopResolutionY(),#PB_OpenGL_NoFlipSynchronization)
glOrtho_(0,image_width,image_height,0,-1,1)
glDrawBuffer_(#GL_FRONT)  

Define time = ElapsedMilliseconds()
render()
  time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

Repeat
  Event = WindowEvent()
Until  Event = #PB_Event_CloseWindow

End 
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

@pjay : So great !! Thanks a lot for this smart code :mrgreen:

Ok I can now to make it more safe for the next chapter :D

Happy coding !
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Come back to Raytracing :)

Here I make a reflection from two sphere and their color too, yay ! The code is here as always :

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider

EnableExplicit

Enumeration Shading
  #None
  #Lambertian
  #Metal
EndEnumeration


#Samples_per_pixel = 35
#Max_Depth = 2
#Sample_scale = 1.0 / #Samples_per_pixel

; Vector
Structure Vec3d : x.d : y.d : z.d : EndStructure

Procedure vec3_normalize(*v.vec3d)
  Protected n.d = Sqr(*v\x * *v\x + *v\y * *v\y + *v\z * *v\z)
  If n <> 0.0 : n = 1.0/n : *v\x * n : *v\y * n : *v\z * n : EndIf
EndProcedure

Macro vec3_zero(v) : v\x = 0.0 : v\y = 0.0 : v\z = 0.0 : EndMacro
Macro vec3_length(v) : Sqr(v\direction\x * v\direction\x + v\direction\y * v\direction\y + v\direction\z * v\direction\z) : EndMacro
Macro vec3_length_squared(v) : (v\x * v\x + v\y * v\y + v\z * v\z) : EndMacro
Macro vec3_dot(v1, v2) : (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z) : EndMacro
Macro vec3_copy(v, v1) : v\x = v1\x : v\y = v1\y : v\z = v1\z : EndMacro
Macro vec3_add(v, v1, v2) : v\x = v1\x + v2\x : v\y = v1\y + v2\y : v\z = v1\z + v2\z : EndMacro
Macro vec3_subtract(v, v1, v2) : v\x = v1\x - v2\x : v\y = v1\y - v2\y : v\z = v1\z - v2\z : EndMacro
Macro vec3_mul(v, v1, v2) : v\x = v1\x * v2\x : v\y = v1\y * v2\y : v\z = v1\z * v2\z : EndMacro
Macro vec3_scale(v, v1, s) : v\x = v1\x * s : v\y = v1\y * s : v\z = v1\z * s : EndMacro  
Macro vec3_set(v, vx, vy, vz) : v\x = vx : v\y = vy : v\z = vz : EndMacro
Macro vec3_negate(v, v1) : v\x = -v1\x : v\y = -v1\y : v\z = -v1\z : EndMacro
Macro random_double() : Random(#MAXDWORD) / #MAXDWORD : EndMacro
Macro random_min_max(min, max) : min + (max-min) * random_double() : EndMacro
Macro reflect(v, v1, n1) : v\x  = v\x - 2 * vec3_dot(v1, n1) * n1\x : v\y  = v\y - 2 * vec3_dot(v1, n1) * n1\y : v\z  = v\z - 2 * vec3_dot(v1, n1) * n1\z : EndMacro

Structure Ray ; Ray
  origin.vec3d : direction.vec3d
EndStructure

Structure Sphere ; Sphere
  center.vec3d
  color.vec3d
  radius.d
  Shading.i
EndStructure

Structure Material ; Material
  albedo.vec3d
EndStructure

Procedure.d clamp(x.d, min.d, max.d)
  If x < min
    ProcedureReturn min
  ElseIf x > max
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

Procedure unit_vector_vec3(*v.vec3d)
  vec3_normalize(*v)
EndProcedure  

Procedure random_in_unit_sphere(*v.vec3d)
  Repeat
    *v\x = random_min_max(-1.0, 1.0)
    *v\y = random_min_max(-1.0, 1.0)
    *v\z = random_min_max(-1.0, 1.0)
    If vec3_length_squared(*v) < 1.0 : ProcedureReturn : EndIf
  ForEver
EndProcedure
Procedure random_unit_vector(*v.vec3d)
  random_in_unit_sphere(*v)
  unit_vector_vec3(*v)
EndProcedure

Macro surrounds(x, min, max) : Bool(min <= x And x <= max) : EndMacro

; Create Sphere primitve
Macro m_Sphere(center_x, center_y, center_z, radius_, color_x, color_y, color_z, shading_)
  AddElement(scene())
  vec3_set(scene()\center,center_x,center_y,center_z)
  vec3_set(scene()\color,color_x,color_y,color_z)
  scene()\radius = radius_
  scene()\Shading = shading_
EndMacro

Global NewList scene.Sphere()

m_Sphere(0.0, -100.5, -1.0, 100.0, 0.8, 0.8, 0.0, #Lambertian); ; << - PJ240614 - pushed this down
m_Sphere(0.0, 0.0, -1.2, 0.5, 0.1, 0.2, 0.5, #Lambertian)
m_Sphere(-1.0, 0.0, -1.0, 0.5, 0.8, 0.8, 0.8, #Metal)
m_Sphere(1.0, 0.0, -1.0, 0.5, 0.8, 0.6, 0.2, #Metal)

Procedure near_zero (*v.vec3d) ;Near zero for pathtracing calculation
  Protected e.d = 1e-8
  ProcedureReturn Bool((Abs(*v\x) < e) And (Abs(*v\y) < e) And (Abs(*v\z) < e))
EndProcedure


; Hit for 3D objects
Procedure.d hit(*_s.Sphere, *ray.Ray, max.d)
  
  Protected oc.vec3d
  Protected root.d = 0.0
  Protected min.d = 0.0001
  
  vec3_subtract(oc, *_s\center,*ray\origin) 
  
  Protected a.d = vec3_length_squared(*ray\direction)
  Protected h.d = vec3_dot(*ray\direction,oc)
  Protected c.d = vec3_length_squared(oc) - *_s\radius * *_s\radius
  
  Protected discriminant.d = h * h - a * c
  
  If discriminant < 0.0 : ProcedureReturn #False : EndIf
  
  Protected sqrtd.d = Sqr(discriminant)
  
  root = (h - sqrtd) / a
  
  If Not surrounds(root, min, max)
    root = (h + sqrtd) / a
    If Not surrounds(root, min, max) : ProcedureReturn 0.0 : EndIf
  EndIf 

  ProcedureReturn root
  
EndProcedure

; Hit for intersection
Procedure.b intersect( *ray.Ray, *t.double, *id.Integer )
  Protected d.d, inf.d = 999.999
  *t\d = inf
  ForEach scene()
    d = hit(@scene(), *ray, *t\d)
    If d
      *t\d  = d
      *id\i = ListIndex(scene())
    EndIf
  Next
  ProcedureReturn Bool( *t\d < inf )
EndProcedure

; Unit vector Y for background
Procedure.d unit_vector_y(*vy.Ray)
  Protected len.d
  Static y.d : y.d = 0
  len = vec3_length(*vy)
  If len <> 0.0 : y = *vy\direction\y / len  : EndIf
  ProcedureReturn y
EndProcedure

Procedure.d ray_color(*rr.Ray, *in.vec3d)
  Protected a.d
  Static color1.vec3d, color2.vec3d, unit_directionection.vec3d
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0

  unit_directionection\y = unit_vector_y(*rr)
  a = 0.5 * (unit_directionection\y + 1.0) 
  *in\x + ((1.0 - a) * color1\x + a * color2\x)
  *in\y + ((1.0 - a) * color1\y + a * color2\y)
  *in\z + ((1.0 - a) * color1\z + a * color2\z)
EndProcedure  

Procedure.d linear_to_gamma(num.d)
  If num > 0.0
    ProcedureReturn Sqr(num)
  EndIf
  
  ProcedureReturn 0.0
  
EndProcedure


Procedure.d ray_color_hit(*ray.Ray, *in.vec3d, depth)
  Protected.vec3d x, p
  Protected t.d = 0.0
  Protected id.i = 0
  Protected.ray rr, unit_vector, scattered
  Protected.Vec3d out, reflected
  
  If depth < 0
    vec3_set(*in,0.0,0.0,0.0) : ProcedureReturn
  EndIf
  Protected.vec3d ruv, n, nl
  If intersect(*ray, @t, @id)
    SelectElement(scene(),id)
    Protected *obj.Sphere = scene()
    
    ; Point at parameter
    x\x = *ray\origin\x + t * *ray\direction\x
    x\y = *ray\origin\y + t * *ray\direction\y
    x\z = *ray\origin\z + t * *ray\direction\z
    
    ; Norm
    vec3_subtract(n, x, *obj\center)
    vec3_normalize(n)
    
    ; Set to normal
    If vec3_dot(n, *ray\direction ) < 0.0
      vec3_copy(nl, n)
    Else
      vec3_negate(nl, n)
    EndIf
    
    
    If *obj\shading = #Lambertian
      vec3_copy(rr\origin, x)
  
      random_unit_vector(@ruv) 
      vec3_add(rr\direction,nl,ruv) 
      
        
      ray_color_hit(rr, *in , depth-1)
      *in\x * *obj\color\x : *in\y * *obj\color\y : *in\z * *obj\color\z 
    
    ElseIf *obj\shading = #Metal
      
      reflect(reflected, *ray\direction, nl)
    
      vec3_copy(rr\origin, x)
      
      vec3_copy(rr\direction, reflected)
      
      ray_color_hit(rr, *in , depth-1)
      *in\x * *obj\color\x : *in\y * *obj\color\y : *in\z * *obj\color\z 
    
    
    EndIf
;     
;     vec3_set(*in,0.0,0.0,0.0)
    
  Else
    
    ray_color(*ray, *in)
    *in\x * 0.5 : *in\y * 0.5 : *in\z * 0.5
  EndIf
  
EndProcedure

Define aspect_ratio.d = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
  Shared image_width, image_height
  Static focal_length.d = 1.0, viewport_height.d = 2.0
  Protected i_w.d = image_width, i_h.d = image_height
  Protected viewport_width.d = viewport_height * (i_w/i_h)
  
  Protected.vec3d point3, ray_direction, viewport3_u, viewport3_v, pixel_delta_u, pixel_delta_v, v_f, viewport_upper_left, pixel_100_loc
  
  vec3_set(point3, 0,0,0)
  vec3_set(ray_direction, 0,0,0)
  vec3_set(viewport3_u, viewport_width,0,0)
  vec3_set(viewport3_v, 0,-viewport_height,0)
  vec3_set(pixel_delta_u, viewport3_u\x / i_w, viewport3_u\y / i_w, viewport3_u\z / i_w)
  vec3_set(pixel_delta_v, viewport3_v\x / i_h, viewport3_v\y / i_h, viewport3_v\z / i_h)
  vec3_set(v_f, 0,0,focal_length)
  vec3_set(viewport_upper_left,
           point3\x - v_f\x - viewport3_u\x / 2 - viewport3_v\x / 2,
           point3\y - v_f\y - viewport3_u\y / 2 - viewport3_v\y / 2,
           point3\z - v_f\z - viewport3_u\z / 2 - viewport3_v\z / 2)
  vec3_set(pixel_100_loc, 0,0,focal_length)

  pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
  pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
  pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)
  
  Protected x.d, y.d, z.d, v.vec3d
  Protected ray.ray
  Static offset.vec3d, pixel_sample.vec3d
  Protected j,i,jj.d, ii.d, sample
  
  For j = 0 To image_height-1
    glBegin_(#GL_POINTS)
    For i = 0 To image_width-1
      x = 0.0 : y = 0.0 : z = 0.0
      For sample = 1 To #Samples_per_pixel
        ii = i + random_double() - 0.5 
        jj = j + random_double() - 0.5 
        
        pixel_sample\x = pixel_100_loc\x + ii * pixel_delta_u\x +  jj * pixel_delta_v\x
        pixel_sample\y = pixel_100_loc\y + ii * pixel_delta_u\y +  jj * pixel_delta_v\y
        pixel_sample\z = pixel_100_loc\z + ii * pixel_delta_u\z +  jj * pixel_delta_v\z
        
        vec3_subtract(ray_direction,pixel_sample,point3)

        ray\origin = point3
        ray\direction = ray_direction          
        
        ray_color_hit(@ray, @v, #Max_Depth) 
        x = x + v\x : y = y + v\y : z = z + v\z
      Next
      
      glColor3f_(linear_to_gamma(#Sample_scale * x), linear_to_gamma(#Sample_scale * y), linear_to_gamma(#Sample_scale * z)) : glVertex2s_(i,j)

    Next
    glEnd_() : glFlush_()
  Next
  
EndProcedure

OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
OpenGLGadget(0,0,0,image_width / DesktopResolutionX(),image_height / DesktopResolutionY(),#PB_OpenGL_NoFlipSynchronization)
glOrtho_(0,image_width,image_height,0,-1,1)
glDrawBuffer_(#GL_FRONT)  

Define time = ElapsedMilliseconds()
render()
  time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

Repeat
  Event = WindowEvent()
Until  Event = #PB_Event_CloseWindow

End 
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Now the fuzzy metal :)

Code: Select all

;
; Inspired from Ray Tracing in One Weekend : 
; https://raytracing.github.io/books/RayTracingInOneWeekend.html
;
; Written and adapted in Purebasic by threedslider

EnableExplicit

Enumeration Shading
  #None
  #Lambertian
  #Metal
EndEnumeration


#Samples_per_pixel = 35
#Max_Depth = 2
#Sample_scale = 1.0 / #Samples_per_pixel

; Vector
Structure Vec3d : x.d : y.d : z.d : EndStructure

Procedure vec3_normalize(*v.vec3d)
  Protected n.d = Sqr(*v\x * *v\x + *v\y * *v\y + *v\z * *v\z)
  If n <> 0.0 : n = 1.0/n : *v\x * n : *v\y * n : *v\z * n : EndIf
EndProcedure

Macro vec3_zero(v) : v\x = 0.0 : v\y = 0.0 : v\z = 0.0 : EndMacro
Macro vec3_length(v) : Sqr(v\direction\x * v\direction\x + v\direction\y * v\direction\y + v\direction\z * v\direction\z) : EndMacro
Macro vec3_length_squared(v) : (v\x * v\x + v\y * v\y + v\z * v\z) : EndMacro
Macro vec3_dot(v1, v2) : (v1\x * v2\x + v1\y * v2\y + v1\z * v2\z) : EndMacro
Macro vec3_copy(v, v1) : v\x = v1\x : v\y = v1\y : v\z = v1\z : EndMacro
Macro vec3_add(v, v1, v2) : v\x = v1\x + v2\x : v\y = v1\y + v2\y : v\z = v1\z + v2\z : EndMacro
Macro vec3_subtract(v, v1, v2) : v\x = v1\x - v2\x : v\y = v1\y - v2\y : v\z = v1\z - v2\z : EndMacro
Macro vec3_mul(v, v1, v2) : v\x = v1\x * v2\x : v\y = v1\y * v2\y : v\z = v1\z * v2\z : EndMacro
Macro vec3_scale(v, v1, s) : v\x = v1\x * s : v\y = v1\y * s : v\z = v1\z * s : EndMacro  
Macro vec3_set(v, vx, vy, vz) : v\x = vx : v\y = vy : v\z = vz : EndMacro
Macro vec3_negate(v, v1) : v\x = -v1\x : v\y = -v1\y : v\z = -v1\z : EndMacro
Macro random_double() : Random(#MAXDWORD) / #MAXDWORD : EndMacro
Macro random_min_max(min, max) : min + (max-min) * random_double() : EndMacro
Macro reflect(v, v1, n1) : v\x  = v\x - 2 * vec3_dot(v1, n1) * n1\x : v\y  = v\y - 2 * vec3_dot(v1, n1) * n1\y : v\z  = v\z - 2 * vec3_dot(v1, n1) * n1\z : EndMacro

Structure Ray ; Ray
  origin.vec3d : direction.vec3d
EndStructure

Structure Sphere ; Sphere
  center.vec3d
  color.vec3d
  radius.d
  fuzz.d
  Shading.i
EndStructure

Structure Material ; Material
  albedo.vec3d
EndStructure

Procedure.d clamp(x.d, min.d, max.d)
  If x < min
    ProcedureReturn min
  ElseIf x > max
    ProcedureReturn max
  EndIf
  ProcedureReturn x
EndProcedure

Procedure unit_vector_vec3(*v.vec3d)
  vec3_normalize(*v)
EndProcedure  

Procedure random_in_unit_sphere(*v.vec3d)
  Repeat
    *v\x = random_min_max(-1.0, 1.0)
    *v\y = random_min_max(-1.0, 1.0)
    *v\z = random_min_max(-1.0, 1.0)
    If vec3_length_squared(*v) < 1.0 : ProcedureReturn : EndIf
  ForEver
EndProcedure
Procedure random_unit_vector(*v.vec3d)
  random_in_unit_sphere(*v)
  unit_vector_vec3(*v)
EndProcedure

Macro surrounds(x, min, max) : Bool(min <= x And x <= max) : EndMacro

; Create Sphere primitve
Macro m_Sphere(center_x, center_y, center_z, radius_, color_x, color_y, color_z, fuzz_, shading_)
  AddElement(scene())
  vec3_set(scene()\center,center_x,center_y,center_z)
  vec3_set(scene()\color,color_x,color_y,color_z)
  scene()\radius = radius_
  scene()\fuzz = fuzz_
  scene()\Shading = shading_
EndMacro

Global NewList scene.Sphere()

m_Sphere(0.0, -100.5, -1.0, 100.0, 0.8, 0.8, 0.0, 0.0, #Lambertian); ; << - PJ240614 - pushed this down
m_Sphere(0.0, 0.0, -1.1, 0.5, 0.1, 0.2, 0.5, 0.0, #Lambertian)
m_Sphere(-1.0, 0.0, -1.0, 0.5, 0.8, 0.8, 0.8, 0.3, #Metal)
m_Sphere(1.0, 0.0, -1.0, 0.5, 0.8, 0.6, 0.2, 0.99, #Metal)

Procedure near_zero (*v.vec3d) ;Near zero for pathtracing calculation
  Protected e.d = 1e-8
  ProcedureReturn Bool((Abs(*v\x) < e) And (Abs(*v\y) < e) And (Abs(*v\z) < e))
EndProcedure


; Hit for 3D objects
Procedure.d hit(*_s.Sphere, *ray.Ray, max.d)
  
  Protected oc.vec3d
  Protected root.d = 0.0
  Protected min.d = 0.0001
  
  vec3_subtract(oc, *_s\center,*ray\origin) 
  
  Protected a.d = vec3_length_squared(*ray\direction)
  Protected h.d = vec3_dot(*ray\direction,oc)
  Protected c.d = vec3_length_squared(oc) - *_s\radius * *_s\radius
  
  Protected discriminant.d = h * h - a * c
  
  If discriminant < 0.0 : ProcedureReturn #False : EndIf
  
  Protected sqrtd.d = Sqr(discriminant)
  
  root = (h - sqrtd) / a
  
  If Not surrounds(root, min, max)
    root = (h + sqrtd) / a
    If Not surrounds(root, min, max) : ProcedureReturn 0.0 : EndIf
  EndIf 

  ProcedureReturn root
  
EndProcedure

; Hit for intersection
Procedure.b intersect( *ray.Ray, *t.double, *id.Integer )
  Protected d.d, inf.d = 999.999
  *t\d = inf
  ForEach scene()
    d = hit(@scene(), *ray, *t\d)
    If d
      *t\d  = d
      *id\i = ListIndex(scene())
    EndIf
  Next
  ProcedureReturn Bool( *t\d < inf )
EndProcedure

; Unit vector Y for background
Procedure.d unit_vector_y(*vy.Ray)
  Protected len.d
  Static y.d : y.d = 0
  len = vec3_length(*vy)
  If len <> 0.0 : y = *vy\direction\y / len  : EndIf
  ProcedureReturn y
EndProcedure

Procedure.d ray_color(*rr.Ray, *in.vec3d)
  Protected a.d
  Static color1.vec3d, color2.vec3d, unit_directionection.vec3d
  
  color1\x = 1.0 : color1\y = 1.0 : color1\z = 1.0
  color2\x = 0.5 : color2\y = 0.7 : color2\z = 1.0

  unit_directionection\y = unit_vector_y(*rr)
  a = 0.5 * (unit_directionection\y + 1.0) 
  *in\x + ((1.0 - a) * color1\x + a * color2\x)
  *in\y + ((1.0 - a) * color1\y + a * color2\y)
  *in\z + ((1.0 - a) * color1\z + a * color2\z)
EndProcedure  

Procedure.d linear_to_gamma(num.d)
  If num > 0.0
    ProcedureReturn Sqr(num)
  EndIf
  
  ProcedureReturn 0.0
  
EndProcedure


Procedure.d ray_color_hit(*ray.Ray, *in.vec3d, depth)
  Protected.vec3d x, p
  Protected t.d = 0.0
  Protected id.i = 0
  Protected.ray rr, unit_vector, scattered
  Protected.Vec3d out, reflected, fuzz, fuzz_unit, fuzz_random, fuzz_tmp
  
  If depth < 0
    vec3_set(*in,0.0,0.0,0.0) : ProcedureReturn
  EndIf
  Protected.vec3d ruv, n, nl
  If intersect(*ray, @t, @id)
    SelectElement(scene(),id)
    Protected *obj.Sphere = scene()
    
    ; Point at parameter
    x\x = *ray\origin\x + t * *ray\direction\x
    x\y = *ray\origin\y + t * *ray\direction\y
    x\z = *ray\origin\z + t * *ray\direction\z
    
    ; Norm
    vec3_subtract(n, x, *obj\center)
    vec3_normalize(n)
    
    ; Set to normal
    If vec3_dot(n, *ray\direction ) < 0.0
      vec3_copy(nl, n)
    Else
      vec3_negate(nl, n)
    EndIf
    
    
    If *obj\shading = #Lambertian
      vec3_copy(rr\origin, x)
  
      random_unit_vector(@ruv) 
      vec3_add(rr\direction,nl,ruv) 
      
        
      ray_color_hit(rr, *in , depth-1)
      *in\x * *obj\color\x : *in\y * *obj\color\y : *in\z * *obj\color\z 
    
    ElseIf *obj\shading = #Metal
      
      reflect(reflected, *ray\direction, nl)
      
      vec3_copy(fuzz_unit, reflected)
      
      unit_vector_vec3(@fuzz_unit) : random_unit_vector(@fuzz_random)
      
      vec3_scale (fuzz_tmp, fuzz_random, *obj\fuzz)
      
      vec3_add (fuzz, fuzz_unit, fuzz_tmp)
    
      vec3_copy(rr\origin, x)
      
      vec3_copy(rr\direction, fuzz)
      
      If vec3_dot(rr\direction, nl) > 0
      ray_color_hit(rr, *in , depth-1)
      *in\x * *obj\color\x : *in\y * *obj\color\y : *in\z * *obj\color\z 
      EndIf
    
    
    EndIf
;     
;     vec3_set(*in,0.0,0.0,0.0)
    
  Else
    
    ray_color(*ray, *in)
    *in\x * 0.5 : *in\y * 0.5 : *in\z * 0.5
  EndIf
  
EndProcedure

Define aspect_ratio.d = 16.0 / 9.0
Define image_width = 800

Define image_height = Int(image_width / aspect_ratio)

If image_height < 1
  image_height = 1
Else
  image_height = image_height
EndIf

; Render engine
Procedure render()
  Shared image_width, image_height
  Static focal_length.d = 1.0, viewport_height.d = 2.0
  Protected i_w.d = image_width, i_h.d = image_height
  Protected viewport_width.d = viewport_height * (i_w/i_h)
  
  Protected.vec3d point3, ray_direction, viewport3_u, viewport3_v, pixel_delta_u, pixel_delta_v, v_f, viewport_upper_left, pixel_100_loc
  
  vec3_set(point3, 0,0,0)
  vec3_set(ray_direction, 0,0,0)
  vec3_set(viewport3_u, viewport_width,0,0)
  vec3_set(viewport3_v, 0,-viewport_height,0)
  vec3_set(pixel_delta_u, viewport3_u\x / i_w, viewport3_u\y / i_w, viewport3_u\z / i_w)
  vec3_set(pixel_delta_v, viewport3_v\x / i_h, viewport3_v\y / i_h, viewport3_v\z / i_h)
  vec3_set(v_f, 0,0,focal_length)
  vec3_set(viewport_upper_left,
           point3\x - v_f\x - viewport3_u\x / 2 - viewport3_v\x / 2,
           point3\y - v_f\y - viewport3_u\y / 2 - viewport3_v\y / 2,
           point3\z - v_f\z - viewport3_u\z / 2 - viewport3_v\z / 2)
  vec3_set(pixel_100_loc, 0,0,focal_length)

  pixel_100_loc\x = viewport_upper_left\x + 0.5 * (pixel_delta_u\x + pixel_delta_v\x)
  pixel_100_loc\y = viewport_upper_left\y + 0.5 * (pixel_delta_u\y + pixel_delta_v\y)
  pixel_100_loc\z = viewport_upper_left\z + 0.5 * (pixel_delta_u\z + pixel_delta_v\z)
  
  Protected x.d, y.d, z.d, v.vec3d
  Protected ray.ray
  Static offset.vec3d, pixel_sample.vec3d
  Protected j,i,jj.d, ii.d, sample
  
  For j = 0 To image_height-1
    glBegin_(#GL_POINTS)
    For i = 0 To image_width-1
      x = 0.0 : y = 0.0 : z = 0.0
      For sample = 1 To #Samples_per_pixel
        ii = i + random_double() - 0.5 
        jj = j + random_double() - 0.5 
        
        pixel_sample\x = pixel_100_loc\x + ii * pixel_delta_u\x +  jj * pixel_delta_v\x
        pixel_sample\y = pixel_100_loc\y + ii * pixel_delta_u\y +  jj * pixel_delta_v\y
        pixel_sample\z = pixel_100_loc\z + ii * pixel_delta_u\z +  jj * pixel_delta_v\z
        
        vec3_subtract(ray_direction,pixel_sample,point3)

        ray\origin = point3
        ray\direction = ray_direction          
        
        ray_color_hit(@ray, @v, #Max_Depth) 
        x = x + v\x : y = y + v\y : z = z + v\z
      Next
      
      glColor3f_(linear_to_gamma(#Sample_scale * x), linear_to_gamma(#Sample_scale * y), linear_to_gamma(#Sample_scale * z)) : glVertex2s_(i,j)

    Next
    glEnd_() : glFlush_()
  Next
  
EndProcedure

OpenWindow(0, 0, 0, image_width / DesktopResolutionX(), image_height / DesktopResolutionY(), "Inspired from Raytracing in One Weekend", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
OpenGLGadget(0,0,0,image_width / DesktopResolutionX(),image_height / DesktopResolutionY(),#PB_OpenGL_NoFlipSynchronization)
glOrtho_(0,image_width,image_height,0,-1,1)
glDrawBuffer_(#GL_FRONT)  

Define time = ElapsedMilliseconds()
render()
  time = ElapsedMilliseconds() - time
SetWindowTitle(0,"Inspired from Raytracing in One Weekend - RenderTime: "+Str(time)+"ms")

Define Event

Repeat
  Event = WindowEvent()
Until  Event = #PB_Event_CloseWindow

End 
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: Raytracing in One Weekend

Post by threedslider »

Come back to this my topic of Raytracing in one weekend ! :mrgreen:

I would like to finish my three chapter for my loved with Purebasic :o

Hope to finish this adventure of raytracing !

I will resume this my project very soon :wink:
Post Reply