Spaces:
Running
Running
| # import cython | |
| import numpy as np | |
| from math import sqrt | |
| def normalize(x, y, z): | |
| unit = sqrt(x * x + y * y + z * z) | |
| if unit == 0: | |
| return 0, 0, 0 | |
| return x / unit, y / unit, z / unit | |
| def get_min_max(a, b, c): | |
| min = a | |
| max = a | |
| if min > b: | |
| min = b | |
| if min > c: | |
| min = c | |
| if max < b: | |
| max = b | |
| if max < c: | |
| max = c | |
| return int(min), int(max) | |
| def dot_product(a0, a1, a2, b0, b1, b2): | |
| r = a0 * b0 + a1 * b1 + a2 * b2 | |
| return r | |
| def cross_product(a0, a1, a2, b0, b1, b2): | |
| x = a1 * b2 - a2 * b1 | |
| y = a2 * b0 - a0 * b2 | |
| z = a0 * b1 - a1 * b0 | |
| return x,y,z | |
| # @cython.boundscheck(False) | |
| def generate_faces(triangles, width, height): | |
| """ draw the triangle faces with z buffer | |
| Args: | |
| triangles: groups of vertices | |
| FYI: | |
| * zbuffer, https://github.com/ssloy/tinyrenderer/wiki/Lesson-3:-Hidden-faces-removal-(z-buffer) | |
| * uv mapping and perspective correction | |
| """ | |
| i, j, k, length = 0, 0, 0, 0 | |
| bcy, bcz, x, y, z = 0.,0.,0.,0.,0. | |
| a, b, c = [0.,0.,0.],[0.,0.,0.],[0.,0.,0.] | |
| m, bc = [0.,0.,0.],[0.,0.,0.] | |
| uva, uvb, uvc = [0.,0.],[0.,0.],[0.,0.] | |
| minx, maxx, miny, maxy = 0,0,0,0 | |
| length = triangles.shape[0] | |
| zbuffer = {} | |
| faces = [] | |
| for i in range(length): | |
| a = triangles[i, 0, 0], triangles[i, 0, 1], triangles[i, 0, 2] | |
| b = triangles[i, 1, 0], triangles[i, 1, 1], triangles[i, 1, 2] | |
| c = triangles[i, 2, 0], triangles[i, 2, 1], triangles[i, 2, 2] | |
| uva = triangles[i, 0, 3], triangles[i, 0, 4] | |
| uvb = triangles[i, 1, 3], triangles[i, 1, 4] | |
| uvc = triangles[i, 2, 3], triangles[i, 2, 4] | |
| minx, maxx = get_min_max(a[0], b[0], c[0]) | |
| miny, maxy = get_min_max(a[1], b[1], c[1]) | |
| pixels = [] | |
| for j in range(minx, maxx + 2): | |
| for k in range(miny - 1, maxy + 2): | |
| # 必须显式转换成 double 参与底下的运算,不然结果是错的 | |
| x = j | |
| y = k | |
| m[0], m[1], m[2] = cross_product(c[0] - a[0], b[0] - a[0], a[0] - x, c[1] - a[1], b[1] - a[1], a[1] - y) | |
| if abs(m[2]) > 0: | |
| bcy = m[1] / m[2] | |
| bcz = m[0] / m[2] | |
| bc = (1 - bcy - bcz, bcy, bcz) | |
| else: | |
| continue | |
| # here, -0.00001 because of the precision lose | |
| if bc[0] < -0.00001 or bc[1] < -0.00001 or bc[2] < -0.00001: | |
| continue | |
| z = 1 / (bc[0] / a[2] + bc[1] / b[2] + bc[2] / c[2]) | |
| # Blender 导出来的 uv 数据,跟之前的顶点数据有一样的问题,Y轴是个反的, | |
| # 所以这里的纹理图片要旋转一下才能 work | |
| v = (uva[0] * bc[0] / a[2] + uvb[0] * bc[1] / b[2] + uvc[0] * bc[2] / c[2]) * z * width | |
| u = height - (uva[1] * bc[0] / a[2] + uvb[1] * bc[1] / b[2] + uvc[1] * bc[2] / c[2]) * z * height | |
| # https://en.wikipedia.org/wiki/Pairing_function | |
| idx = ((x + y) * (x + y + 1) + y) / 2 | |
| if zbuffer.get(idx) is None or zbuffer[idx] < z: | |
| zbuffer[idx] = z | |
| pixels.append((i, j, k, int(u) - 1, int(v) - 1)) | |
| faces.append(pixels) | |
| return faces | |