def render_custom(ro, rd, pr_mat): # ro: vec3, rd: vec3, t: ti.f32 """ Определение цвета каждого пикселя :param ro: где расположена камера :param rd: направление в сторону каждого пикселя на экране (он идёт дальше, пока не пересечёт какой-то объект) :param pr_mat: предыдущий материал :return: цвет пикселя, точка падения луча на объект, отражённый луч, материал """ # d, mat_i, p = raymarch(ro, rd) d, mat_i, p = raymarch_outline(ro, rd, 0.05) n = normal(p, rd) r = reflect(rd, n) occ = ambient_occlusion(p, n) col = vec3(0.) lp = vec3(5., 5., -5.) # lamp position ld = normalize(lp - p) mat_n = materials.shape[0] background = materials[mat_n - 1] - abs(rd.y) mate = materials[mat_i] coeff_of_reflect = 1. if pr_mat == 2 and mat_i != 2 or pr_mat == 2 and mat_i == 2: col = vec3(0., 0., 0.) elif pr_mat == 4 and mat_i != 4 or pr_mat == 4 and mat_i == 4: col = vec3(0., 0., 0.) elif pr_mat == 5 and mat_i != 5 or pr_mat == 5 and mat_i == 5: col = vec3(0., 0., 0.) else: if mat_i >= mat_n - 2: col = mate # background, outline else: diff, spec = phong(n, rd, ld, 16) if mat_i == 0: mate = boxmap_texture(translate_cube(rotate_cube(p)), rotate_cube(n), 60, 32.) diff = ti.ceil(diff * 2.) / 2. if mat_i == 2: diff = ti.ceil(diff * 2.) / 2. pass shad = sharp_shadow(p + n * EPS, ld) if flag[None] != 0.: shad = soft_shadow(p + n * EPS, ld) k_a = 0.3 k_d = 1.0 k_s = 1.5 # 1.5 amb = mate dif = diff * mate spe = spec * vec3(1.) col = coeff_of_reflect * (k_a * amb * occ + (k_d * dif + k_s * spe * occ) * shad) #fog col = mix(col, background, smoothstep(20., 50., d)) ro = p + n * 0.1 rd = r return col, ro, rd, mat_i
def render_line(model, camera, face, w0=0, w1=1): posa, posb = face.pos # Position texa, texb = face.tex # TexCoord nrma, nrmb = face.nrm # Normal clra = [posa, texa, nrma] clrb = [posb, texb, nrmb] A = camera.uncook(posa) B = camera.uncook(posb) M = int(ti.floor(min(A, B) - 1)) N = int(ti.ceil(max(A, B) + 1)) M = ts.clamp(M, 0, ti.Vector(camera.fb.res)) N = ts.clamp(N, 0, ti.Vector(camera.fb.res)) B_A = (B - A).normalized() for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): udf = abs((X - A).cross(B_A)) if udf >= w1: continue strength = ts.smoothstep(udf, w1, w0) color = ts.vec3(strength) camera.img[X] += color
def render_particle(model, camera, index): scene = model.scene a = (model.L2C[None] @ ts.vec4(model.pos[index], 1)).xyz r = model.radius[index] A = camera.uncook(a) rad = camera.uncook(ts.vec3(r, r, a.z), False) M = int(ti.floor(A - rad)) N = int(ti.ceil(A + rad)) M = ts.clamp(M, 0, ti.Vector(camera.res)) N = ts.clamp(N, 0, ti.Vector(camera.res)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): pos = camera.cook(float(ts.vec3(X, a.z))) dp = pos - a dp2 = dp.norm_sqr() if dp2 > r**2: continue dz = ti.sqrt(r**2 - dp2) if camera.fb.atomic_depth(X, a.z - dz): continue n = ts.vec3(dp.xy, -dz) normal = ts.normalize(n) view = ts.normalize(a + n) color = model.colorize(pos, normal) camera.fb['img'][X] = color camera.fb['normal'][X] = normal
def paintArrow(img: ti.template(), orig, dir, color=1, width=3, max_size=12, min_scale=0.4): res = ts.vec(*img.shape) I = orig * res D = dir * res J = I + D DL = ts.length(D) S = min(max_size, DL * min_scale) DS = D / (DL + 1e-4) * S SW = S + width D1 = ti.Matrix.rotation2d(+math.pi * 3 / 4) @ DS D2 = ti.Matrix.rotation2d(-math.pi * 3 / 4) @ DS bmin, bmax = ti.floor(max(0, min(I, J) - SW)), ti.ceil( min(res - 1, max(I, J) + SW)) for P in ti.grouped(ti.ndrange((bmin.x, bmax.x), (bmin.y, bmax.y))): c0 = ts.smoothstep(abs(sdLine(I, J, P)), width, width / 2) c1 = ts.smoothstep(abs(sdLine(J, J + D1, P)), width, width / 2) c2 = ts.smoothstep(abs(sdLine(J, J + D2, P)), width, width / 2) ti.atomic_max(img[P], max(c0, c1, c2) * color)
def render_cylinder(model, camera, v1, v2, radius, c1, c2): scene = model.scene a = camera.untrans_pos(v1) b = camera.untrans_pos(v2) A = camera.uncook(a) B = camera.uncook(b) bx = int(ti.ceil(camera.fx * radius / min(a.z, b.z))) by = int(ti.ceil(camera.fy * radius / min(a.z, b.z))) M, N = ti.floor(min(A, B)), ti.ceil(max(A, B)) M.x -= bx N.x += bx M.y -= by N.y += by M.x, N.x = min(max(M.x, 0), camera.img.shape[0]), min(max(N.x, 0), camera.img.shape[1]) M.y, N.y = min(max(M.y, 0), camera.img.shape[0]), min(max(N.y, 0), camera.img.shape[1]) if (M.x < N.x and M.y < N.y): for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): t = ti.dot(X - A, B - A) / (B - A).norm_sqr() if t < 0 or t > 1: continue proj = a * (1 - t) + b * t W = ti.cast(ti.Vector([X.x, X.y, proj.z]), ti.f32) w = cook_coord(camera, W) dw = w - proj dw2 = dw.norm_sqr() if dw2 > radius**2: continue dz = ti.sqrt(radius**2 - dw2) n = ti.Vector([dw.x, dw.y, -dz]) zindex = 1 / (proj.z - dz) if zindex >= ti.atomic_max(camera.zbuf[X], zindex): basecolor = c1 if t < 0.5 else c2 normal = ts.normalize(n) view = ts.normalize(a + n) color = get_ambient(camera, normal, view) * basecolor for light in ti.static(scene.lights): light_color = scene.opt.render_func(a + n, normal, \ view, light, basecolor) color += light_color camera.img[X] = color camera.nbuf[X] = normal
def draw_box(cx: ti.f32, cy: ti.f32, theta: ti.f32, w: ti.f32, h: ti.f32, r: ti.f32): costheta = abs(ti.cos(theta)) sintheta = abs(ti.sin(theta)) tw = w * 0.5 th = h * 0.5 x0 = max(int(ti.floor(cx - tw * costheta - th * sintheta)) - 1, 0) x1 = min( int(ti.ceil(cx + tw * costheta + th * sintheta)) + 1, WINDOW_WIDTH) y0 = max(int(ti.floor(cy - tw * sintheta - th * costheta)) - 1, 0) y1 = min( int(ti.ceil(cy + tw * sintheta + th * costheta)) + 1, WINDOW_HEIGHT) w -= r * 2.0 h -= r * 2.0 for i, j in ti.ndrange((y0, y1), (x0, x1)): alpha_blend( j, i, max(min(0.5 - boxSDF(j, i, cx, cy, theta, w, h) + r, 1.0), 0.0), 0.8)
def make_tests(): x = 1.5 v = ti.math.vec3(1.1, 2.2, 3.3) assert ti.floor(x) == 1 assert ti.floor(x, dt) == 1.0 assert ti.floor(x, int) == 1 assert all(ti.floor(v) == [1, 2, 3]) assert all(ti.floor(v, dt) == [1.0, 2.0, 3.0]) assert all(ti.floor(v, int) == [1, 2, 3]) assert ti.ceil(x) == 2 assert ti.ceil(x, dt) == 2.0 assert ti.ceil(x, int) == 2 assert all(ti.ceil(v) == [2, 3, 4]) assert all(ti.ceil(v, dt) == [2.0, 3.0, 4.0]) assert all(ti.ceil(v, int) == [2, 3, 4]) assert ti.round(x) == 2 assert ti.round(x, dt) == 2.0 assert ti.round(x, int) == 2 assert all(ti.round(v) == [1, 2, 3]) assert all(ti.round(v, dt) == [1.0, 2.0, 3.0]) assert all(ti.round(v, int) == [1, 2, 3])
def render_particle(model, camera, vertex, radius): scene = model.scene L2W = model.L2W a = camera.untrans_pos(L2W @ vertex) A = camera.uncook(a) M = int(ti.floor(A - radius)) N = int(ti.ceil(A + radius)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): if X.x < 0 or X.x >= camera.res[0] or X.y < 0 or X.y >= camera.res[1]: continue if (X - A).norm_sqr() > radius**2: continue camera.img[X] = ts.vec3(1)
def func(): xi[0] = -yi[None] xi[1] = ~yi[None] xi[2] = ti.logical_not(yi[None]) xi[3] = ti.abs(yi[None]) xf[0] = -yf[None] xf[1] = ti.abs(yf[None]) xf[2] = ti.sqrt(yf[None]) xf[3] = ti.sin(yf[None]) xf[4] = ti.cos(yf[None]) xf[5] = ti.tan(yf[None]) xf[6] = ti.asin(yf[None]) xf[7] = ti.acos(yf[None]) xf[8] = ti.tanh(yf[None]) xf[9] = ti.floor(yf[None]) xf[10] = ti.ceil(yf[None]) xf[11] = ti.exp(yf[None]) xf[12] = ti.log(yf[None])
def raster(self): # 8x8 rough rasterization for q in ti.grouped(self.tml): self.tml[q] = 0 self.tmd[q] = 1e6 # TODO: use brensel instead of barycent for raster for e in self.tri: aa, bb, cc = self.getverts(self.tri[e]) a, b, c = V(aa.x, aa.y), V(bb.x, bb.y), V(cc.x, cc.y) xy2uvw = self.makexy2uvw(a, b, c) # xy-to-distance matrix pmin = max(0, int(ti.floor(min(a, b, c) / self.S))) pmax = min(self.N // self.S, int(ti.ceil(max(a, b, c) / self.S) + 1)) for q in ti.grouped(ti.ndrange(*zip(pmin, pmax))): p = q * self.S + self.S // 2 uvw = xy2uvw @ V(p.x, p.y, 1) if all(uvw > -self.S) or all(uvw < self.S): l = ti.atomic_add(self.tml[q], 1) self.tmp[l, q] = e # append an element
def do_render(self, scene): W = 1 A = scene.uncook_coor(scene.camera.untrans_pos(self.a)) B = scene.uncook_coor(scene.camera.untrans_pos(self.b)) M, N = int(ti.floor(min(A, B) - W)), int(ti.ceil(max(A, B) + W)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): P = B - A udf = (ts.cross(X, P) + ts.cross(B, A))**2 / P.norm_sqr() XoP = ts.dot(X, P) AoB = ts.dot(A, B) if XoP > B.norm_sqr() - AoB: udf = (B - X).norm_sqr() elif XoP < AoB - A.norm_sqr(): udf = (A - X).norm_sqr() if udf < 0: scene.img[X] = ts.vec3(1.0) elif udf < W**2: t = ts.smoothstep(udf, 0, W**2) ti.atomic_min(scene.img[X], ts.vec3(t))
def do_render(self): model = self.model scene = model.scene L2W = model.L2W a = scene.camera.untrans_pos(L2W @ self.vertex(0).pos) b = scene.camera.untrans_pos(L2W @ self.vertex(1).pos) c = scene.camera.untrans_pos(L2W @ self.vertex(2).pos) A = scene.uncook_coor(a) B = scene.uncook_coor(b) C = scene.uncook_coor(c) B_A = B - A C_B = C - B A_C = A - C ilB_A = 1 / ts.length(B_A) ilC_B = 1 / ts.length(C_B) ilA_C = 1 / ts.length(A_C) B_A *= ilB_A C_B *= ilC_B A_C *= ilA_C BxA = ts.cross(B, A) * ilB_A CxB = ts.cross(C, B) * ilC_B AxC = ts.cross(A, C) * ilA_C normal = ts.normalize(ts.cross(a - c, a - b)) light_dir = scene.camera.untrans_dir(scene.light_dir[None]) pos = (a + b + c) / 3 color = scene.opt.render_func(pos, normal, ts.vec3(0.0), light_dir) color = scene.opt.pre_process(color) Ak = 1 / (ts.cross(A, C_B) + CxB) Bk = 1 / (ts.cross(B, A_C) + AxC) Ck = 1 / (ts.cross(C, B_A) + BxA) W = 1 M, N = int(ti.floor(min(A, B, C) - W)), int(ti.ceil(max(A, B, C) + W)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): AB = ts.cross(X, B_A) + BxA BC = ts.cross(X, C_B) + CxB CA = ts.cross(X, A_C) + AxC if AB <= 0 and BC <= 0 and CA <= 0: zindex = pos.z #(Ak * a.z * BC + Bk * b.z * CA + Ck * c.z * AB) zstep = zindex - ti.atomic_max(scene.zbuf[X], zindex) if zstep >= 0: scene.img[X] = color
def initialize_particle_grid(self): for p in range(self.num_particles[None]): v = ti.Vector([0.0, 0.0, 0.0]) if ti.static(self.enable_motion_blur): v = self.particle_v[p] x = self.particle_x[p] ipos = ti.floor(x * self.inv_dx).cast(ti.i32) offset_begin = shutter_begin * self.shutter_time * v offset_end = (shutter_begin + 1.0) * self.shutter_time * v offset_begin_grid = offset_begin offset_end_grid = offset_end for k in ti.static(range(3)): if offset_end_grid[k] < offset_begin_grid[k]: t = offset_end_grid[k] offset_end_grid[k] = offset_begin_grid[k] offset_begin_grid[k] = t offset_begin_grid = int(ti.floor( offset_begin_grid * self.inv_dx)) - 1 offset_end_grid = int(ti.ceil(offset_end_grid * self.inv_dx)) + 2 for i in range(offset_begin_grid[0], offset_end_grid[0]): for j in range(offset_begin_grid[1], offset_end_grid[1]): for k in range(offset_begin_grid[2], offset_end_grid[2]): offset = ti.Vector([i, j, k]) box_ipos = ipos + offset if self.inside_particle_grid(box_ipos): box_min = box_ipos * self.dx box_max = (box_ipos + ti.Vector([1, 1, 1])) * self.dx if sphere_aabb_intersect_motion( box_min, box_max, x + offset_begin, x + offset_end, self.sphere_radius): self.voxel_has_particle[box_ipos] = 1 self.voxel_grid_density[box_ipos] = 1 ti.append( self.pid.parent(), box_ipos - ti.Vector(self.particle_grid_offset), p)
def do_render(self, scene): a = scene.camera.untrans_pos(self.a) b = scene.camera.untrans_pos(self.b) c = scene.camera.untrans_pos(self.c) A = scene.uncook_coor(a) B = scene.uncook_coor(b) C = scene.uncook_coor(c) B_A = B - A C_B = C - B A_C = A - C ilB_A = 1 / ts.length(B_A) ilC_B = 1 / ts.length(C_B) ilA_C = 1 / ts.length(A_C) B_A *= ilB_A C_B *= ilC_B A_C *= ilA_C BxA = ts.cross(B, A) * ilB_A CxB = ts.cross(C, B) * ilC_B AxC = ts.cross(A, C) * ilA_C normal = ts.normalize(ts.cross(a - c, a - b)) light_dir = scene.camera.untrans_dir(scene.light_dir[None]) pos = (a + b + c) * (1 / 3) dir = ts.vec3(0.0) color = scene.opt.render_func(pos, normal, dir, light_dir) color = scene.opt.pre_process(color) W = 0.4 M, N = int(ti.floor(min(A, B, C) - W)), int(ti.ceil(max(A, B, C) + W)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): AB = ts.cross(X, B_A) + BxA BC = ts.cross(X, C_B) + CxB CA = ts.cross(X, A_C) + AxC udf = max(AB, BC, CA) if udf < 0: scene.img[X] = color elif udf < W: t = ts.smoothstep(udf, W, 0) ti.atomic_max(scene.img[X], t * color)
def render_particle(model, camera, index): scene = model.scene L2W = model.L2W a = model.pos[index] r = model.radius[index] a = camera.untrans_pos(L2W @ a) A = camera.uncook(a) rad = camera.uncook(ts.vec3(r, r, a.z), False) M = int(ti.floor(A - rad)) N = int(ti.ceil(A + rad)) M = ts.clamp(M, 0, ti.Vector(camera.res)) N = ts.clamp(N, 0, ti.Vector(camera.res)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): pos = camera.cook(float(ts.vec3(X, a.z))) dp = pos - a dp2 = dp.norm_sqr() if dp2 > r**2: continue dz = ti.sqrt(r**2 - dp2) zindex = 1 / (a.z - dz) if zindex < ti.atomic_max(camera.fb['idepth'][X], zindex): continue n = ts.vec3(dp.xy, -dz) normal = ts.normalize(n) view = ts.normalize(a + n) color = ts.vec3(1.0) color = model.colorize(pos, normal, color) camera.fb['img'][X] = color camera.fb['normal'][X] = normal
def gradientNoise(t:ti.template)->ti.f32: fraction = t-int(t) interpolator = easeInOut(fraction) #return fraction.dot(fraction) #return fraction[0] ft = int(t) ct = ti.cast(ti.ceil(t), ti.i32) lowerleftd = rand1dT1d(ti.Vector([ft[0], ft[1]]))*2-1 #return (lowerleftd.dot(ti.Vector([1,1])))/2 #return lowerleftd[1] lowerrightd = rand1dT1d(ti.Vector([ct[0], ft[1]]))*2-1 upleftd = rand1dT1d(ti.Vector([ft[0], ct[1]]))*2-1 uprightd = rand1dT1d(ti.Vector([ct[0], ct[1]]))*2-1 lowleftv = lowerleftd.dot(fraction-ti.Vector([0, 0])) lowrightv = lowerrightd.dot(fraction-ti.Vector([1, 0])) #-return ti.abs(lowleftv) upleftv = upleftd.dot(fraction-ti.Vector([0, 1])) uprightv= uprightd.dot(fraction-ti.Vector([1, 1])) low = lerp(lowleftv, lowrightv, interpolator[0]) #return low up = lerp(upleftv, uprightv, interpolator[0]) #return low noise = lerp(low, up, interpolator[1]) return noise
def round(val): # 向上取整 _round_val = ti.ceil(val) if (_round_val - val) > 0.5: _round_val -= 1.0 return _round_val
def foo(): x[None] = -y[None] x[None] = ti.floor(x[None]) y[None] = ti.ceil(y[None])
def iceil(x): return int(ti.ceil(x))
def frac(x): fractional = x - ti.floor(x) if x > 0. else x - ti.ceil(x) return fractional
def __init__(self, x_list, flag_list, gui, dim=2, **kwargs): # basic render settings self.dim = dim assert self.dim == 1 or self.dim == 2 or self.dim == 3 self.dim_size = ti.Vector([1, 1.02]) self.dt = 5e-4 self.gui = gui # basic solver settings self.r = 5e-3 # particle spacing self.h = self.r * 2.0 self.nbrs_num_max = 500 self.grid_pnum_max = 500 self.theta_c = 0.025 self.theta_s = 0.0075 self.density = 400 self.snow_m = self.density * self.h**3 self.mu_b = 1.0 self.psi = 1.5 self.E = 140 self.mu = 0.2 self.omega = 0.5 self.epsilon = 10.0 self.error_rate = 1e-3 if 'theta_c' in kwargs: self.theta_c = kwargs['theta_c'] if 'theta_s' in kwargs: self.theta_s = kwargs['theta_s'] if 'density' in kwargs: self.density = kwargs['density'] # inferenced settings self.p_num = len(x_list) self.grid_size = ti.ceil(self.dim_size / (2 * self.h)) + 1 self.kernel_sig = 0. if self.dim == 1: self.kernel_sig = 2. / 3. elif self.dim == 2: self.kernel_sig = 10. / (7 * np.pi) elif self.dim == 3: self.kernel_sig = 1 / np.pi self.kernel_sig /= self.h**self.dim # particle attributes self.x = ti.Vector(self.dim, ti.f32) # positions self.v = ti.Vector(self.dim, ti.f32) # velocity self.v_tmp = ti.Vector(self.dim, ti.f32) # velocity_star self.rho = ti.var(ti.f32) # density self.rho_0 = ti.var(ti.f32) # rest density of the current time self.rho_tmp = ti.var(ti.f32) # density_star self.a_other = ti.Vector(self.dim, ti.f32) # acceleration_other self.a_friction = ti.Vector(self.dim, ti.f32) # acceleration_friction self.a_lambda = ti.Vector(self.dim, ti.f32) # acceleration_lambda self.a_G = ti.Vector(self.dim, ti.f32) # acceleration_G self.pressure = ti.var(ti.f32) self.flag = ti.var(ti.f32) self.F_E = ti.Matrix(self.dim, self.dim, ti.f32) self.L = ti.Matrix(self.dim, self.dim, ti.f32) self.lbda = ti.var(ti.f32) self.G = ti.var(ti.f32) self.diag = ti.var(ti.f32) # a_ii for jacobi solver self.vec_tmp = ti.Vector(self.dim, ti.f32) self.var_tmp = ti.var(ti.f32) self.lhs = ti.Vector(self.dim, ti.f32) self.rhs = ti.Vector(self.dim, ti.f32) self.mat_tmp = ti.Matrix(self.dim, self.dim, ti.f32) self.p_solve = ti.Vector(self.dim, ti.f32) self.v_solve = ti.Vector(self.dim, ti.f32) self.res_solve = ti.Vector(self.dim, ti.f32) self.s_solve = ti.Vector(self.dim, ti.f32) self.t_solve = ti.Vector(self.dim, ti.f32) ti.root.dense(ti.i, self.p_num).place( self.x, self.flag, self.v, self.v_tmp, self.rho, self.rho_0, self.rho_tmp, self.a_other, self.a_friction, self.a_lambda, self.a_G, self.pressure, self.F_E, self.L, self.lbda, self.G, self.diag, self.vec_tmp, self.var_tmp, self.lhs, self.rhs, self.mat_tmp, self.p_solve, self.v_solve, self.res_solve, self.s_solve, self.t_solve) self.nbrs_num = ti.var(ti.i32) self.nbrs_list = ti.var(ti.i32) nbrs_nodes = ti.root.dense(ti.i, self.p_num) nbrs_nodes.place(self.nbrs_num) nbrs_nodes.dense(ti.j, self.nbrs_num_max).place(self.nbrs_list) # grid attributes self.grid_p_num = ti.var(ti.i32) self.grids = ti.var(ti.i32) grid_nodes = ti.root.dense(ti.ij, (self.grid_size[0], self.grid_size[1])) grid_nodes.place(self.grid_p_num) grid_nodes.dense(ti.k, self.grid_pnum_max).place(self.grids) # data initialize self.rho.fill(self.density) self.parallel_init(np.array(x_list), np.array(flag_list))
def render_triangle(model, camera, face): scene = model.scene L2W = model.L2W posa, posb, posc = model.pos[face[0, 0]], model.pos[face[1, 0]], model.pos[ face[2, 0]] texa, texb, texc = model.tex[face[0, 1]], model.tex[face[1, 1]], model.tex[ face[2, 1]] nrma, nrmb, nrmc = model.nrm[face[0, 2]], model.nrm[face[1, 2]], model.nrm[ face[2, 2]] posa = camera.untrans_pos(L2W @ posa) posb = camera.untrans_pos(L2W @ posb) posc = camera.untrans_pos(L2W @ posc) nrma = camera.untrans_dir(L2W.matrix @ nrma) nrmb = camera.untrans_dir(L2W.matrix @ nrmb) nrmc = camera.untrans_dir(L2W.matrix @ nrmc) pos_center = (posa + posb + posc) / 3 if ti.static(camera.type == camera.ORTHO): pos_center = ts.vec3(0.0, 0.0, 1.0) dpab = posa - posb dpac = posa - posc dtab = texa - texb dtac = texa - texc normal = ts.cross(dpab, dpac) tan, bitan = compute_tangent(-dpab, -dpac, -dtab, -dtac) # NOTE: the normal computation indicates that a front-facing face should # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW); # this is to be compatible with obj model loading. if ts.dot(pos_center, normal) <= 0: clra = model.vertex_shader(posa, texa, nrma, tan, bitan) clrb = model.vertex_shader(posb, texb, nrmb, tan, bitan) clrc = model.vertex_shader(posc, texc, nrmc, tan, bitan) A = camera.uncook(posa) B = camera.uncook(posb) C = camera.uncook(posc) scr_norm = 1 / ts.cross(A - C, B - A) B_A = (B - A) * scr_norm C_B = (C - B) * scr_norm A_C = (A - C) * scr_norm # screen space bounding box M = int(ti.floor(min(A, B, C) - 1)) N = int(ti.ceil(max(A, B, C) + 1)) M = ts.clamp(M, 0, ti.Vector(camera.res)) N = ts.clamp(N, 0, ti.Vector(camera.res)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): # barycentric coordinates using the area method X_A = X - A w_C = ts.cross(B_A, X_A) w_B = ts.cross(A_C, X_A) w_A = 1 - w_C - w_B # draw eps = ti.get_rel_eps() * 0.2 is_inside = w_A >= -eps and w_B >= -eps and w_C >= -eps if not is_inside: continue zindex = 1 / (posa.z * w_A + posb.z * w_B + posc.z * w_C) if zindex < ti.atomic_max(camera.fb['idepth'][X], zindex): continue clr = [ a * w_A + b * w_B + c * w_C for a, b, c in zip(clra, clrb, clrc) ] camera.fb.update(X, model.pixel_shader(*clr))
def render_triangle(model, camera, face): scene = model.scene L2W = model.L2W _1 = ti.static(min(1, model.faces.m - 1)) _2 = ti.static(min(2, model.faces.m - 1)) ia, ib, ic = model.vi[face[0, 0]], model.vi[face[1, 0]], model.vi[face[2, 0]] ta, tb, tc = model.vt[face[0, _1]], model.vt[face[1, _1]], model.vt[face[2, _1]] na, nb, nc = model.vn[face[0, _2]], model.vn[face[1, _2]], model.vn[face[2, _2]] a = camera.untrans_pos(L2W @ ia) b = camera.untrans_pos(L2W @ ib) c = camera.untrans_pos(L2W @ ic) # NOTE: the normal computation indicates that # a front-facing face should # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW); # this is to be compatible with obj model loading. normal = ts.normalize(ts.cross(a - b, a - c)) pos = (a + b + c) / 3 view_pos = (a + b + c) / 3 if ti.static(camera.type == camera.ORTHO): view_pos = ts.vec3(0.0, 0.0, 1.0) if ts.dot(view_pos, normal) <= 0: # shading color = ts.vec3(0.0) for light in ti.static(scene.lights): color += scene.opt.render_func(pos, normal, ts.vec3(0.0), light) color = scene.opt.pre_process(color) A = camera.uncook(a) B = camera.uncook(b) C = camera.uncook(c) scr_norm = 1 / ts.cross(A - C, B - A) B_A = (B - A) * scr_norm C_B = (C - B) * scr_norm A_C = (A - C) * scr_norm W = 1 # screen space bounding box M, N = int(ti.floor(min(A, B, C) - W)), int(ti.ceil(max(A, B, C) + W)) M.x, N.x = min(max(M.x, 0), camera.img.shape[0]), min(max(N.x, 0), camera.img.shape[1]) M.y, N.y = min(max(M.y, 0), camera.img.shape[0]), min(max(N.y, 0), camera.img.shape[1]) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): # barycentric coordinates using the area method X_A = X - A w_C = ts.cross(B_A, X_A) w_B = ts.cross(A_C, X_A) w_A = 1 - w_C - w_B # draw in_screen = w_A >= 0 and w_B >= 0 and w_C >= 0 and 0 < X[ 0] < camera.img.shape[0] and 0 < X[1] < camera.img.shape[1] if not in_screen: continue zindex = 1 / (a.z * w_A + b.z * w_B + c.z * w_C) if zindex < ti.atomic_max(camera.zbuf[X], zindex): continue coor = (ta * w_A + tb * w_B + tc * w_C) camera.img[X] = color * model.texSample(coor)
def __init__(self, x_list, gui, dim=2, **kwargs): # basic render settings self.dim = dim self.dim_size = ti.Vector([1., 1.]) self.minx = ti.Vector([-1., -1.]) assert self.dim == 1 or self.dim == 2 or self.dim == 3 self.dt = 2e-3 # time unit is 1/30 second # self.dt = 0.022 self.gui = gui self.vel_max = 50 # basic solver settings self.r = 0.1 # particle spacing self.h = self.r self.nbrs_num_max = 3000 self.grid_pnum_max = 3000 self.g = ti.Vector([0, -9.8]) # check section 5.3 for setting self.sigma = 0.3 self.beta = 0.3 # a non-zero value self.gamma = 0.1 # typically 0 ~ 0.2 self.alpha = 0.3 # check section 7 at the last for setting self.k = 0.504 self.k_near = 5.04 self.k_spring = 0.3 self.rho_0 = 100.0 # see section 6.1 self.mu = 0 # see https://github.com/omgware/fluid-simulator-v2/blob/master/fluid-simulator/src/com/fluidsimulator/FluidSimulatorSPH.java self.collisionForce = 100.0 # inferenced settings self.p_num = len(x_list) self.grid_size = ti.ceil( (self.dim_size - self.minx) / (2 * self.h)) + 10 # particle attributes self.x = ti.Vector(self.dim, ti.f32) # positions self.x_old = ti.Vector(self.dim, ti.f32) # old positions self.v = ti.Vector(self.dim, ti.f32) # velocity ti.root.dense(ti.i, self.p_num).place(self.x, self.x_old, self.v) self.nbrs_num = ti.var(ti.i32) self.nbrs_list = ti.var(ti.i32) self.strs_list = ti.var(ti.f32) self.strs_flag = ti.var(ti.i32) # self.L_list = ti.var(ti.f32) nbrs_nodes = ti.root.dense(ti.i, self.p_num) nbrs_nodes.place(self.nbrs_num) nbrs_nodes.dense(ti.j, self.nbrs_num_max).place(self.nbrs_list, self.strs_list, self.strs_flag) # grid attributes self.grid_p_num = ti.var(ti.i32) self.grids = ti.var(ti.i32) grid_nodes = ti.root.dense(ti.ij, (self.grid_size[0], self.grid_size[1])) grid_nodes.place(self.grid_p_num) grid_nodes.dense(ti.k, self.grid_pnum_max).place(self.grids) self.particle_list = np.array(x_list)
def render_triangle(model, camera, face): posa, posb, posc = face.pos # Position texa, texb, texc = face.tex # TexCoord nrma, nrmb, nrmc = face.nrm # Normal pos_center = (posa + posb + posc) / 3 if ti.static(camera.type == camera.ORTHO): pos_center = ts.vec3(0.0, 0.0, 1.0) # NOTE: the normal computation indicates that a front-facing face should # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW); # this is to be compatible with obj model loading. if ts.dot(pos_center, ts.cross(posa - posc, posa - posb)) >= 0: tan, bitan = compute_tangent(posb - posa, posc - posa, texb - texa, texc - texa) # TODO: node-ize this clra = [posa, texa, nrma] # TODO: interpolate tan and bitan? merge with nrm? clrb = [posb, texb, nrmb] clrc = [posc, texc, nrmc] A = camera.uncook(posa) B = camera.uncook(posb) C = camera.uncook(posc) scr_norm = ts.cross(A - C, B - A) if scr_norm != 0: # degenerate to 'line' if zero B_A = (B - A) / scr_norm A_C = (A - C) / scr_norm shake = ts.vec2(0.0) if ti.static(camera.fb.n_taa): for i, s in ti.static(enumerate(map(ti.Vector, TAA_SHAKES[:camera.fb.n_taa]))): if camera.fb.itaa[None] == i: shake = s * 0.5 # screen space bounding box M = int(ti.floor(min(A, B, C) - 1)) N = int(ti.ceil(max(A, B, C) + 1)) M = ts.clamp(M, 0, ti.Vector(camera.fb.res)) N = ts.clamp(N, 0, ti.Vector(camera.fb.res)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): # barycentric coordinates using the area method X_A = X - A + shake w_C = ts.cross(B_A, X_A) w_B = ts.cross(A_C, X_A) w_A = 1 - w_C - w_B # draw eps = ti.get_rel_eps() * 0.2 is_inside = w_A >= -eps and w_B >= -eps and w_C >= -eps if not is_inside: continue # https://gitee.com/zxtree2006/tinyrenderer/blob/master/our_gl.cpp if ti.static(camera.type != camera.ORTHO): bclip = ts.vec3(w_A / posa.z, w_B / posb.z, w_C / posc.z) bclip /= bclip.x + bclip.y + bclip.z w_A, w_B, w_C = bclip depth = (posa.z * w_A + posb.z * w_B + posc.z * w_C) if camera.fb.atomic_depth(X, depth): continue posx, texx, nrmx = [a * w_A + b * w_B + c * w_C for a, b, c in zip(clra, clrb, clrc)] color = ti.static(model.material.pixel_shader(model, posx, texx, nrmx, tan, bitan)) if ti.static(isinstance(color, dict)): camera.fb.update(X, color) else: camera.fb.update(X, dict(img=color))
def foo(): x[None] = ti.neg(y[None]) x[None] = ti.floor(x[None]) y[None] = ti.ceil(y[None])
def render_triangle(model, camera, face): scene = model.scene L2C = model.L2C[None] # Local to Camera, i.e. ModelView in OpenGL posa, posb, posc = face.pos texa, texb, texc = face.tex nrma, nrmb, nrmc = face.nrm posa = (L2C @ ts.vec4(posa, 1)).xyz posb = (L2C @ ts.vec4(posb, 1)).xyz posc = (L2C @ ts.vec4(posc, 1)).xyz nrma = (L2C @ ts.vec4(nrma, 0)).xyz nrmb = (L2C @ ts.vec4(nrmb, 0)).xyz nrmc = (L2C @ ts.vec4(nrmc, 0)).xyz pos_center = (posa + posb + posc) / 3 if ti.static(camera.type == camera.ORTHO): pos_center = ts.vec3(0.0, 0.0, 1.0) dpab = posa - posb dpac = posa - posc dtab = texa - texb dtac = texa - texc normal = ts.cross(dpab, dpac) # NOTE: the normal computation indicates that a front-facing face should # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW); # this is to be compatible with obj model loading. if ts.dot(pos_center, normal) <= 0: tan, bitan = compute_tangent(-dpab, -dpac, -dtab, -dtac) # TODO: node-ize this clra = model.vertex_shader( posa, texa, nrma, tan, bitan) # TODO: interpolate tan and bitan? merge with nrm? clrb = model.vertex_shader(posb, texb, nrmb, tan, bitan) clrc = model.vertex_shader(posc, texc, nrmc, tan, bitan) A = camera.uncook(posa) B = camera.uncook(posb) C = camera.uncook(posc) scr_norm = ts.cross(A - C, B - A) if scr_norm != 0: # degenerate to 'line' if zero B_A = (B - A) / scr_norm A_C = (A - C) / scr_norm shake = ts.vec2(0.0) if ti.static(camera.fb.n_taa): for i, s in ti.static( enumerate(map(ti.Vector, TAA_SHAKES[:camera.fb.n_taa]))): if camera.fb.itaa[None] == i: shake = s * 0.5 # screen space bounding box M = int(ti.floor(min(A, B, C) - 1)) N = int(ti.ceil(max(A, B, C) + 1)) M = ts.clamp(M, 0, ti.Vector(camera.fb.res)) N = ts.clamp(N, 0, ti.Vector(camera.fb.res)) for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))): # barycentric coordinates using the area method X_A = X - A + shake w_C = ts.cross(B_A, X_A) w_B = ts.cross(A_C, X_A) w_A = 1 - w_C - w_B # draw eps = ti.get_rel_eps() * 0.2 is_inside = w_A >= -eps and w_B >= -eps and w_C >= -eps if not is_inside: continue # https://gitee.com/zxtree2006/tinyrenderer/blob/master/our_gl.cpp if ti.static(camera.type != camera.ORTHO): bclip = ts.vec3(w_A / posa.z, w_B / posb.z, w_C / posc.z) bclip /= bclip.x + bclip.y + bclip.z w_A, w_B, w_C = bclip depth = (posa.z * w_A + posb.z * w_B + posc.z * w_C) if camera.fb.atomic_depth(X, depth): continue clr = [ a * w_A + b * w_B + c * w_C for a, b, c in zip(clra, clrb, clrc) ] camera.fb.update(X, model.pixel_shader(*clr))