前提:
纹理在模型上贴好后,能使用blender python api直接获取的就是,这个模型的每个三角面片上顶点对应的纹理坐标。这其中每个三角面的顶点构成一个三角形(A),每个三角面的顶点对应的纹理坐标也构成一个三角形(B)。(注:实际上blender常用的是四边形,所以处理时要把四边形分成两个三角形)
计算步骤:
1、遍历每个像素(P)时,先判断这个像素属于一群B三角形中的哪个三角形。
2、然后结合这个像素坐标P以及三角形B算出这个像素对应的重心坐标。
3、然后在结合重心坐标与这个像素P算出这个点在三角形A中的位置。
上述算法的默认条件是:认定三角形A与三角形B一致。然后在利用重心坐标进行差值计算
代码:
因为我需要存储到sqlite3的数据库,所以会用到sqlite3
import bpy
import sqlite3
import bmesh
dbpath = '/Users/wxzjr/file/uv_xyz.db'
# 链接数据库的句柄
db = sqlite3.connect(dbpath)
# cursor对象
cur = db.cursor()
# 存储要写入数据库的数据
sql_value = []
# 获取当前激活的对象:
obj = bpy.data.objects["nan_right"]
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
# 2. 获取对象的网格数据:
mesh = obj.data
# 3. 获取纹理图像数据,以及纹理图像的宽度和高度:
image = bpy.data.images["texture_20001.jpg"]
pixels = image.pixels
width, height = image.size
print(width,height)
uv_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])) / denominator
beta = ((p3[1] - p1[1]) * (x - p3[0]) + (p1[0] - p3[0]) * (y - p3[1])) / denominator
gamma = 1.0 - alpha - beta
return (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()
total_load = 0
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 = None
for face in mesh.polygons:
# 获取面的所有顶点
vertices = []
# 获取面的所有顶点对应的纹理坐标
tex_coords = []
loop_start = face.loop_start
loop_end = face.loop_start + face.loop_total
if face.loop_total == 4:
# 第一个三角面
vertices_0 = mesh.loops[face.loop_start].vertex_index
tex_coords.append(uv_layers_data[face.loop_start].uv)
vertices.append(world_mat @ mesh.vertices[vertices_0].co)
vertices_1 = mesh.loops[face.loop_start+1].vertex_index
tex_coords.append(uv_layers_data[face.loop_start+1].uv)
vertices.append(world_mat @ mesh.vertices[vertices_1].co)
vertices_2 = mesh.loops[face.loop_start+2].vertex_index
tex_coords.append(uv_layers_data[face.loop_start+2].uv)
vertices.append(world_mat @ mesh.vertices[vertices_2].co)
vertices_3 = mesh.loops[face.loop_start+3].vertex_index
# 计算重心坐标,判断该像素点是否在该三角面内
barycenter = calculate_barycenter((u, 1-v), tex_coords)
if (barycenter[0] >= 0.0 and barycenter[1] >= 0.0 and barycenter[2] >= 0.0):
poly = face
break
# 第二个三角面
vertices = []
tex_coords = []
tex_coords.append(uv_layers_data[face.loop_start].uv)
vertices.append(world_mat @ mesh.vertices[vertices_0].co)
tex_coords.append(uv_layers_data[face.loop_start+2].uv)
vertices.append(world_mat @ mesh.vertices[vertices_2].co)
tex_coords.append(uv_layers_data[face.loop_start+3].uv)
vertices.append(world_mat @ mesh.vertices[vertices_3].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 = face
break
else:
for loop_index in range(loop_start, loop_end):
vertices_index = mesh.loops[loop_index].vertex_index
tex_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 = face
# 如果找到了三角形,则计算该像素点在三角形中的重心坐标
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))
else:
print("Pixel {},{} not in face)".format(x,y))
insertValueIntoTable(sql_value)
dissconnectDB()