前提:
纹理在模型上贴好后,能使用blender python api直接获取的就是,这个模型的每个三角面片上顶点对应的纹理坐标。这其中每个三角面的顶点构成一个三角形(A),每个三角面的顶点对应的纹理坐标也构成一个三角形(B)。
计算步骤:
1、遍历每个像素(P)时,先判断这个像素属于一群B三角形中的哪个三角形。
2、然后结合这个像素坐标P以及三角形B算出这个像素对应的重心坐标。
3、然后在结合重心坐标与这个像素P算出这个点在三角形A中的位置。
上述算法的默认条件是:认定三角形A与三角形B一致。然后在利用重心坐标进行差值计算
代码:
因为我需要存储到sqlite3的数据库,所以会用到sqlite3
import bpy
import sqlite3dbpath = 'D:/javascriptResult/write.db'
# 链接数据库的句柄
db = sqlite3.connect(dbpath)
# cursor对象
cur = db.cursor()
# 存储要写入数据库的数据
sql_value = []
# 获取当前激活的对象:
obj = bpy.data.objects["test"]
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
# 2. 获取对象的网格数据:
mesh = obj.data
# 3. 获取纹理图像数据,以及纹理图像的宽度和高度:
image = bpy.data.images["test.jpg"]
pixels = image.pixels
width, height = image.sizeuv_layers_data = obj.data.uv_layers.active.data# 获取三角面数据
face_data = obj.data.polygons# 获取模型的世界变换矩阵
world_mat = obj.matrix_world# 清空表格
def clearData():print('开始清空表')sql = 'delete from pixelmap where 1 = 1'try:cur.execute(sql)print('清空表成功')except Exception as e:print(e)print('清空表失败')def dissconnectDB():# 关闭游标cur.close()# 关闭连接db.close()print('断开数据库链接')# 执行sql创建表
def createTable():print('开始创建表')# 执行sql创建表sql = 'create table pixelmap(id INTEGER PRIMARY KEY AUTOINCREMENT,texture string,u float,v float,canvas string,x float,y float,z float)'try:cur.execute(sql)print('创建表成功')except Exception as e:print(e)print('创建表失败')def insertValueIntoTable(value):print('开始插入数据')try:# 执行sql创建表sql = 'insert into pixelmap(texture,u,v,canvas,x,y,z) values(?,?,?,?,?,?,?)'cur.executemany(sql, value)# 提交事务db.commit()print('插入成功')except Exception as e:print('插入失败')print(e)db.rollback()# 计算点在三角形中的重心坐标
def calculate_barycenter(point, points):x, y = point[0], point[1]p1, p2, p3 = points[0], points[1], points[2]denominator = (p2[1] - p3[1]) * (p1[0] - p3[0]) + (p3[0] - p2[0]) * (p1[1] - p3[1])alpha = ((p2[1] - p3[1]) * (x - p3[0]) + (p3[0] - p2[0]) * (y - p3[1])) / denominatorbeta = ((p3[1] - p1[1]) * (x - p3[0]) + (p1[0] - p3[0]) * (y - p3[1])) / denominatorgamma = 1.0 - alpha - betareturn (alpha, beta, gamma)def barycentric_to_cartesian(uvw, xyz):# xyz是三角形的三个顶点坐标,uvw是目标点的重心坐标p = xyz[0] * uvw[0] + xyz[1] * uvw[1] + xyz[2] * uvw[2]return p# 4. 对于每个像素点,找到它们所对应的三角形,并计算出该像素点在三角形中的重心坐标:
# 遍历每个像素点
createTable()
clearData()
for y in range(height):for x in range(width):# 获取像素点的颜色值r = pixels[(y * width + x) * 4]g = pixels[(y * width + x) * 4 + 1]b = pixels[(y * width + x) * 4 + 2]# 如果像素点的颜色不是全黑,则表示它在纹理贴图上有对应的三角形if r != 0 or g != 0 or b != 0:# 获取纹理坐标u, v = float(x/width), float(y/height)# 遍历所有面poly = Nonefor face in mesh.polygons:# 获取面的所有顶点vertices = []# 获取面的所有顶点对应的纹理坐标tex_coords = []loop_start = face.loop_startloop_end = face.loop_start + face.loop_totalfor loop_index in range(loop_start, loop_end):vertices_index = mesh.loops[loop_index].vertex_indextex_coords.append(uv_layers_data[loop_index].uv)vertices.append(world_mat @ mesh.vertices[vertices_index].co)# 计算重心坐标,判断该像素点是否在该三角面内barycenter = calculate_barycenter((u, 1-v), tex_coords)if (barycenter[0] > 0.0 and barycenter[1] > 0.0 and barycenter[2] > 0.0):poly = faceprint("Pixel ({},{})---In_face ({})".format(u, v, "true"))print("Pixel ({},{}) calcuate".format(u, v))# 如果找到了三角形,则计算该像素点在三角形中的重心坐标if poly is not None:point = barycentric_to_cartesian(barycenter, vertices)sql_value.append(('wlzj_1_ipg', u, 1.0-v, "wlzj_1_mesh", point.x, point.y, point.z))# 输出该像素点对应的坐标print("Pixel ({}, {}) maps to vertex ({}, {}, {})".format(u, 1-v, point.x, point.y, point.z))
insertValueIntoTable(sql_value)
dissconnectDB()