Пример #1
0
 def test_sub(self):
     a = Vec3(0.2, 0.3, 0.4)
     b = Vec3(0.1, 0.1, 0.1)
     c = a - b
     self.assertAlmostEqual(c.x, 0.1, 5)
     self.assertAlmostEqual(c.y, 0.2, 5)
     self.assertAlmostEqual(c.z, 0.3, 5)
Пример #2
0
 def test_add(self):
     a = Vec3(0.1, 0.1, 0.2)
     b = Vec3(0.2, 0.3, 0.5)
     c = a + b
     self.assertAlmostEqual(c.x, 0.3, 5)
     self.assertAlmostEqual(c.y, 0.4, 5)
     self.assertAlmostEqual(c.z, 0.7, 5)
Пример #3
0
 def test_multVector(self):
     a = Vec3(0.2, 0.3, 0.4)
     b = Vec3(2, 2, 2)
     c = a * b
     self.assertAlmostEqual(c.x, 0.4, 5)
     self.assertAlmostEqual(c.y, 0.6, 5)
     self.assertAlmostEqual(c.z, 0.8, 5)
Пример #4
0
	def scatter(self, rIn, rec): # returns (attenuation, scattered) or None
		reflected = Vec3.reflect(Vec3.unitVector(rIn.direction()), rec.normal)
		scattered = Ray(rec.p, reflected + Vec3.randomInUnitSphere()*self.fuzz)
		attenuation = self.albedo
		if Vec3.dot(scattered.direction(), rec.normal) > 0:
			return (attenuation, scattered)
		else:
			return None
Пример #5
0
def random_in_unit_sphere():
    while True:
        p = Vec3(float(random.random()), float(random.random()),
                 float(random.random())).Scale(2.0).Sub(Vec3(1.0, 1.0, 1.0))
        if p.dot(p) >= 1.0:
            pass
        else:
            return p
Пример #6
0
 def __init__(self):
     '''
     @param {t: hit time, p: hit position, normal: hit point normal, mat_ptr: material} 
     @return: None
     '''
     self.t = 0
     self.p = Vec3(0.0, 0.0, 0.0)
     self.normal = Vec3(0.0, 0.0, 0.0)
     self.mat_ptr = None
Пример #7
0
 def render_fast(self):
     r = float(self.camera.width) / self.camera.height
     S = (-1., 1. / r + .25, 1., -1. / r + .25)
     x = jnp.tile(jnp.linspace(S[0], S[2], self.camera.width),
                  self.camera.height)
     y = jnp.repeat(jnp.linspace(S[1], S[3], self.camera.height),
                    self.camera.width)
     origin = Vec3(self.camera.origin[0], self.camera.origin[1],
                   self.camera.origin[2])
     image = self.ray_trace(origin, (Vec3(x, y, 0) - origin).norm())
     return image
Пример #8
0
 def dot(self, other):
     '''
     get product with self and other, self is the left operand.
     '''
     return Mat3(
         Vec3(self.x.dot(other.rx), self.x.dot(other.ry),
              self.x.dot(other.rz)),
         Vec3(self.y.dot(other.rx), self.y.dot(other.ry),
              self.y.dot(other.rz)),
         Vec3(self.z.dot(other.rx), self.z.dot(other.ry),
              self.z.dot(other.rz)))
Пример #9
0
def render_pixel_antialias(u,
                           v,
                           camera,
                           light,
                           asset,
                           file,
                           antialias_level=0):
    """
    :param u:
    :param v:
    :param camera:
    :param light:
    :param asset:
    :param file:
    :param antialias_level: level of antialiasing n
    shoot 2^2n rays per pixel and average the result
    :return:
    """

    a_n = 2**antialias_level
    ray_offset = [2 / a_n * i - (1 - 1 / a_n) for i in range(a_n)]
    # ray_offset = [-0.5, 0.5]
    # ray_offset =  [-0.75, -0.25, 0.25, 0.75]

    c_list = []

    for du in ray_offset:
        for dv in ray_offset:
            c = Vec3(0, 0, 0)
            ray = camera.shoot_ray(u + du, v + dv)
            intersect, i = asset.intersect(ray)
            if intersect:
                n = asset.normal(i)
                l = light.pos - i
                r = normalize(2 * n - l)

                l_intensity = light.strength(i) * light.color
                i_ambient = k_ambient * ambient
                i_diffuse = k_diffuse * max(dot(n, normalize(l)),
                                            0) * l_intensity
                i_specular = k_specular * pow(
                    max(dot(r, normalize(-1 * i)), 0),
                    n_specular) * l_intensity

                c = mul(asset.color, i_ambient + i_diffuse + i_specular)
            c_list.append(c)
    c = Vec3(0, 0, 0)
    for ci in c_list:
        c += ci
    c = c / len(ray_offset)**2
    file.write("{} {} {}\n".format(floatto8bit(c.x), floatto8bit(c.y),
                                   floatto8bit(c.z)))
