0
$\begingroup$

I am trying to implement a simple raytracer in python. I am following this tutorial here

here is my code, i have explained in comments the relevant parts .All i am getting is a blank image.

Program

import math
from collections import namedtuple


class P3Image:
    def __init__(self, width,  height):
        self.width = width
        self.height = height
        self.img = [[0]*width]*height

    def pixel(self, x, y, color):
        self.img[y][x] = color

    def __rescale(self, color):
        ''' rescale a color range from 0-1 to 0-225 '''
        return Vec3(round(color.x*255), round(color.y*255) , round(color.z*255))

    def save(self, filename):
        with open(filename, 'w') as f:
            f.write(f'P3 {self.width} {self.height}\n255\n')
            for row in self.img:
                for color in row:
                    color = self.__rescale(color)
                    f.write(f'{color.x} {color.y} {color.z}  ')
                f.write('\n')


class Vec3:
    def __init__(self, x, y, z):

        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return f'Vec3({self.x},{self.y},{self.z})'

    def __sub__(self, other):
        return Vec3(self.x - other.x , self.y - other.y , self.z - other.z)

    def __add__(self, other):
        return Vec3(self.x +  other.x , self.y + other.y , self.z + other.z)

    def mag(self):
        return math.sqrt(self.x**2, self.y**2, self.z**2)

    def dot(self, other):
        if isinstance(other, Vec3):
            return self.x * other.x + self.y * other.y + self.z * other.z
        raise Exception('this should recieve an arfument of type Vec3') 

    def cross(self, other):
        if isinstance(other, Vec3):
            return Vec3( self.y* other.z - self.x* other.y, self.z* other.x - self.x* other.z , self.x * self.y - self.y * self.x)
        raise Exception('this should recieve an arfument of type Vec3') 


def trace_ray(O, D, t_min, t_max):
    closest_t = math.inf
    closest_sphere = None

    for sphere in scene['spheres']:
        t1, t2 = ray_sphere_intersect(O, D, sphere)

        if  (t_min < t1 < t_max) and t1 < closest_t:
            closest_t  = t1
            closest_sphere = sphere

        if (t_min < t2 < t_max) and t2 < closest_t:
            closest_t = t2

            closest_sphere = sphere

    if closest_sphere == None:
        return Vec3(1,1,1)
    else:
        return closest_sphere['color']



def dot(v1, v2):
    return v1.x* v2.x + v1.y * v2.y + v1.z * v2.z

def ray_sphere_intersect(O, D, sphere):
    C = sphere['center']
    r = sphere['radius']
    oc = O - C
    k1 = dot(D,D)
    k2 = 2*dot(oc, D)
    k3 = dot(oc, oc) - r*r

    discriminant = k2*k2 - 4*k1*k3

    if discriminant < 0:
        return math.inf, math.inf

    t1 = (-k2 + math.sqrt(discriminant))/(2*k1)
    t2 = (-k2 - math.sqrt(discriminant))/(2*k1)
    return t1, t2


def canvas_to_viewport(x,y):
    return Vec3(x*Vw/Cw, y*Vh/Ch, d)


def c_to_i(x, y):
    '''canvas to image'''

    xnew = xmin + (xmax - xmin)/Cw
    ynew = ymin + (ymax - ymin)/Ch
    return Vec3(xnew, ynew, d)



# define scene with one sphere 
scene = {
        'spheres':[
                {
                'center':Vec3(0,0 ,0),
                'radius': 0.5,
                'color':Vec3(1,0,0)
                },
            ]
    }


if __name__ == '__main__':

    WIDTH  = 320 
    HEIGHT = 200
    camera = Vec3(0, 0, -1)

    aspect_ratio = WIDTH/HEIGHT

    xmin = -1 
    xmax = 1  

    ymax = 1/aspect_ratio
    ymin = -1*ymax

    xstep = (xmax - xmin) / (WIDTH-1)
    ystep = (ymax - ymin) / (HEIGHT-1)

    image = P3Image(WIDTH, HEIGHT)

    for j in range(HEIGHT):
        y  = ymin + j*ystep
        for i in range(WIDTH): 
            x  = xmin + i*xstep
            ray = Vec3(x,y,0) - camera
            color = trace_ray(camera, ray, -1, math.inf)

            print(i,j,color)

            image.pixel(i, j, color)


    image.save('ii.ppm')

NOTE: I modified the main render loop to avoid negative index while generating image as suggested by @Oliver. but now even now i get a blank image. but i am sure the rays are hitting sphere properly as evident from the output of print statement in code.

$\endgroup$
  • $\begingroup$ The print(x,y, color) trace shows it is working for me. It's just the image writing code which fails. I suspect because of the negative indices. $\endgroup$ – Olivier May 5 at 1:28
  • $\begingroup$ @Olivier yes, i figured that by looking at the pattern of print(x,y,color), is having a viewport necessary as i have seen some code directly doing for x in range(Cw) and for y in range(Ch) , $\endgroup$ – anekix May 5 at 6:36
  • $\begingroup$ you could loop that way and redefine canvas_to_viewport accordingly. There's no single correct way to do this. $\endgroup$ – Olivier May 5 at 13:15
  • $\begingroup$ @Olivier I changed the main render loop to avoid the negative index while generating images but still the output is blank, I have updated the original code above. $\endgroup$ – anekix May 5 at 18:36
0
$\begingroup$

Ok, after several hours of debugging, I finally got the answer and posting it here. The issue was in image generation.

class P3Image:
    def __init__(self, width,  height):
        self.width = width
        self.height = height

        self.img = [[None for _ in range(width)] for _ in range(height)]

    def set_pixel(self, x, y, color):
        self.img[y][x] = color
        print(self.img[19][173], y,x, color)


    def __rescale(self, color):
        ''' rescale a color range from 0-1 to 0-225 '''
        return Vec3(round(color.x*255), round(color.y*255) , round(color.z*255))

    def save(self, filename):
        with open(filename, 'w') as f:

            f.write(f'P3 {self.width} {self.height}\n255\n')
            for row in self.img:
                for color in row:
                    c = self.__rescale(color)
                    f.write(f'{c.x} {c.y} {c.z}  ')

                f.write('\n')
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.