def out_dir(indir, n, mat): u = ti.Vector([1.0, 0.0, 0.0]) if mat == mat_lambertian: if abs(n[1]) < 1 - eps: u = ti.normalized(ti.cross(n, ti.Vector([0.0, 1.0, 0.0]))) v = ti.cross(n, u) phi = 2 * math.pi * ti.random() ay = ti.sqrt(ti.random()) ax = ti.sqrt(1 - ay**2) u = ax * (ti.cos(phi) * u + ti.sin(phi) * v) + ay * n elif mat == mat_metal: u = reflect(indir, n) else: # glass cos = ti.dot(indir, n) ni_over_nt = refr_idx outn = n if cos > 0.0: outn = -n cos = refr_idx * cos else: ni_over_nt = 1.0 / refr_idx cos = -cos has_refr, refr_dir = refract(indir, outn, ni_over_nt) refl_prob = 1.0 if has_refr: refl_prob = schlick(cos, refr_idx) if ti.random() < refl_prob: u = reflect(indir, n) else: u = refr_dir return ti.normalized(u)
def next_hit(pos, d): closest, normal = inf, ti.Vector.zero(ti.f32, 3) c, mat = ti.Vector.zero(ti.f32, 3), mat_lambertian # right near sphere cur_dist, hit_pos = intersect_sphere(pos, d, sp1_center, sp1_radius) if 0 < cur_dist < closest: closest = cur_dist normal = ti.normalized(hit_pos - sp1_center) c, mat = ti.Vector([1.0, 1.0, 1.0]), mat_glass # left far sphere cur_dist, hit_pos = intersect_sphere(pos, d, sp2_center, sp2_radius) if 0 < cur_dist < closest: closest = cur_dist normal = ti.normalized(hit_pos - sp2_center) c, mat = ti.Vector([0.8, 0.5, 0.4]), mat_metal # left pnorm = ti.Vector([1.0, 0.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, d, ti.Vector([-1.0, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([1.0, 0.0, 0.0]), mat_lambertian # right pnorm = ti.Vector([-1.0, 0.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, d, ti.Vector([1.0, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([0.0, 1.0, 0.0]), mat_lambertian # bottom pnorm = ti.Vector([0.0, 1.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, d, ti.Vector([0.0, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([1.0, 1.0, 1.0]), mat_lambertian # top pnorm = ti.Vector([0.0, -1.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, d, ti.Vector([0.0, 2.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([1.0, 1.0, 1.0]), mat_lambertian # far pnorm = ti.Vector([0.0, 0.0, 1.0]) cur_dist, _ = ray_plane_intersect(pos, d, ti.Vector([0.0, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([1.0, 1.0, 1.0]), mat_lambertian return closest, normal, c, mat
def render(): for u, v in color_buffer: aspect_ratio = res[0] / res[1] pos = camera_pos d = ti.Vector([ (2 * fov * (u + ti.random()) / res[1] - fov * aspect_ratio - 1e-5), 2 * fov * (v + ti.random()) / res[1] - fov - 1e-5, -1.0 ]) d = ti.normalized(d) throughput = ti.Vector([1.0, 1.0, 1.0]) depth = 0 hit_light = 0.00 while depth < max_ray_depth: closest, normal, c = next_hit(pos, d) depth += 1 dist_to_light = intersect_light(pos, d) if dist_to_light < closest: hit_light = 1 depth = max_ray_depth else: hit_pos = pos + closest * d if normal.norm_sqr() != 0: d = out_dir(normal) pos = hit_pos + 1e-4 * d throughput *= c else: depth = max_ray_depth color_buffer[u, v] += throughput * hit_light
def sample_ray_dir(indir, normal, hit_pos, mat): u = ti.Vector([0.0, 0.0, 0.0]) pdf = 1.0 if mat == mat_lambertian: u = sample_brdf(normal) pdf = max(eps, compute_brdf_pdf(normal, u)) elif mat == mat_specular: u = reflect(indir, normal) elif mat == mat_glass: cos = indir.dot(normal) ni_over_nt = refr_idx outn = normal if cos > 0.0: outn = -normal cos = refr_idx * cos else: ni_over_nt = 1.0 / refr_idx cos = -cos has_refr, refr_dir = refract(indir, outn, ni_over_nt) refl_prob = 1.0 if has_refr: refl_prob = schlick(cos, refr_idx) if ti.random() < refl_prob: u = reflect(indir, normal) else: u = refr_dir return ti.normalized(u), pdf
def sdf_normal(p): d = 1e-3 n = ti.Vector([0.0, 0.0, 0.0]) sdf_center = sdf(p) for i in ti.static(range(3)): inc = p inc[i] += d n[i] = (1 / d) * (sdf(inc) - sdf_center) return ti.normalized(n)
def out_dir(n): u = ti.Vector([1.0, 0.0, 0.0]) if abs(n[1]) < 1 - eps: u = ti.normalized(ti.cross(n, ti.Vector([0.0, 1.0, 0.0]))) v = ti.cross(n, u) phi = 2 * math.pi * ti.random() ay = ti.sqrt(ti.random()) ax = ti.sqrt(1 - ay**2) return ax * (ti.cos(phi) * u + ti.sin(phi) * v) + ay * n
def voxelize_triangles(self, num_triangles: ti.i32, triangles: ti.ext_arr()): for i in range(num_triangles): jitter_scale = ti.cast(0, self.precision) if ti.static(self.precision is ti.f32): jitter_scale = 1e-4 else: jitter_scale = 1e-8 # We jitter the vertices to prevent voxel samples from lying precicely at triangle edges jitter = ti.Vector([-0.057616723909439505, -0.25608986292614977, 0.06716309129743714]) * jitter_scale a = ti.Vector([triangles[i, 0], triangles[i, 1], triangles[i, 2]]) + jitter b = ti.Vector([triangles[i, 3], triangles[i, 4], triangles[i, 5]]) + jitter c = ti.Vector([triangles[i, 6], triangles[i, 7], triangles[i, 8]]) + jitter bound_min = ti.Vector.zero(self.precision, 3) bound_max = ti.Vector.zero(self.precision, 3) for k in ti.static(range(3)): bound_min[k] = min(a[k], b[k], c[k]) bound_max[k] = max(a[k], b[k], c[k]) p_min = int(ti.floor(bound_min[0] * self.inv_dx)) p_max = int(ti.floor(bound_max[0] * self.inv_dx)) + 1 p_min = max(self.padding, p_min) p_max = min(self.res[0] - self.padding, p_max) q_min = int(ti.floor(bound_min[1] * self.inv_dx)) q_max = int(ti.floor(bound_max[1] * self.inv_dx)) + 1 q_min = max(self.padding, q_min) q_max = min(self.res[1] - self.padding, q_max) normal = ti.normalized(ti.cross(b - a, c - a)) if abs(normal[2]) < 1e-10: continue a_proj = ti.Vector([a[0], a[1]]) b_proj = ti.Vector([b[0], b[1]]) c_proj = ti.Vector([c[0], c[1]]) for p in range(p_min, p_max): for q in range(q_min, q_max): pos2d = ti.Vector([(p + 0.5) * self.dx, (q + 0.5) * self.dx]) if inside_ccw(pos2d, a_proj, b_proj, c_proj) or inside_ccw(pos2d, a_proj, c_proj, b_proj): base_voxel = ti.Vector([pos2d[0], pos2d[1], 0]) height = int( -ti.dot(normal, base_voxel - a) / normal[2] * self.inv_dx + 0.5) height = min(height, self.res[1] - self.padding) inc = 0 if normal[2] > 0: inc = 1 else: inc = -1 self.fill(p, q, height, inc)
def refract(d, n, ni_over_nt): # Assuming |d| and |n| are normalized has_r, rd = 0, d dt = ti.dot(d, n) discr = 1.0 - ni_over_nt * ni_over_nt * (1.0 - dt * dt) if discr > 0.0: has_r = 1 rd = ti.normalized(ni_over_nt * (d - n * dt) - n * ti.sqrt(discr)) else: rd *= 0.0 return has_r, rd
def render(): for u, v in color_buffer: aspect_ratio = res[0] / res[1] pos = camera_pos cur_iter = count_var[0] str_x, str_y = (cur_iter / stratify_res), (cur_iter % stratify_res) ray_dir = ti.Vector([ (2 * fov * (u + (str_x + ti.random()) * inv_stratify) / res[1] - fov * aspect_ratio - 1e-5), (2 * fov * (v + (str_y + ti.random()) * inv_stratify) / res[1] - fov - 1e-5), -1.0, ]) ray_dir = ti.normalized(ray_dir) acc_color = ti.Vector([0.0, 0.0, 0.0]) throughput = ti.Vector([1.0, 1.0, 1.0]) depth = 0 while depth < max_ray_depth: closest, hit_normal, hit_color, mat = intersect_scene(pos, ray_dir) if mat == mat_none: break hit_pos = pos + closest * ray_dir hit_light = (mat == mat_light) if hit_light: acc_color += throughput * light_color break elif mat == mat_lambertian: acc_color += throughput * sample_direct_light( hit_pos, hit_normal, hit_color) depth += 1 ray_dir, pdf = sample_ray_dir(ray_dir, hit_normal, hit_pos, mat) pos = hit_pos + 1e-4 * ray_dir if mat == mat_lambertian: throughput *= lambertian_brdf * hit_color * dot_or_zero( hit_normal, ray_dir) / pdf else: throughput *= hit_color color_buffer[u, v] += acc_color count_var[0] = (count_var[0] + 1) % (stratify_res * stratify_res)
def grid_op(): for i, j in grid_m: if grid_m[i, j] > 0: inv_m = 1 / grid_m[i, j] v_out = inv_m * grid_v_in[i, j] # momentum to velocity v_out[1] -= dt * gravity # gravity # center sticky circle dist = ti.Vector([i * dx - 0.5, j * dx - 0.5]) if dist.norm_sqr() < 0.005: dist = ti.normalized(dist) v_out -= dist * ti.dot(v_out, dist) # boundary conditions if i < bound and v_out[0] < 0: v_out[0] = 0 if i > n_grid - bound and v_out[0] > 0: v_out[0] = 0 if j < bound and v_out[1] < 0: v_out[1] = 0 if j > n_grid - bound and v_out[1] > 0: v_out[1] = 0 grid_v_out[i, j] = v_out
def sample_area_light(hit_pos, pos_normal): # sampling inside the light area x = ti.random() * light_x_range + light_x_min_pos z = ti.random() * light_z_range + light_z_min_pos on_light_pos = ti.Vector([x, light_y_pos, z]) return ti.normalized(on_light_pos - hit_pos)
def intersect_scene(pos, ray_dir): closest, normal = inf, ti.Vector.zero(ti.f32, 3) c, mat = ti.Vector.zero(ti.f32, 3), mat_none # right near sphere cur_dist, hit_pos = intersect_sphere(pos, ray_dir, sp1_center, sp1_radius) if 0 < cur_dist < closest: closest = cur_dist normal = ti.normalized(hit_pos - sp1_center) c, mat = ti.Vector([1.0, 1.0, 1.0]), mat_glass # left box hit, cur_dist, pnorm = ray_aabb_intersection2_transformed( box_min, box_max, pos, ray_dir) if hit and 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([0.8, 0.5, 0.4]), mat_specular # left pnorm = ti.Vector([1.0, 0.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, ray_dir, ti.Vector([-1.1, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([0.65, 0.05, 0.05]), mat_lambertian # right pnorm = ti.Vector([-1.0, 0.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, ray_dir, ti.Vector([1.1, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = ti.Vector([0.12, 0.45, 0.15]), mat_lambertian # bottom gray = ti.Vector([0.93, 0.93, 0.93]) pnorm = ti.Vector([0.0, 1.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, ray_dir, ti.Vector([0.0, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = gray, mat_lambertian # top pnorm = ti.Vector([0.0, -1.0, 0.0]) cur_dist, _ = ray_plane_intersect(pos, ray_dir, ti.Vector([0.0, 2.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = gray, mat_lambertian # far pnorm = ti.Vector([0.0, 0.0, 1.0]) cur_dist, _ = ray_plane_intersect(pos, ray_dir, ti.Vector([0.0, 0.0, 0.0]), pnorm) if 0 < cur_dist < closest: closest = cur_dist normal = pnorm c, mat = gray, mat_lambertian # light hit_l, cur_dist = intersect_light(pos, ray_dir, closest) if hit_l and 0 < cur_dist < closest: # technically speaking, no need to check the second term closest = cur_dist normal = light_normal c, mat = light_color, mat_light return closest, normal, c, mat
def paint(t: ti.f32): fin = ti.Vector([0.0, 0.0, 0.0]) # Parallized over all pixels intensity = 0.0 for i in range(n * 16): #this is parallilized for j in range(n * 9): for x in range(3): uv = ti.Vector([((i / (16 * n)) - 0.5) * (2), (j / (9 * n)) - 0.5]) starting_y = 17.0 ending_y = 5.0 motion_y = -t lookat_starting_y = 17.0 lookat_ending_y = 5.0 # motion_y = 0 ro = ti.Vector([5.0, starting_y, 1.0]) lookat = ti.Vector([5.0, lookat_starting_y, 6.0]) if starting_y + motion_y > ending_y: ro = ti.Vector([5.0, starting_y + motion_y, 1.0]) lookat = ti.Vector( [5.0, lookat_starting_y + motion_y, 6.0]) else: ro = ti.Vector([5.0, ending_y, 1.0]) lookat = ti.Vector([5.0, lookat_ending_y, 6.0]) zoom = 1.0 forward = ti.normalized(lookat - ro) right = ti.cross(ti.Vector([0.0, 1.0, 0.0]), forward) up = ti.cross(forward, right) center = ro + forward * zoom intersection = center + uv[0] * right + uv[1] * up rd = ti.normalized(intersection - ro) d, no, intersection_object, clouddO, cloud_intersection, clouddO2, cloud_intersection2, clouddO3, cloud_intersection3, sdf, sdf_inter = rayCast( ro, rd, t, frameTimeBlur * x) p = ro + rd * d light, normal = GetLight(p, t, intersection_object, no, frameTimeBlur * x, rd) if x == 0: sdf_p = ro + rd * sdf sdf_light, normal_sdf = GetLight(sdf_p, t, sdf_inter, no, frameTimeBlur * x, rd) # if (intersection_object == CAPSULE): # rd2 = reflect(rd, normal) # d2, no2, intersection_object2 = rayCast_reflection(ro + normal*.003, rd2, t+(0.03*0), 0.03*0) # p += rd2*d2 # light2, normal2 = GetLight(p, t, intersection_object2, no2, frameTimeBlur*x, rd2) # sdf_light += light2*0.30 pixels[i, j] = ti.Vector( [sdf_light[0], sdf_light[1], sdf_light[2], 1.0]) #color alpha = 0.8 alpha1 = 0.4 alpha2 = 0.2 if intersection_object == PARTICLES: if x == 0: #doing pixel = pixels + ... to add the particle color value on top of the background pixels[i, j] = pixels[i, j] * alpha + ti.Vector([ light[0], light[1], light[2], 1.0 / (1.0 - alpha) ]) * (1.0 - alpha) if x == 1: pixels[i, j] = pixels[i, j] * alpha1 + ti.Vector([ light[0], light[1], light[2], 1.0 / (1.0 - alpha1) ]) * (1.0 - alpha1) if x == 2: pixels[i, j] = pixels[i, j] * alpha2 + ti.Vector([ light[0], light[1], light[2], 1.0 / (1.0 - alpha2) ]) * (1.0 - alpha2) if x == 2: if cloud_intersection == 1: p_cloud = ro + rd * clouddO light_cloud, normal_cloud = GetLight( p_cloud, t, CLOUD, no, 0, rd) pixels[i, j] = pixels[i, j] + ti.Vector([ light_cloud[0] * 0.30, light_cloud[1] * 0.30, light_cloud[2] * 0.30, 1.0 ]) if cloud_intersection3 == 1: p_cloud3 = ro + rd * clouddO3 light_cloud3, normal_cloud3 = GetLight( p_cloud3, t, CLOUD3, no, 0, rd) pixels[i, j] = pixels[i, j] + ti.Vector([ light_cloud3[0] * 0.30, light_cloud3[1] * 0.30, light_cloud3[2] * 0.30, 1.0 ]) if cloud_intersection2 == 1: p_cloud2 = ro + rd * clouddO2 light_cloud2, normal_cloud2 = GetLight( p_cloud2, t, CLOUD2, no, 0, rd) pixels[i, j] = pixels[i, j] + ti.Vector([ light_cloud2[0] * 0.30, light_cloud2[1] * 0.30, light_cloud2[2] * 0.30, 1.0 ])