Пример #10
0
 def __init__(self, origin, lookat, vup, vfov, aspectRatio, aperture,
              focusDist):
     self.origin = origin
     self.lensRadius = aperture / 2
     self.theta = math.radians(vfov)
     self.halfHeight = math.tan(self.theta / 2)
     self.halfWidth = aspectRatio * self.halfHeight
     self.w = Vec3.unitVector(lookat - origin)
     self.u = Vec3.unitVector(Vec3.cross(self.w, vup))
     self.v = Vec3.cross(self.u, self.w)
     self.lowerLeftCorner = origin - self.u * self.halfWidth * focusDist - self.v * self.halfHeight * focusDist + self.w * focusDist
     self.horizontal = self.u * self.halfWidth * focusDist * 2
     self.vertical = self.v * self.halfHeight * focusDist * 2
Пример #11
0
 def __init__(self,
              camera: Camera,
              scene: Scene,
              light_position: Vec3 = Vec3(5., 5., -10.)):
     self.camera = camera
     self.scene = scene
     self.light_position = light_position
Пример #12
0
    def scatter(self, ray, rec, wrapper):
        ''' 
        @return: bool
        '''
        self.wrapper = wrapper
        outward_normal = None
        reflected = self.reflect(ray.direction(), rec.normal)
        ni_over_nt = None
        self.wrapper.attenuation = Vec3(1.0, 1.0, 1.0)
        refracted = None

        if (ray.direction().dot(rec.normal) > 0):
            # 从密度小的介质到密度大的介质
            outward_normal = rec.normal.Scale(-1.0)
            ni_over_nt = self.ri
            cosine = ray.direction().dot(
                rec.normal) / (ray.direction().length() * rec.normal.length())
        else:
            # 从密度大的介质到密度小的介质
            outward_normal = rec.normal
            ni_over_nt = 1.0 / self.ri
            cosine = -ray.direction().dot(rec.normal) / (
                ray.direction().length() * rec.normal.length())

        if self.refract(ray.direction(), outward_normal, ni_over_nt,
                        self.wrapper):
            reflect_prob = self.schlick(cosine, self.ri)
        else:
            reflect_prob = 1.0

        if random.random() < reflect_prob:
            self.wrapper.scattered = Ray(rec.p, reflected)
        else:
            self.wrapper.scattered = Ray(rec.p, self.wrapper.refracted)
        return True
Пример #13
0
	def __init__(self, t, p, mat, r, outwardNormal):
		self.t = t
		self.p = p
		self.mat = mat
		
		self.frontFace = Vec3.dot(r.direction(), outwardNormal) < 0
		self.normal = outwardNormal if self.frontFace else outwardNormal*-1
Пример #14
0
 def test_normalize(self):
     a = Vec3(0.1, 0.2, 0.3)
     b = a.normalize()
     # result from here
     # https://calculator.academy/normalize-vector-calculator/#f1p1|f2p0
     self.assertAlmostEqual(b.x, 0.2672, 3)
     self.assertAlmostEqual(b.y, 0.5345, 3)
     self.assertAlmostEqual(b.z, 0.8017, 3)
Пример #15
0
def rayColor(r, world, depth):
	# if we've exceeded the ray bounce limit, no more light is gathered.
	if depth <= 0:
		return Vec3(0,0,0)

	rec = world.hit(r, 0.001, float('inf'))
	if rec != None:		
		result = rec.mat.scatter(r, rec)
		if result != None:
			attenuation = result[0]
			scattered = result[1]
			return rayColor(scattered, world, depth-1)*attenuation
		return Vec3(0,0,0)

	# background
	unitDirection = Vec3.unitVector(r.direction())
	t = (unitDirection.y() + 1.0)*0.5
	return Vec3(1.0, 1.0, 1.0)*(1.0-t) + Vec3(0.5, 0.7, 1.0)*t
