raytrace.py

Created by andreanx

Created on April 15, 2020

6.58 KB

A raytracing demo. Requires version 13 or above. Just adapted and optimized it for the NumWorks Kandinsky and 32K heap from a Casio version by Lightmare : https://www.planet-casio.com/Fr/programmes/programme4056-4-raytracing-lightmare-jeux-divers.html


class polyscr:
  w, h = 0, 0
  has_show_screen = False
  # color mode :
  # 0: (R8, G8, B8)
  # 1: int RGB-565
  color_mode = 0

  show_screen = lambda self: None

  def color(self, r, g=0, b=0):
    if not self.color_mode:
      return (isinstance(r, tuple) or isinstance(r,list)) and r or (r,g,b)
    else:
      if isinstance(r, tuple) or isinstance(r,list):
        r, g, b = r[0], r[1], r[2]
      return r<<11 | g<<5 | b

  def __init__(self):
    try:
      from nsp import Texture as scr
      self.w, self.h = 320, 240
      scr = scr(self.w, self.h, None)
      self.getPixel = scr.getPx
      self.setPixel = scr.setPx
      self.show_screen = scr.display
      self.has_show_screen = True
      self.color_mode = 1
    except:
      try:
        import ti_graphics as scr
        self.getPixel = scr.getPixel
        self.setPixel = scr.setPixel
      except ImportError:
        try:
          import casioplot as scr
          self.show_screen = scr.show_screen
          self.has_show_screen = True
        except ImportError:
          import kandinsky as scr
        self.getPixel = scr.get_pixel
        self.setPixel = scr.set_pixel

    self.get_pixel = self.getPixel
    self.set_pixel = self.setPixel
    self.getPx = self.getPixel
    self.setPx = self.setPixel
    self.display = self.show_screen

    if self.w <= 0:
      def isRealPixel(x, y):
        c = self.get_pixel(x, y)
        if c == self.color(0,0,0):
          self.set_pixel(x, y, self.color(255,0,0))
          c = self.get_pixel(x, y)
        return c is not None and c != self.color(0, 0, 0)

      self.w,self.h,dw,dh = 0,0,1,1
      while dw or dh:
        if not isRealPixel(self.w-(dw==0),self.h-(dh==0)):
          if isRealPixel(self.w,self.h-1): dh = 0
          elif isRealPixel(self.w-1,self.h): dw = 0
          else: dw,dh=0, 0
        self.w+=dw
        self.h+=dh

plt = polyscr()

import math

def inputintminmax(s, v_min, v_max, show=False):
  if show:
    s+="? ("+str(v_min)+"-"+str(v_max)+")\n"
  v = v_min - 1
  while v<v_min or v>v_max:
    v = int(input(s))
  return v

l = inputintminmax("Language / langue:\n0: english\n1: francais\nChoix / choice? ", 0, 1)
lang=[
  ["Rendering size h*w:\nh=", "w=", "Marble type:\n0: blue\n1: transparent\nChoice? ", "Refresh display:\n0: at the end\n1: each new line\n2: each new pixel\nChoice? "],
  ["Taille rendu h*L:\nh=", "L=", "Type bille:\n0: bleue\n1: transparente\nChoix? ", "Rafraichissement:\n0: a la fin\n1: chaque ligne\n2: chaque pixel\nChoix? "]]

h = inputintminmax(lang[l][0], 1, plt.h, True)
w = inputintminmax(lang[l][1], 1, plt.w, True)
sphere_status = inputintminmax(lang[l][2], 0, 1)
progress = plt.has_show_screen and inputintminmax(lang[l][3], 0, 2) or 0

sphere = (0,7,1,2, sphere_status)

plane = (0,0,-0.25,-1)

light = (2,2,10)

inter_sph = [0,0,0]

diam_vec = [0, 0, 0]

ray = [0,0,0]
light_ray = [0,0,0]

a = 0
b = 0
c = 0
cam_vec = [0,1,0,math.pi / 4,math.pi / 4]

cam_vec[3] += math.acos(cam_vec[0] / math.sqrt(2))
cam_vec[4] += math.asin(cam_vec[1] / math.sqrt(2))

cor_x = math.cos(cam_vec[3]) * math.sqrt(2)
cor_y = math.cos(cam_vec[4]) * math.sqrt(2)
cor_z = math.cos(cam_vec[2] / math.sqrt(2) + math.pi / 4) * math.sqrt(2)

for i in range(3):
    ray[i] = cam_vec[i] + (cor_x,cor_y,cor_z)[i]

