使用双线性插值 加载纹理贴图(实现的很粗糙 效果 有点差orz…
生成的图片:
原图:
import numpy as np
import taichi as ti
import torch
import math
import cv2
ti.init(arch=ti.gpu)
eps = 1e-5
_t = ti.imread('images/yui.jpg').astype(np.float32) / 255.0
_c = np.array([[-0.5, -0.5, 0.0, 0.0, 0.0],
[0.5, -0.5, 0.0, 1.0, 0.0],
[0.5, 0.5, 0.0, 1.0, 1.0],
[0.5, 0.5, 0.0, 1.0, 1.0],
[-0.5, 0.5, 0.0, 0.0, 1.0],
[-0.5, -0.5, 0.0, 0.0, 0.0]]).astype(np.float32)
texture = ti.var(ti.f32, shape=_t.shape)
t_s = ti.var(ti.i32, shape=(2,))
cube = ti.var(ti.f32, shape=_c.shape)
item_t = ti.var(ti.i32, shape=())
item_c = ti.var(ti.i32, shape=())
mat_model = ti.Matrix(4, 4, dt=ti.f32, shape=())
mat_view = ti.Matrix(4, 4, dt=ti.f32, shape=())
mat_proj = ti.Matrix(4, 4, dt=ti.f32, shape=())
tri = ti.Vector(4, dt=ti.f32, shape=(3,))
n = 512
pixels = ti.var(ti.f32, shape=(n, n, 3))
z_buffer = ti.var(ti.f32, shape=(n, n))
dv = ti.var(ti.f32, shape=(3, 3))
rot = ti.var(ti.f32, shape=(3, 2))
c_p = ti.var(ti.f32, shape=(3,))
@ti.func
def clip(x, a, b):
return max(a, min(x, b))
@ti.func
def init_mat_model():
it = item_c[None]
rot_x = ti.Matrix([[1.0, 0.0, 0.0, 0.0],
[0.0, ti.cos(rot[it, 0]), -ti.sin(rot[it, 0]), 0.0],
[0.0, ti.sin(rot[it, 0]), ti.cos(rot[it, 0]), 0.0],
[0.0, 0.0, 0.0, 1.0]])
rot_y = ti.Matrix([[ti.cos(rot[it, 1]), 0.0, ti.sin(rot[it, 1]), 0.0],
[0.0, 1.0, 0.0, 0.0],
[-ti.sin(rot[it, 1]), 0.0, ti.cos(rot[it, 1]), 0.0],
[0.0, 0.0, 0.0, 1.0]])
T = ti.Matrix([[1.0, 0.0, 0.0, dv[it, 0]],
[0.0, 1.0, 0.0, dv[it, 1]],
[0.0, 0.0, 1.0, dv[it, 2]],
[0.0, 0.0, 0.0, 1.0]])
mat_model[None] = T @ rot_y @ rot_x
@ti.func
def init_mat_view():
T = ti.Matrix([[1.0, 0.0, 0.0, -c_p[0]],
[0.0, 1.0, 0.0, -c_p[1]],
[0.0, 0.0, 1.0, -c_p[2]],
[0.0, 0.0, 0.0, 1.0]])
g = -ti.Vector([c_p[0], c_p[1], c_p[2]]).normalized()
h = ti.Vector([0.0, 1.0, 0.0])
gxt = g.cross(h).normalized()
t = gxt.cross(g).normalized()
R = ti.Matrix([[gxt[0], gxt[1], gxt[2], 0.0],
[t[0], t[1], t[2], 0.0],
[-g[0], -g[1], -g[2], 0.0],
[0.0, 0.0, 0.0, 1.0]])
mat_view[None] = R @ T
@ti.func
def init_mat_proj():
zn, zf = 0.1, 50.0
t, b = 1.0 * zn, -1.0 * zn
l, r = -1.0 * zn, 1.0 * zn
zn *= -1
zf *= -1
Mp = ti.Matrix([[zn, 0.0, 0.0, 0.0],
[0.0, zn, 0.0, 0.0],
[0.0, 0.0, zn + zf, -zn * zf],
[0.0, 0.0, 1.0, 0.0]])
Mot = ti.Matrix([[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, -(zn + zf) / 2.0],
[0.0, 0.0, 0.0, 1.0]])
Mos = ti.Matrix([[2.0 / (r - l), 0.0, 0.0, 0.0],
[0.0, 2.0 / (t - b), 0.0, 0.0],
[0.0, 0.0, 2.0 / (zn - zf), 0.0],
[0.0, 0.0, 0.0, 1.0]])
mat_proj[None] = Mos @ Mot @ Mp
@ti.func
def init_range(tri):
l = int(ti.floor(clip(min(tri[0][0], tri[1][0], tri[2][0]), 0, n - 1)))
r = int(ti.ceil(clip(max(tri[0][0], tri[1][0], tri[2][0]), 0, n - 1)))
b = int(ti.floor(clip(min(tri[0][1], tri[1][1], tri[2][1]), 0, n - 1)))
t = int(ti.ceil(clip(max(tri[0][1], tri[1][1], tri[2][1]), 0, n - 1)))
return l, r, b, t
@ti.func
def det(a, b):
return a[0] * b[1] - a[1] * b[0]
@ti.func
def area(a, b):
return ti.abs(det(a, b))
@ti.func
def in_tri(x, y, tri):
m = ti.Matrix([[0., 0.], [0., 0.], [0., 0.]])
s = ti.Vector([0., 0., 0.])
for k in ti.static(range(3)):
m[k, 0], m[k, 1] = tri[k][0] - x, tri[k][1] - y
for k in ti.static(range(3)):
s[k] = det(ti.Vector([m[k, 0], m[k, 1]]),
ti.Vector([m[(k + 1) % 3, 0], m[(k + 1) % 3, 1]]))
return s[0] * s[1] >= 0 and s[1] * s[2] >= 0 and s[2] * s[0] >= 0
@ti.func
def bary(x, y, tri):
m = ti.Matrix([[0., 0.], [0., 0.], [0., 0.]])
s = ti.Vector([0., 0., 0.])
for k in ti.static(range(3)):
m[k, 0], m[k, 1] = tri[k][0] - x, tri[k][1] - y
for k in ti.static(range(3)):
s[k] = det(ti.Vector([m[k, 0], m[k, 1]]),
ti.Vector([m[(k + 1) % 3, 0], m[(k + 1) % 3, 1]]))
s = ti.abs(s)
u, v, w = s[1], s[2], s[0]
sa = u + v + w
return u / sa, v / sa, w / sa
@ti.func
def interpolation(x, y): # bilinear
th, tw = t_s[0], t_s[1]
c = ti.Vector([clip(x * tw, 0., th - 1 - eps),
clip(y * th, 0., tw - 1 - eps)])
p = ti.ti_int(ti.floor(c))
color = ti.Vector([0., 0., 0.])
for i in ti.static(range(2)):
for j in ti.static(range(2)):
v = ti.Vector([p[0] + i, p[1] + j], dt=ti.f32) - c
w = abs(v[0]) * abs(v[1])
for k in ti.static(range(3)):
color[k] += w * texture[p[0] + i, p[1] + j, k]
return color
@ti.func
def set_pixel(x, y, z, c):
if 1 > z > -1 and z > z_buffer[x, y]:
for k in ti.static(range(3)):
pixels[x, y, k] = c[k]
z_buffer[x, y] = z
@ti.kernel
def render_tri():
it = item_t[None]
init_mat_model()
init_mat_view()
init_mat_proj()
for i in ti.static(range(3)):
tri[i] = ti.Vector([cube[3 * it + i, 0],
cube[3 * it + i, 1],
cube[3 * it + i, 2],
1.0])
tri[i] = mat_proj @ mat_view @ mat_model @ tri[i]
tri[i] /= tri[i][3]
tri[i][0] = 0.5 * n * (1.0 + tri[i][0])
tri[i][1] = 0.5 * n * (1.0 + tri[i][1])
cd = ti.Matrix([[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]])
for k in ti.static(range(3)):
cd[k, 0], cd[k, 1] = cube[3 * it + k, 3], cube[3 * it + k, 4]
l, r, b, t = init_range(tri)
# print(l, r, b, t)
for x, y in ti.ndrange((l, r + 1), (b, t + 1)):
if in_tri(x + 0.5, y + 0.5, tri):
u, v, w = bary(x + 0.5, y + 0.5, tri)
z = u * tri[0][2] + v * tri[1][2] + w * tri[2][2]
tx = u * cd[0, 0] + v * cd[1, 0] + w * cd[2, 0]
ty = u * cd[0, 1] + v * cd[1, 1] + w * cd[2, 1]
tx = clip(tx, 0., 1.)
ty = clip(ty, 0., 1.)
color = interpolation(tx, ty)
set_pixel(x, y, z, color)
@ti.kernel
def init():
for i, j in z_buffer:
z_buffer[i, j] = -100
pixels[i, j, 0] = 202 / 255
pixels[i, j, 1] = 235 / 255
pixels[i, j, 2] = 216 / 255
texture.from_numpy(_t)
cube.from_numpy(_c)
c_p.from_numpy(np.array([1.0, 1.0, 1.0]).astype(np.float32))
t_s[0], t_s[1] = _t.shape[0], _t.shape[1]
dv.from_numpy(np.array([[0.0, 0.0, 0.5],
[0.0, 0.5, 0.0],
[0.5, 0.0, 0.0]]).astype(np.float32))
rot.from_numpy(np.array([[0.0, 0.0],
[-math.pi / 2, 0.0],
[0.0, math.pi / 2]]).astype(np.float32))
gui = ti.GUI('Yui')
init()
for i in range(3):
for j in range(2):
item_c[None] = i
item_t[None] = j
render_tri()
img = np.round(pixels.to_numpy() * 255).astype(np.uint8)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
cv2.imwrite('images/yui_draw.jpg', img)
for t in range(10000):
gui.set_image(pixels)
gui.show()