Пример #16
0
    def __init__(self, look_from, look_at, vup, vfov, aspect):
        self.lower_left = Vec3(-2.0, -2.0, -2.0)
        self.horizontal = Vec3(4.0, 0.0, 0.0)  # width
        self.vertical = Vec3(0.0, 4.0, 0.0)  # height
        self.origin = Vec3(0.0, 0.0, 0.0)  # camara position

        # Camera cordinate
        self.u, self.v, self.w = None, None, None
        self.theta = float(vfov * math.pi / 180)  # fov theta
        self.half_height = float(math.tan(self.theta / 2))
        self.half_width = aspect * self.half_height
        self.origin = look_from
        self.w = look_from.Sub(look_at).normalize()
        self.u = vup.cross(self.w).normalize()
        self.v = self.w.cross(self.u).normalize()
        self.lower_left = self.origin.Sub(self.u.Scale(self.half_width)).Sub(
            self.v.Scale(self.half_height)).Sub(self.w)
        self.horizontal = self.u.Scale(2 * self.half_width)
        self.vertical = self.v.Scale(2 * self.half_height)
Пример #17
0
    def light(self,
              origin,
              direction,
              intersection,
              light_position,
              eye_position,
              scene_objects,
              bounce=0,
              far=1.0e15):
        '''
        Basic light model using a only diffuse lighting
        '''
        rayhit = origin + direction * intersection
        normal = ((rayhit - self.center) * (1. / self.radius))
        direction_to_light = (light_position - rayhit).norm()
        direction_to_eye = (eye_position - rayhit).norm()
        nudged = rayhit + normal * 0.001  # To avoid shadow acne

        # Create shadow mask
        light_distances = [
            o.intersect(nudged, direction_to_light, far=far)
            for o in scene_objects
        ]
        light_nearest = reduce(jnp.minimum, light_distances)
        light_mask = light_distances[scene_objects.index(
            self)] == light_nearest

        # Ambient light
        color = Vec3(0.05, 0.05, 0.05)

        # Lambert shading (diffuse)
        light_hit = jnp.maximum(normal.dot(direction_to_light), 0)
        color += self.diffusecolor(rayhit) * light_hit * light_mask

        # Phong light
        phong = normal.dot((direction_to_light + direction_to_eye).norm())
        color += Vec3(1., 1., 1.) * jnp.power(jnp.clip(phong, 0, 1),
                                              50) * light_mask

        return color
Пример #18
0
    def scatter(self, rIn, rec):  # returns (attenuation, scattered ray)
        attenuation = Vec3(1.0, 1.0, 1.0)  # Color
        # Check if the enter the material
        etaiOverEtat = (1.0 / self.refIdx) if rec.frontFace else self.refIdx

        unitDirection = Vec3.unitVector(rIn.direction())
        cosTheta = min(Vec3.dot(unitDirection * -1, rec.normal), 1.0)
        sinTheta = math.sqrt(1.0 - cosTheta * cosTheta)
        # TIR
        if etaiOverEtat * sinTheta > 1.0:
            reflected = Vec3.reflect(unitDirection, rec.normal)
            scattered = Ray(rec.p, reflected)
            return (attenuation, scattered)
        # Reflection / Refraction ratio
        reflectProb = Dielectric.__schlick(cosTheta, etaiOverEtat)
        # Reflection
        if random.random() < reflectProb:
            reflected = Vec3.reflect(unitDirection, rec.normal)
            scattered = Ray(rec.p, reflected)
            return (attenuation, scattered)

        # Refraction
        refracted = Vec3.refract(unitDirection, rec.normal, etaiOverEtat)
        scattered = Ray(rec.p, refracted)
        return (attenuation, scattered)
Пример #19
0
 def ray_trace(self, origin, normalized_direction):
     far = 1.0e15  # A large number, which we can never hit
     distances = [
         o.intersect(origin, normalized_direction, far)
         for o in self.scene.objects
     ]
     nearest = reduce(jnp.minimum, distances)
     color = Vec3(0, 0, 0)
     for (o, d) in zip(self.scene.objects, distances):
         color += o.light(
             origin, normalized_direction, d, self.light_position, origin,
             self.scene.objects) * (nearest != far) * (d == nearest)
     return color
Пример #20
0
def color(r, world, depth):
    '''
    @param {ray, hitable_list, recursion_depth} 
    @return: 
    '''
    rec = HitRecord()
    if world.hit(r, 0, float('inf'), rec):
        # ray reflect target
        wrapper = Wrapper()
        # target = rec.p.Add(rec.normal).Add(random_in_unit_sphere())

        # paint according to normal value
        # decay 50% every reflect
        if depth < 50 and rec.mat_ptr.scatter(r, rec, wrapper):
            return wrapper.attenuation.Mul(
                color(wrapper.scattered, world, depth + 1))
            #return color(Ray(rec.p, target.Sub(rec.p)), world, depth+1).Scale(0.5)
        else:
            return Vec3(0.0, 0.0, 0.0)
    else:
        unit_dir = r.direction().normalize()
        t = 0.5 * (unit_dir.y() + 1.0)
        return Vec3(1.0, 1.0,
                    1.0).Scale(1.0 - t).Add(Vec3(0.5, 0.7, 1.0).Scale(t))
