def march(self) -> int: r_n = 0 for I in ti.grouped(ti.ndrange(*[[1, self.N - 1]] * self.dim)): id = 0 if ti.static(self.dim == 2): i, j = I if self.m[i, j] > 1: id |= 1 if self.m[i + 1, j] > 1: id |= 2 if self.m[i, j + 1] > 1: id |= 4 if self.m[i + 1, j + 1] > 1: id |= 8 else: i, j, k = I if self.m[i, j, k] > 1: id |= 1 if self.m[i + 1, j, k] > 1: id |= 2 if self.m[i + 1, j + 1, k] > 1: id |= 4 if self.m[i, j + 1, k] > 1: id |= 8 if self.m[i, j, k + 1] > 1: id |= 16 if self.m[i + 1, j, k + 1] > 1: id |= 32 if self.m[i + 1, j + 1, k + 1] > 1: id |= 64 if self.m[i, j + 1, k + 1] > 1: id |= 128 for m in range(self.et.shape[1]): if self.et[id, m][0] == -1: break n = ti.atomic_add(r_n, 1) for l in ti.static(range(self.dim)): e = self.et[id, m][l] R = float(I) if ti.static(self.dim == 2): i, j = I # (.5, 0), (0, .5), (1, .5), (.5, 1) if e == 0: p = (1 - self.m[i, j]) / (self.m[i + 1, j] - self.m[i, j]) R = [i + p, j] elif e == 1: p = (1 - self.m[i, j]) / (self.m[i, j + 1] - self.m[i, j]) R = [i, j + p] elif e == 2: p = (1 - self.m[i + 1, j]) / ( self.m[i + 1, j + 1] - self.m[i + 1, j]) R = [i + 1, j + p] elif e == 3: p = (1 - self.m[i, j + 1]) / ( self.m[i + 1, j + 1] - self.m[i, j + 1]) R = [i + p, j + 1] else: i, j, k = I # (.5, 0, 0), (1, .5, 0), (.5, 1, 0), (0, .5, 0) # (.5, 0, 1), (1, .5, 1), (.5, 1, 1), (0, .5, 1) # (0, 0, .5), (1, 0, .5), (1, 1, .5), (0, 1, .5) if e == 0: p = (1 - self.m[i, j, k]) / (self.m[i + 1, j, k] - self.m[i, j, k]) R = [i + p, j, k] elif e == 1: p = (1 - self.m[i + 1, j, k]) / ( self.m[i + 1, j + 1, k] - self.m[i + 1, j, k]) R = [i + 1, j + p, k] elif e == 2: p = (1 - self.m[i, j + 1, k]) / ( self.m[i + 1, j + 1, k] - self.m[i, j + 1, k]) R = [i + p, j + 1, k] elif e == 3: p = (1 - self.m[i, j, k]) / (self.m[i, j + 1, k] - self.m[i, j, k]) R = [i, j + p, k] elif e == 4: p = (1 - self.m[i, j, k + 1]) / ( self.m[i + 1, j, k + 1] - self.m[i, j, k + 1]) R = [i + p, j, k + 1] elif e == 5: p = (1 - self.m[i + 1, j, k + 1]) / ( self.m[i + 1, j + 1, k + 1] - self.m[i + 1, j, k + 1]) R = [i + 1, j + p, k + 1] elif e == 6: p = (1 - self.m[i, j + 1, k + 1]) / ( self.m[i + 1, j + 1, k + 1] - self.m[i, j + 1, k + 1]) R = [i + p, j + 1, k + 1] elif e == 7: p = (1 - self.m[i, j, k + 1]) / ( self.m[i, j + 1, k + 1] - self.m[i, j, k + 1]) R = [i, j + p, k + 1] elif e == 8: p = (1 - self.m[i, j, k]) / (self.m[i, j, k + 1] - self.m[i, j, k]) R = [i, j, k + p] elif e == 9: p = (1 - self.m[i + 1, j, k]) / ( self.m[i + 1, j, k + 1] - self.m[i + 1, j, k]) R = [i + 1, j, k + p] elif e == 10: p = (1 - self.m[i + 1, j + 1, k]) / ( self.m[i + 1, j + 1, k + 1] - self.m[i + 1, j + 1, k]) R = [i + 1, j + 1, k + p] elif e == 11: p = (1 - self.m[i, j + 1, k]) / ( self.m[i, j + 1, k + 1] - self.m[i, j + 1, k]) R = [i, j + 1, k + p] self.r[n, l] = R return r_n
def substep(): for i, j in grid_m: grid_v[i, j] = [0, 0] grid_m[i, j] = 0 for p in x: # Particle state update and scatter to grid (P2G) base = (x[p] * inv_dx - 0.5).cast(int) fx = x[p] * inv_dx - base.cast(float) # Quadratic kernels [http://mpm.graphics Eqn. 123, with x=fx, fx-1,fx-2] w = [ 0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5) ] F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p] # deformation gradient update h = max(0.1, min(5, ti.exp(10 * (1.0 - Jp[p]))) ) # Hardening coefficient: snow gets harder when compressed if material[p] == 1: # jelly, make it softer h = 0.3 mu, la = mu_0 * h, lambda_0 * h if material[p] == 0: # liquid mu = 0.0 U, sig, V = ti.svd(F[p]) J = 1.0 for d in ti.static(range(2)): new_sig = sig[d, d] if material[p] == 2: # Snow new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3) # Plasticity Jp[p] *= sig[d, d] / new_sig sig[d, d] = new_sig J *= new_sig if material[ p] == 0: # Reset deformation gradient to avoid numerical instability F[p] = ti.Matrix.identity(ti.f32, 2) * ti.sqrt(J) elif material[p] == 2: F[p] = U @ sig @ V.T( ) # Reconstruct elastic deformation gradient after plasticity stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity( ti.f32, 2) * la * J * (J - 1) stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress affine = stress + p_mass * C[p] for i, j in ti.static(ti.ndrange( 3, 3)): # Loop over 3x3 grid node neighborhood offset = ti.Vector([i, j]) dpos = (offset.cast(float) - fx) * dx weight = w[i][0] * w[j][1] grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos) grid_m[base + offset] += weight * p_mass for i, j in grid_m: if grid_m[i, j] > 0: # No need for epsilon here grid_v[i, j] = (1 / grid_m[i, j]) * grid_v[i, j] # Momentum to velocity grid_v[i, j] += dt * gravity[None] * 30 # gravity dist = attractor_pos[None] - dx * ti.Vector([i, j]) grid_v[i, j] += dist / ( 0.01 + dist.norm()) * attractor_strength[None] * dt * 100 if i < 3 and grid_v[i, j][0] < 0: grid_v[i, j][0] = 0 # Boundary conditions if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0 if j < 3 and grid_v[i, j][1] < 0: grid_v[i, j][1] = 0 if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0 for p in x: # grid to particle (G2P) base = (x[p] * inv_dx - 0.5).cast(int) fx = x[p] * inv_dx - base.cast(float) w = [ 0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1.0), 0.5 * ti.sqr(fx - 0.5) ] new_v = ti.Vector.zero(ti.f32, 2) new_C = ti.Matrix.zero(ti.f32, 2, 2) for i, j in ti.static(ti.ndrange( 3, 3)): # loop over 3x3 grid node neighborhood dpos = ti.Vector([i, j]).cast(float) - fx g_v = grid_v[base + ti.Vector([i, j])] weight = w[i][0] * w[j][1] new_v += weight * g_v new_C += 4 * inv_dx * weight * ti.outer_product(g_v, dpos) v[p], C[p] = new_v, new_C x[p] += dt * v[p] # advection
def func(): for i in x: for j in ti.static(range(3)): y[i] += x[i] y[i] += x[i] * 42
def test(): for i, v in ti.static(enumerate(range(8))): pass print(i)
def static(): if ti.static(x[0]): x[0] = 1 else: x[0] = 0
def run(self): for I in ti.static(self.dst): self.dst[I] += self.src[I]
def point_aabb_distance2(box_min, box_max, o): p = ti.Vector([0.0, 0.0, 0.0]) for i in ti.static(range(3)): p[i] = ti.max(ti.min(o[i], box_max[i]), box_min[i]) return ti.Matrix.norm_sqr(p - o)
def compute_center(t: ti.i32): for _ in range(1): c = ti.Vector([0.0, 0.0]) for i in ti.static(range(n_objects)): c += x[t, i] center[t] = (1.0 / n_objects) * c
def p2g(self, dt: ti.f32): ti.no_activate(self.particle) ti.block_dim(256) ti.block_local(*self.grid_v.entries) ti.block_local(self.grid_m) for I in ti.grouped(self.pid): p = self.pid[I] base = ti.floor(self.x[p] * self.inv_dx - 0.5).cast(int) for D in ti.static(range(self.dim)): base[D] = ti.assume_in_range(base[D], I[D], 0, 1) fx = self.x[p] * self.inv_dx - base.cast(float) # Quadratic kernels [http://mpm.graphics Eqn. 123, with x=fx, fx-1,fx-2] w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2] # deformation gradient update self.F[p] = (ti.Matrix.identity(ti.f32, self.dim) + dt * self.C[p]) @ self.F[p] # Hardening coefficient: snow gets harder when compressed h = ti.exp(10 * (1.0 - self.Jp[p])) if self.material[ p] == self.material_elastic: # jelly, make it softer h = 0.3 mu, la = self.mu_0 * h, self.lambda_0 * h if self.material[p] == self.material_water: # liquid mu = 0.0 U, sig, V = ti.svd(self.F[p]) J = 1.0 if self.material[p] != self.material_sand: for d in ti.static(range(self.dim)): new_sig = sig[d, d] if self.material[p] == self.material_snow: # Snow new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3) # Plasticity self.Jp[p] *= sig[d, d] / new_sig sig[d, d] = new_sig J *= new_sig if self.material[p] == self.material_water: # Reset deformation gradient to avoid numerical instability new_F = ti.Matrix.identity(ti.f32, self.dim) new_F[0, 0] = J self.F[p] = new_F elif self.material[p] == self.material_snow: # Reconstruct elastic deformation gradient after plasticity self.F[p] = U @ sig @ V.transpose() stress = ti.Matrix.zero(ti.f32, self.dim, self.dim) if self.material[p] != self.material_sand: stress = 2 * mu * ( self.F[p] - U @ V.transpose()) @ self.F[p].transpose( ) + ti.Matrix.identity(ti.f32, self.dim) * la * J * (J - 1) else: sig = self.sand_projection(sig, p) self.F[p] = U @ sig @ V.transpose() log_sig_sum = 0.0 center = ti.Matrix.zero(ti.f32, self.dim, self.dim) for i in ti.static(range(self.dim)): log_sig_sum += ti.log(sig[i, i]) center[i, i] = 2.0 * self.mu_0 * ti.log( sig[i, i]) * (1 / sig[i, i]) for i in ti.static(range(self.dim)): center[i, i] += self.lambda_0 * log_sig_sum * (1 / sig[i, i]) stress = U @ center @ V.transpose() @ self.F[p].transpose() stress = (-dt * self.p_vol * 4 * self.inv_dx**2) * stress affine = stress + self.p_mass * self.C[p] # Loop over 3x3 grid node neighborhood for offset in ti.static(ti.grouped(self.stencil_range())): dpos = (offset.cast(float) - fx) * self.dx weight = 1.0 for d in ti.static(range(self.dim)): weight *= w[offset[d]][d] self.grid_v[base + offset] += weight * (self.p_mass * self.v[p] + affine @ dpos) self.grid_m[base + offset] += weight * self.p_mass
def fill_vbo(vbo: template(), value: template(), offset: template(), num_components: template()): for i in vbo: for c in ti.static(range(num_components)): vbo[i][offset + c] = value
def substep(): for i, j in grid_m: grid_v[i, j] = [0, 0] grid_m[i, j] = 0 for p in x: base = (x[p] * inv_dx - 0.5).cast(int) fx = x[p] * inv_dx - base.cast(float) w = [ 0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5) ] F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p] h = ti.exp(10 * (1.0 - Jp[p])) if material[p] == 0: h = 0.3 mu, la = mu_0 * h, lambda_0 * h U, sig, V = ti.svd(F[p]) J = 1.0 for d in ti.static(range(2)): new_sig = sig[d, d] if material[p] == 1: new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3) Jp[p] *= sig[d, d] / new_sig sig[d, d] = new_sig J *= new_sig if material[p] == 1: F[p] = U @ sig @ V.T() stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity( ti.f32, 2) * la * J * (J - 1) stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress affine = stress + p_mass * C[p] for i, j in ti.static(ti.ndrange(3, 3)): offset = ti.Vector([i, j]) dpos = (offset.cast(float) - fx) * dx weight = w[i][0] * w[j][1] grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos) grid_m[base + offset] += weight * p_mass for i, j in grid_m: if grid_m[i, j] > 0: grid_v[i, j] = (1 / grid_m[i, j]) * grid_v[i, j] grid_v[i, j][1] -= dt * 50 if i < 3 and grid_v[i, j][0] < 0: grid_v[i, j][0] = 0 if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0 if j < 3 and grid_v[i, j][1] < 0: grid_v[i, j][1] = 0 if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0 for p in x: base = (x[p] * inv_dx - 0.5).cast(int) fx = x[p] * inv_dx - base.cast(float) w = [ 0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1.0), 0.5 * ti.sqr(fx - 0.5) ] new_v = ti.Vector.zero(ti.f32, 2) new_C = ti.Matrix.zero(ti.f32, 2, 2) for i, j in ti.static(ti.ndrange(3, 3)): dpos = ti.Vector([i, j]).cast(float) - fx g_v = grid_v[base + ti.Vector([i, j])] weight = w[i][0] * w[j][1] new_v += weight * g_v new_C += 4 * inv_dx * weight * ti.outer_product(g_v, dpos) v[p], C[p] = new_v, new_C x[p] += dt * v[p]
def copy_to_vbo(vbo: template(), src: template(), offset: template(), num_components: template()): for i in src: for c in ti.static(range(num_components)): vbo[i][offset + c] = src[i][c]
def render(): ti.parallelize(6) for u, v in color_buffer: pos = camera_pos d = ti.Vector([(2 * fov * (u + ti.random(ti.f32)) / res[1] - fov * aspect_ratio - 1e-5), 2 * fov * (v + ti.random(ti.f32)) / res[1] - fov - 1e-5, -1.0]) d = ti.Matrix.normalized(d) t = (ti.random() - 0.5) * shutter_time contrib = ti.Vector([0.0, 0.0, 0.0]) throughput = ti.Vector([1.0, 1.0, 1.0]) depth = 0 hit_sky = 1 ray_depth = 0 while depth < max_ray_depth: closest, normal, c = next_hit(pos, d, t) hit_pos = pos + closest * d depth += 1 ray_depth = depth if normal.norm() != 0: d = out_dir(normal) pos = hit_pos + 1e-4 * d throughput *= c if ti.static(use_directional_light): dir_noise = ti.Vector([ ti.random() - 0.5, ti.random() - 0.5, ti.random() - 0.5 ]) * light_direction_noise direct = ti.Matrix.normalized( ti.Vector(light_direction) + dir_noise) dot = direct.dot(normal) if dot > 0: dist, _, _ = next_hit(pos, direct, t) if dist > dist_limit: contrib += throughput * ti.Vector( light_color) * dot else: # hit sky hit_sky = 1 depth = max_ray_depth max_c = throughput.max() if ti.random() > max_c: depth = max_ray_depth throughput = [0, 0, 0] else: throughput /= max_c if hit_sky: if ray_depth != 1: # contrib *= max(d[1], 0.05) pass else: # directly hit sky pass else: throughput *= 0 # contrib += throughput color_buffer[u, v] += contrib
def dda_particle(eye_pos, d, t): grid_res = particle_grid_res bbox_min = bbox[0] bbox_max = bbox[1] hit_pos = ti.Vector([0.0, 0.0, 0.0]) normal = ti.Vector([0.0, 0.0, 0.0]) c = ti.Vector([0.0, 0.0, 0.0]) for i in ti.static(range(3)): if abs(d[i]) < 1e-6: d[i] = 1e-6 inter, near, far = ray_aabb_intersection(bbox_min, bbox_max, eye_pos, d) near = max(0, near) closest_intersection = inf if inter: pos = eye_pos + d * (near + eps) rinv = 1.0 / d rsign = ti.Vector([0, 0, 0]) for i in ti.static(range(3)): if d[i] > 0: rsign[i] = 1 else: rsign[i] = -1 o = grid_res * pos ipos = ti.Matrix.floor(o).cast(int) dis = (ipos - o + 0.5 + rsign * 0.5) * rinv running = 1 while running: inside = inside_particle_grid(ipos) if inside: num_particles = voxel_has_particle[ipos] if num_particles != 0: num_particles = ti.length(pid.parent(), ipos) for k in range(num_particles): p = pid[ipos[0], ipos[1], ipos[2], k] v = particle_v[p] x = particle_x[p] + t * v color = particle_color[p] dist, poss = intersect_sphere(eye_pos, d, x, sphere_radius) hit_pos = poss if dist < closest_intersection and dist > 0: hit_pos = eye_pos + dist * d closest_intersection = dist normal = ti.Matrix.normalized(hit_pos - x) c = color else: running = 0 normal = [0, 0, 0] if closest_intersection < inf: running = 0 else: # hits nothing. Continue ray marching mm = ti.Vector([0, 0, 0]) if dis[0] <= dis[1] and dis[0] <= dis[2]: mm[0] = 1 elif dis[1] <= dis[0] and dis[1] <= dis[2]: mm[1] = 1 else: mm[2] = 1 dis += mm * rsign * rinv ipos += mm * rsign return closest_intersection, normal, c
def run(self): for I in ti.static(self.src): self.buf[I] = self.src[I]
def copy_dynamic_nd(self, np_x: ti.ext_arr(), input_x: ti.template()): for i in self.x: for j in ti.static(range(self.dim)): np_x[i, j] = input_x[i][j]
def _run(self, nxt: ti.template(), src: ti.template()): for I in ti.static(src): nxt[I] = src[I]
def util_vector_to_plain_array(self, v: ti.template()) -> ti.template(): ### used .mat of Proxy object, PROBLEM? return list([v[i] for i in ti.static(range(v.mat.n))])
def update(): for i in ti.static(range(number_coeffs)): coeffs[i] -= learning_rate * coeffs.grad[i]
def util_matrix_to_plain_array(self, v: ti.template()) -> ti.template(): ### used .mat of Proxy object, PROBLEM? return list([[v[n, m] for m in ti.static(range(v.mat.m))] for n in ti.static(range(v.mat.n))])
def test(): for i in ti.static(range(8)): pass print(i)
def cook_coor(self, I, camera): scale = ti.static(2 / min(*camera.img.shape())) coor = (I - ts.vec2(*camera.img.shape()) / 2) * scale return coor
def static(): if ti.static(val > 0.5): x[0] = 1 else: x[0] = 0
def uncook_coor(self, coor, camera): scale = ti.static(min(*camera.img.shape()) / 2) I = coor.xy * scale + ts.vec2(*camera.img.shape()) / 2 return I
def neighbor_sum(self, x, I): ret = 0.0 for i in ti.static(range(self.dim)): offset = ti.Vector.unit(self.dim, i) ret += x[I + offset] + x[I - offset] return ret
def func(): for i in ti.static(ti.static(range(1))): pass
def func(): for i in range(ti.static(N // 2 + 3), N): x[i] = ti.abs(y[i])
def test(): for I in ti.static(ti.grouped(val)): pass
def clamp(p): for d in ti.static(range(p.n)): p[d] = min(1 - 1e-4 - dx + stagger[d] * dx, max(p[d], stagger[d] * dx)) return p
def clamp( val , low , high): if ti.static(is_scalar(val)): return clamp else: return clamp_vector(val , low , high)