def crear_rayo_aleatorio(ray, punto, pared): """Crea un rayo aleatorio a partir del rabote :param ray: El rayo actual :param punto: El punto de interseccion entre el rayo y la pared :param pared: La pared de interseccion :return: Un rayo con el origen en el punto de rebote """ # Verificar especularidad if pared.especularidad: return crear_rayo_especular(ray, punto, pared) if pared.horizontal: if ray.origen.y > punto.y: # pared inferior angulo = math.radians(random.uniform(5, 175)) return Ray(Point(punto.x, punto.y + 2), angulo) else: # pared superior angulo = math.radians(random.uniform(-175, -5)) return Ray(Point(punto.x, punto.y - 2), angulo) else: if ray.origen.x < punto.x: # pared derecha angulo = math.radians(random.uniform(-270, -90)) return Ray(Point(punto.x - 2, punto.y), angulo) else: # pared izquierda angulo = math.radians(random.uniform(-85, 85)) return Ray(Point(punto.x + 2, punto.y), angulo)
def get_ray_intersection_point(self, dist): """Calcular el punto de interseccion entre el rayo y el segmento :param dist: La distancia entre ambos puntos :return: El punto de interseccion entre el rayo y el segmento """ pt = Point() pt.x = self.origen.x + self.direccion.x * dist pt.y = self.origen.y + self.direccion.y * dist return pt
def vector_from_angle(angle): """Retorna un vector a partir de un angulo :param angle: El angulo que se desea utilizar :return: El nuevo vector """ return normalize(Point(1 * math.cos(angle), 1 * math.sin(angle)))
def render(): """Renderiza la imagen con iluminacion global :return: """ s = time.time() for i in range(515): for j in range(515): # Obtener punto en la imagen image_point = Point(i, j) pixel_color = 0 for light_source in light_sources: # Calcular direccion a la direccion = light_source - image_point light_distance = get_vector_length(direccion) # Verificar con interseccion en la pared free = True for wall in segments: if wall.transparencia: continue # Revisar interseccion distancia_interseccion = ray_segment_intersect( image_point, normalize(direccion), wall.point1, wall.point2) # Verificar colision if distancia_interseccion != -1.0 and distancia_interseccion < light_distance: free = False break # Verificar si no hay colision if free: # Calcular intensidad intensidad = (1.2 - (light_distance / 600)) ** 2 # Obtener color del pixel valores = (imagen[int(image_point.y)] [int(image_point.x)])[:3] # Combinar color, fuente de luz y color de la luz valores = valores * intensidad * light_color # Agregar todas las fuentes de luz pixel_color += valores # Promedia pixel y asignar valor canvas[int(image_point.x)][int(image_point.y)] = pixel_color // len(light_sources) # Realizar Monte Carlo Path tracing rayos_efectivos = 0 for samples in range(0, number_samples): # Iniciar rayos initial_ray = Ray(image_point, random.uniform(0, 360)) # Iluminacion indirecta incoming_color = trace_path(initial_ray) # Verificar si se obtiene un color if not isinstance(incoming_color, type(np.array([0, 0, 0]))): continue pixel_color += incoming_color rayos_efectivos += 1 #Promediar color final canvas[int(image_point.x)][int(image_point.y)] = pixel_color // (len(light_sources) + rayos_efectivos) e = time.time() print(e - s)
def crear_rayo_especular(ray, punto, pared): """Crea un rayo especular :param ray: El rayo saliente :param punto: El punto de interseccion del rayo :param pared: La pared de interseccion :return: El rayo especular """ # Obtener angulo angulo = np.rad2deg( get_angle_between(Segment(ray.origen, punto, False, False), pared)) if pared.horizontal: # Por arriba if ray.origen.y > punto.y: # Por la derecha if ray.origen.x > punto.x: return Ray(Point(punto.x, punto.y + 2), math.radians(2 * (90 - angulo) + angulo)) else: return Ray(Point(punto.x, punto.y + 2), math.radians(-angulo)) # Por la derecha else: # Por abajo if ray.origen.x > punto.x: return Ray(Point(punto.x, punto.y - 2), math.radians(180 - angulo)) else: return Ray(Point(punto.x, punto.y - 2), math.radians(-(180 + (angulo - 180)))) else: # Vertical y por la derecha if ray.origen.x > punto.x: # Por arriba if ray.origen.y > punto.y: return Ray(Point(punto.x + 2, punto.y), math.radians(-(90 - angulo))) # Por abajo else: print(angulo) return Ray(Point(punto.x + 2, punto.y), math.radians(-(90 - angulo))) else: # Por abajo if ray.origen.y > punto.y: return Ray(Point(punto.x - 2, punto.y), math.radians(angulo + 90)) # Por arriba else: return Ray(Point(punto.x - 2, punto.y), math.radians(angulo + 90))
def ray_segment_intersect(self, segmento): """Calcula la distancia entre el punto de interseccion y el origen del rayo :param segmento: El segmento que se desea verificar :return: La distancia entre ambos puntos """ # calculate vectors v1 = self.origen - segmento.point1 v2 = segmento.point2 - segmento.point1 v3 = Point(-self.direccion.y, self.direccion.x) dot = v2.dot(v3) if abs(dot) < 0.000001: return -1.0 t1 = v2.cross(v1) / dot t2 = v1.dot(v3) / dot if t1 >= 0.0 and (t2 >= 0.0 and t2 <= 1.0): return t1 return -1.0
def ray_segment_intersect(origen, direccion, point1, point2): """Determina la interseccion entre un rayo y un segmento :param origen: El origen del rayo :param direccion: La direccion del rayo :param point1: El primer punto del rayo :param point2: El segundo punto del rayo :return: La distancia entre el origen del rayo y el segmento """ # Calcular vectores v1 = origen - point1 v2 = point2 - point1 v3 = Point(-direccion.y, direccion.x) dot = v2.dot(v3) if abs(dot) < 0.000001: return -1.0 t1 = v2.cross(v1) / dot t2 = v1.dot(v3) / dot if t1 >= 0.0 and (t2 >= 0.0 and t2 <= 1.0): return t1 return -1.0
border = 50 pygame.init() screen = pygame.display.set_mode( (WIDTH + (2 * border), HEIGHT + (2 * border))) # Segmentos y fuentes pygame.display.set_caption("Path Tracer") done = False clock = pygame.time.Clock() # Init random random.seed() blank = Image.new("RGB", (516, 516), (0, 0, 0)) canvas = np.array(blank) # Load image file img_file = Image.open("assets/EscenaProgra.png") imagen = np.array(img_file) light_sources = [Point(47, 303), Point(47, 142), Point(475, 76)] # Color de la luz light_color = np.array([1, 1, 0.75]) segments = [ # Segment(Point(180, 135), Point(215, 135), True, False), # Segment(Point(285, 135), Point(320, 135), True, False), # Segment(Point(320, 135), Point(320, 280), False, False), # Segment(Point(320, 320), Point(320, 355), False, False), # Segment(Point(320, 355), Point(215, 355), True, False), # Segment(Point(180, 390), Point(180, 286), False, False), # Segment(Point(180, 286), Point(140, 286), True, False,True), # Segment(Point(320, 320), Point(360, 320), True, False), # Segment(Point(180, 250), Point(180, 135), False, False), #Escena rpgmaker