Пример #21
0
def create_scene():
    obj_list = []
    obj_list.append(
        Sphere(Vec3(0.0, 0.0, -1.0), 0.5, Lambertian(Vec3(0.1, 0.2, 0.5))))
    obj_list.append(Sphere(Vec3(-1.0, 0.0, -1.0), 0.5, Deilectric(1.5)))
    obj_list.append(
        Sphere(Vec3(1.0, 0.0, -1.0), 0.5, Metal(Vec3(0.8, 0.6, 0.2), 0.2)))

    # Ground
    obj_list.append(
        Sphere(Vec3(0.0, -100.5, -1.0), 100.0, Lambertian(Vec3(0.5, 0.5,
                                                               0.5))))
    world = Hitable_list(obj_list)
    return world
Пример #22
0
def writePPM():
    width = 400
    height = 200
    ns = 100  # smaple nums

    lower_left = Vec3(-2.0, -2.0, -2.0)
    horizontal = Vec3(4.0, 0.0, 0.0)
    vertical = Vec3(0.0, 4.0, 0.0)
    origin = Vec3(0.0, 0.0, 0.0)

    with open('result.ppm', 'w') as f:
        f.write("P3\n" + str(width) + " " + str(height) + "\n255\n")
        index = 0
        world = create_scene()
        camera = Camera(Vec3(-2.0, 2.0, 1.0), Vec3(0.0, 0.0, -1.0),
                        Vec3(0.0, 1.0, 0.0), 40.0,
                        float(width) / float(height))
        with tqdm(total=height) as pbar:
            for j in range(height - 1, -1, -1):
                for i in range(0, width):
                    col = Vec3(0, 0, 0)
                    for s in range(0, ns):
                        u = float(i + random.random()) / float(
                            width)  # antialiasing
                        v = float(j + random.random()) / float(height)
                        ray = camera.GetRay(u, v)
                        col = col.Add(color(ray, world, 0))
                    col = col.Scale(1.0 / float(ns))  # average color
                    # gamma
                    col = Vec3(float(math.sqrt(col.x())),
                               float(math.sqrt(col.y())),
                               float(math.sqrt(col.z())))
                    index += 1
                    ir = int(255.59 * col.x())
                    ig = int(255.59 * col.y())
                    ib = int(255.59 * col.z())
                    f.write(str(ir) + " " + str(ig) + " " + str(ib) + "\n")
                pbar.update(1)
Пример #23
0
def render_pixel(u, v, camera, light, asset, file):
    c = Vec3(0, 0, 0)
    ray = camera.shoot_ray(u, v)
    intersect, i = asset.intersect(ray)
    if intersect:
        n = asset.normal(i)
        l = light.pos - i
        r = normalize(2 * n - l)

        l_intensity = light.strength(i) * light.color
        i_ambient = k_ambient * ambient
        i_diffuse = k_diffuse * max(dot(n, normalize(l)), 0) * l_intensity
        i_specular = k_specular * pow(max(dot(r, normalize(-1 * i)), 0),
                                      n_specular) * l_intensity

        c = mul(asset.color, i_ambient + i_diffuse + i_specular)
    file.write("{} {} {}\n".format(floatto8bit(c.x), floatto8bit(c.y),
                                   floatto8bit(c.z)))
Пример #24
0
    def hit(self, r, tMin, tMax):
        a = r.direction().lengthSquared()
        oc = r.origin() - self.center
        halfB = Vec3.dot(oc, r.direction())
        c = oc.lengthSquared() - self.radius * self.radius
        discriminant = halfB * halfB - a * c

        if discriminant > 0:
            root = sqrt(discriminant)

            t = (-halfB - root) / a
            if t < tMax and t > tMin:
                p = r.pointAtParameter(t)
                outwardNormal = (p - self.center) / self.radius
                return HitRecord(t, p, self.mat, r, outwardNormal)

            t = (-halfB + root) / a
            if t < tMax and t > tMin:
                p = r.pointAtParameter(t)
                outwardNormal = (p - self.center) / self.radius
                return HitRecord(t, p, self.mat, r, outwardNormal)

        return None