def calc_r(n):
    return (-2*sum([inter_sph[i]*light_ray[i] - light_ray[i]*sphere[i] for i in range(3)]) + ((n==2) - (n==1)) * math.sqrt(delta_2)) / 2*sum([light_ray[i]**2 for i in range(3)])                    

def calc_delta_2():
    return 4*sum([inter_sph[i]*light_ray[i] - light_ray[i]*sphere[i] for i in range(3)])**2 - 4*sum([light_ray[i]**2 for i in range(3)])*(sum([(inter_sph[i] - sphere[i])**2 for i in range(3)]) - sphere[3]**2)

def do_1(f):
    global color, delta_2
    for i in range(3):
        inter_sph[i] = (a,b,c)[i]+t*ray[i]
        light_ray[i] = light[i] - inter_sph[i]
    
    if sphere[4] == 0 :
        delta_2 = calc_delta_2()
        color = [0,0,255]
        if delta_2 >= 0 :
            r_1 = calc_r(1)
            r_2 = calc_r(2)
            
            if abs(r_1) <= abs(r_2) :
                color[2] -= int(r_2*255/(abs(r_1)+r_2))
    else :
        for i in range(3):
            diam_vec[i] = inter_sph[i] - sphere[i]

        k = sum([diam_vec[i]*((a,b,c)[i] - sphere[i]) for i in range(3)]) / sum([diam_vec[i]**2 for i in range(3)])
        l = sum([diam_vec[i]**2 * k**2 + 2 * diam_vec[i]*((a,b,c)[i] - sphere[i]) + (sphere[i] - (a,b,c)[i])**2 for i in range(3)])

        proj = [k*diam_vec[i] + sphere[i] for i in range(3)]
        
        refl = [2*(proj[i] - (a,b,c)[i]) + (a,b,c)[i] for i in range(3)]
        reflect = [refl[i] - inter_sph[i] for i in range(3)]

        if sum([plane[i]*reflect[i] for i in range(3)]) != 0 :
            t_2 = -(sum([plane[i]*inter_sph[i] for i in range(3)]) + plane[3]) / sum([plane[i] * reflect[i]])

            if t_2 > 0 :
                color[(inter_sph[f]+t_2*reflect[f])%2<1 and (inter_sph[not f]+t_2*reflect[not f])%2>=1 or (inter_sph[f]+t_2*reflect[f])%2>=1 and (inter_sph[not f]+t_2*reflect[not f])%2<1] = 255
                for i in range(3):
                    inter_sph[i] = inter_sph[i]+t_2*reflect[i]
                    light_ray[i] = light[i] - inter_sph[i]

                if calc_delta_2() >= 0 :
                    color[color != [255, 0, 0]] = 100

def do_2():
    color[(b+t_2*ray[1])%2<1 and (a+t_2*ray[0])%2>=1 or (b+t_2*ray[1])%2>=1 and (a+t_2*ray[0])%2<1] = 255
    for i in range(3):
        inter_sph[i] = (a,b,c)[i]+t_2*ray[i]
        light_ray[i] = light[i] - inter_sph[i]

    if calc_delta_2() >= 0 :
        color[color != [255, 0, 0]] = 100


for y in range(h) :
    for x in range(w) :

        color = [0,0,0]
        ray[0] = x * 2/w + cor_x
        ray[2] = cor_z - y * 2/h

        delta = 4*(sum([(a,b,c)[i]*ray[i] for i in range(3)]) - sum([ray[i]*sphere[i] for i in range(3)]))**2 - 4*sum([ray[i]**2 for i in range(3)])*(sum([((a,b,c)[i] - sphere[i])**2 for i in range(3)]) - sphere[3]**2)
        
        if delta > 0 :
            t = (-2*sum([(a,b,c)[i]*ray[i] - ray[i]*sphere[i] for i in range(3)]) - math.sqrt(delta)) / 2*sum([ray[i]**2 for i in range(3)])

        if sum([plane[i]*ray[i] for i in range(3)]) != 0 :
            t_2 = -(sum([plane[i]*(a,b,c)[i] for i in range(3)]) + plane[3]) / sum([plane[i]*ray[i] for i in range(3)])

        if t_2 > 0 and delta > 0 and t_2 > t or delta >= 0 and t_2 <= 0:
            do_1(1 - (t_2 > 0 and delta > 0 and t_2 > t))
        elif t_2 > 0 and (delta > 0 and t_2 <= t or delta < 0):
            do_2()

        plt.set_pixel(x, y, plt.color(color))
  
        if progress>=2 or x == w-1 and progress>=1 or y == h-1 and x == w-1: plt.show_screen()