# Ray tracing Bug with Diffuse material

I am trying to do ray tracing in python (following the tutorial given in Ray Tracing in a weekend). Basically I am shooting rays from eye and the recursively bounce around, each time they hit something they become weaker (actually this is reverse ray casting but you get the idea). The output I am getting is incorrect.

My output:

Expected output:

The shadows are messed up. What could be wrong?

My code:

import numpy as np
import sys
import random
from PIL import Image
from math import *
from util import *

width  = 60
height = 60

samples = 20

#-----------------------------------------------------------------------

def reflected(vector, axis):
return vector - axis * 2 * vector.dot(axis)

def RandomPointInSphere():
p = None
while True:
p = Vector(random.uniform(0,1),random.uniform(0,1),random.uniform(0,1))*2 - Vector(1,1,1)
if(p.dot(p) < 1):
break
return p

def GetNearestObject(objects, ray):
nearest_obj = None
min_hit = Intersection(None, INF, None, None)

for obj in objects:
hit = obj.intersect(ray)
if(hit.distance < min_hit.distance):
nearest_obj = obj
min_hit = hit

return min_hit

#-----------------------------------------------------------------------

def RayColor(objects, ray):
# Part 1: Diffuse Material
result = GetNearestObject(objects, ray)
if(result.point != None):
P = result.point
N = result.normal
E = RandomPointInSphere()
target = P + N + E

newRay = Ray(ray.origin, (target - ray.origin).normalize())
return RayColor(objects, newRay)*0.5
else:
t = 0.5 * (ray.direction.y + 1.0);
color = Vector(1.0, 1.0, 1.0)*(1.0 - t) + Vector(0.5, 0.7, 1.0)*t
color.x = min(color.x,1.0)
color.y = min(color.y,1.0)
color.z = min(color.z,1.0)
return Vector(1,1,1)

#-----------------------------------------------------------------------
def main():
global bitmap

eye    = Vector(0,0,1)
ratio  = float(width) / height
screen = Screen(-1, 1 / ratio, 1, -1 / ratio, 0)

objects = []
objects.append(Sphere(Vector(-0.2,0,-1),  0.7, Material(Vector(0.1,0,0),  Vector(0.7,0,0),    Vector(1,1,1), 100, 0.5)))

objects.append(Sphere(Vector(0,-9000,0),  9000-0.7, Material(Vector(0.1,0.1,0.1),Vector(0.6,0.6,0.6),Vector(1,1,1), 100, 0.5)))

light = Light(Vector(5,5,5), Material(Vector(1,1,1),Vector(1,1,1),Vector(1,1,1)))

for frame in range(1):

img    = Image.new(mode = "RGB", size=(width, height), color=Color.WHITE)
bitmap = img.load() # create the pixel data

#--------------------------------------------------------------
#--------------------------------------------------------------
sys.setrecursionlimit(10000)

#breakpoint()

deltaX = (screen.right - screen.left)/(width-1)
deltaY = (screen.top - screen.bottom)/(height-1)

for y in range(height):
for x in range(width):
pixel     = Vector(screen.left+x*deltaX, screen.top-y*deltaY, screen.z)
direction = (pixel - eye).normalize()
pixelRay = Ray(eye, direction)

# Part 1: Diffuse Material
color = Vector(0,0,0)
for s in range(samples):
color += RayColor(objects, pixelRay)

color *= 1.0/samples
#color = Vector(sqrt(color.x), sqrt(color.y), sqrt(color.z))
bitmap[x,y] = (int(color.x*255), int(color.y*255), int(color.z*255))

print("progress: %d %%" % ((y+1)/height*100.0))

#--------------------------------------------------------------
#--------------------------------------------------------------
#--------------------------------------------------------------

img.show()
img.save("pic1.png")
#img.save("images/fig" + f'{frame:06}' + ".png")
#print("Saving ---> images/fig" + f'{frame:06}' + ".png")
#img.close()

main()
$$$$
`