Пример #25
0
def main():

    res = 100  # rendered image resolution
    camera = Camera((res, res))
    sphere = Sphere(pos=Vec3(0, 2, 0), radius=0.5, color=Vec3(1, 0.2, 0.2))
    light = Light(pos=Vec3(1, 0.5, 1),
                  color=Vec3(1, 1, 0.5),
                  strength=1,
                  radius=1)

    triangle = Triangle([Vec3(0, 0, 1), Vec3(1, 0, 1), Vec3(0, 1, 1)])

    asset = sphere
    #asset = triangle

    out_file = open("out.ppm", "w")
    out_file.write("P3\n{} {} {}\n".format(camera.res_width, camera.res_height,
                                           255))

    for u in range(camera.res_width):
        for v in range(camera.res_height):
            render_pixel_antialias(u, v, camera, light, asset, out_file,
                                   antialias_level)
Пример #26
0
if __name__ == '__main__':
    cam = Camera(width=400, origin=jnp.array([0, 0.05, -1.]))
    num_spheres = 50
    scene_objects = []
    min_x = -7.5
    min_y = .5
    min_z = 2.5
    size_deviation = 0.5
    max_deviation_x = 15.
    max_deviation_y = 10.
    max_deviation_z = 20.
    import random
    colorful_factor = 0.35
    for i in range(1, num_spheres + 1):
        color = Vec3(random.random(), random.random(), random.random())
        color_to_add = random.randint(0, 2)
        if color_to_add == 0:
            color.x += colorful_factor
            color.y -= colorful_factor
            color.z -= colorful_factor
        elif color_to_add == 1:
            color.x -= colorful_factor
            color.y += colorful_factor
            color.x -= colorful_factor
        else:
            color.x -= colorful_factor
            color.y -= colorful_factor
            color.x += colorful_factor

        x_coord = random.random() * max_deviation_x + min_x
Пример #27
0
 def trans(self, v):
     '''
     in-place transform the vector.
     '''
     return Vec3(self.x.dot(v), self.y.dot(v), self.z.dot(v))
Пример #28
0
def randomSphereScene():
	world = HitableList()

	# Bottom "plane"
	world.add(Sphere(Vec3(0,-1000,0), 1000, Lambertian(Vec3(0.5, 0.5, 0.5))))

	# three large spheres
	world.add(Sphere(Vec3(0, 1, 0), 1.0, Dielectric(1.5)))
	world.add(Sphere(Vec3(-4, 1, 0), 1.0, Lambertian(Vec3(0.4, 0.2, 0.1))))
	world.add(Sphere(Vec3(4, 1, 0), 1.0, Metal(Vec3(0.7, 0.6, 0.5), 0.0)))

	# numerous small spheres
	i = 1
	for a in range(-11, 11):
		for b in range(-11, 11):
			chooseMat = random.random()
			center = Vec3(a + 0.9*random.random(), 0.2, b + 0.9*random.uniform(0,1))
			if (center - Vec3(4, 0.2, 0)).length() > 0.9:
				if chooseMat < 0.8:
					# diffuse
					albedo = Vec3.random() * Vec3.random()
					world.add(Sphere(center, 0.2, Lambertian(albedo)))
				elif chooseMat < 0.95:
					# metal
					albedo = random.uniform(.5, 1)
					fuzz = random.uniform(0, .5)
					world.add(Sphere(center, 0.2, Metal(albedo, fuzz)))
				else:
					# glass
					world.add(Sphere(center, 0.2, Dielectric(1.5)))

	return world
Пример #29
0
	def scatter(self, rIn, rec): # returns (attenuation, scattered)
			scatterDirection = rec.normal + Vec3.randomInUnitSphere()
			scattered = Ray(rec.p, scatterDirection)
			attenuation = self.albedo
			return (attenuation, scattered)
Пример #30
0
	# return the translated [0,255] value of each color component.
	return [int(256 * clamp(r, 0.0, 0.999)),
			int(256 * clamp(g, 0.0, 0.999)),
			int(256 * clamp(b, 0.0, 0.999))]


if __name__ == "__main__":
	aspectRatio = 16.0 / 9.0
	imageWidth = 500
	imageHeight = int(imageWidth / aspectRatio)
	samplesPerPixel = 1
	maxDepth = 10

	world = randomSphereScene()

	lookFrom = Vec3(13,2,3)
	lookAt = Vec3(0,0,0)
	vUp = Vec3(0,1,0)
	distToFocus = 10.0
	aperture = 0.1

	cam = Camera(lookFrom, lookAt, vUp, 20, aspectRatio, aperture, distToFocus)

	stopwatch = Stopwatch()
	image = imageHeight*imageWidth*[[0,0,0]]
	index = 0
	for j in range(imageHeight):
		showProgress(stopwatch, j, imageHeight)
		for i in range(imageWidth):
			pixelColor = Vec3(0,0,0)
			for s in range(samplesPerPixel):