如何在 Blender 导出脚本中导出每个顶点的 UV 坐标
Posted
技术标签:
【中文标题】如何在 Blender 导出脚本中导出每个顶点的 UV 坐标【英文标题】:how to export per-vertex UV coordinates in Blender export script 【发布时间】:2012-10-30 21:54:01 【问题描述】:我正在从 Blender 2.64 编写一个 python 导出脚本。这个想法是我试图以对 OpenGL VBO 友好的方式导出网格数据。所以我在结构布局数组中导出顶点属性。例如,对于具有顶点、法线和一对纹理坐标的网格,VBO 中的每个 vertexAttribute
将是 8 个连续的浮点数:
vvvnnntt
到目前为止一切顺利。问题是,当 Blender 进行 UV 映射时,它实际上可以将不同的 uv 分配给同一个顶点。
也就是说,假设您有一个立方体:您有 8 个顶点,并且您有 6 个面(在本例中为四边形)。我本来期望索引为 0,1,2,3 的面/多边形 暗示:
vertex 0, normal 0, uvCoord0
vertex 1, normal 1, uvCoord1
vertex 2, normal 2, uvCoord2
vertex 3, normal 3, uvCoord3
因此,例如,任何提及索引 0 的任何面孔,都将始终暗示
顶点 0,法线 0,uvCoord0 元组。好吧,结果在 Blender 中,如果我理解正确的话,一张脸可以用 uvCoord 0 引用顶点 0,而另一张脸可能用不同的 uVCoord 引用相同的顶点 0。因此,人脸的 loop_indices 实际上必须用于在对象通用 data.vertices
和 data.uv_layers[].data
容器中查找向量和 uvCoord。
这允许为每个面应用 uv 贴图。所以你可以有一个立方体,每个面都应用了不同的 uv 纹理,即使两个相邻的面共享一个顶点,顶点也会根据面有不同的 uv 坐标。
不过,我的网格不应该对同一个顶点有不同的 uv,因为我正在展开相邻的面。这意味着在我的 UV 贴图中,展开的网格是一组相邻的面(例如,如果它是由 6 个面组成的立方体,则考虑一个十字形)并且在两个相邻的面之间,它们的公共顶点应该映射到同一个点在 uv 贴图中。
鉴于上述情况,我认为这种方法应该可行:
vertexAttributeList = []
for vertex in mesh.data.vertices:
vertexAttribute = list(vertex.co)
vertexAttribute.extend(list(vertex.normal))
vertexAttributeList.append(vertexAttribute)
for triangle in mesh.data.polygons:
for uv_layer in mesh.data.uv_layers:
for i in triangle.loop_indices:
lookupIndex = mesh.data.loops[i].vertex_index
if len(vertexAttributeList[lookupIndex]) == 6:
uvCoord = uv_layer.data[i].uv
vertexAttributeList[lookupIndex].extend([uvCoord[0], 1 - uvCoord[1]])
如您所见,上面代码中的含义是我将多次访问顶点,因为我正在迭代共享顶点的网格面(在本例中为三角形)。每次我访问一个顶点时,如果它还没有分配它的 UV 坐标,那么我通过使用三角形的 loop_indices 查找它们来分配它们。毕竟,我的假设是,在一天结束时,每个顶点都有唯一的 uv 坐标。
上面的代码给出了以下布局,例如(我正在显示网格的前 6 个顶点属性):
-1.000000 -1.000000 -1.000000 -0.707083 -0.707083 0.000000 0.076381 0.948520
-1.000000 1.000000 -1.000000 -0.707083 0.707083 0.000000 0.454183 0.948519
1.000000 1.000000 -1.000000 0.707083 0.707083 0.000000 0.325162 0.948519
1.000000 -1.000000 -1.000000 0.707083 -0.707083 0.000000 0.205674 0.948519
-1.000000 -1.000000 1.000000 -0.577349 -0.577349 0.577349 0.581634 0.795012
-1.000000 1.000000 1.000000 -0.577349 0.577349 0.577349 0.454183 0.795012
...
但是当我使用这个信息加上网格的面时,我的布局是这样的:
4 5 1
5 6 2
6 7 3
7 4 0
...
为了在我的程序(某种引擎)中渲染我的模型,uv 映射显然搞砸了。也就是说,模型在顶点和法线方面渲染得很好,但 uv 纹理显然没有正确映射。
有什么想法吗?我的意思是,要么我导出正确并弄乱了我的应用程序中的 OpenGL 渲染代码,要么我导出了顶点和 uv 坐标之间的错误映射。 (或者两者兼而有之,当然......但我现在假设我正在搞乱导出脚本)。
最后一件事,如果我更改上述 python 代码以附加分配给顶点的每个新 uv,而不是仅在尚未分配 uv 时才附加,对于顶点 1,我得到:
[-1.0, -1.0, -1.0, -0.7070833444595337, -0.7070833444595337, 0.0, 0.07638061791658401, 0.9485195726156235, 0.5816344618797302, 0.9485194832086563, 0.07638061791658401, 0.9485195726156235]
请注意,此示例中只有一个 uv 层。很明显,Blender 确实为顶点 1 分配了 2 个 uv 坐标。
【问题讨论】:
顺便说一句,与此同时,我采用了“无 GL_ELEMENT_ARRAY”方法,因此我最终得到了重复的顶点但具有不同的 uv,而我现在有一个巨大的 VBO,其中顶点显示的数量也一样多当三角形引用它们时,uvs mapp 在我渲染时就好了。 【参考方案1】:我认为可能有一种方法可以插入或以其他方式协调/混合 UV,以便在所有内容都相邻的情况下最终每个顶点都有一个 UV。但与此同时,鉴于没有人提出替代方案,我最终复制了具有不同 UV 的顶点,并放弃了为 GL_ELEMENT_ARRAY 导出的尝试。如果使用单个 VBO(使用 glDrawArrays)进行渲染,则以下代码有效:
def exportMesh(filepath):
# Only one mesh per scene
objList = [object for object in bpy.context.scene.objects if object.type == 'MESH']
if len(objList) == 0:
return
elif len(objList) > 1:
return
#raise exepction? dialog box?
# Process the single mesh object:
mesh = objList[0]
# File name is same as the mesh's name in Blender
meshFilePath = filepath[0 : filepath.rindex('/') + 1] + mesh.name + ".mesh"
file = open(meshFilePath, 'w')
WorldTransform = Matrix().Identity(4)
WorldTransform *= Matrix.Rotation(radians(90), 4, "X")
file.write('World Transform:\n')
for rcol in WorldTransform_T.row:
file.write(':9f :9f :9f :9f\n'.format(row[0], row[1], row[2], row[3]))
file.write('\n')
# Mesh (local) transform matrix
file.write('Mesh Transform:\n')
localTransform_T = mesh.matrix_local.copy()
localTransform_T.transpose()
for row in localTransform_T.row:
file.write(':9f :9f :9f :9f\n'.format(row[0], row[1], row[2], row[3]))
file.write('\n')
vertexAttributeList = []
for triangle in mesh.data.polygons:
vertices = list(triangle.vertices)
i = 0
for vertex in vertices:
vertexAttribute = list(mesh.data.vertices[vertex].co)
if triangle.use_smooth:
vertexAttribute.extend(list(mesh.data.vertices[vertex].normal))
else:
vertexAttribute.extend(list(triangle.normal))
for uv_layer in mesh.data.uv_layers:
uvCoord = uv_layer.data[triangle.loop_indices[i]].uv
vertexAttribute.extend([uvCoord[0], 1 - uvCoord[1]])
totalVertexWeight = 0
jointWeights = [group.weight for group in mesh.data.vertices[vertex].groups]
jointIndices = [group.group for group in mesh.data.vertices[vertex].groups]
for weight in jointWeights:
totalVertexWeight += weight
vgNum = len(mesh.vertex_groups)
jointWeightsAttribute = []
jointIndicesAttribute = []
for vgIndex in range(4):
if vgIndex < len(jointIndices):
jointWeightsAttribute.append(jointWeights[vgIndex] / totalVertexWeight)
jointIndicesAttribute.append(jointIndices[vgIndex])
else:
jointWeightsAttribute.append(0)
jointIndicesAttribute.append(0)
vertexAttribute.extend(jointWeightsAttribute)
vertexAttribute.extend(jointIndicesAttribute)
vertexAttributeList.append(vertexAttribute)
i += 1
# VBO
vNum = len(vertexAttributeList)
tNum = len(mesh.data.uv_layers)
file.write('VBO Length: :d\n'.format(vNum))
for vertexAttribute in vertexAttributeList:
file.write(':9f :9f :9f :9f :9f :9f :9f :9f :9f :9f :9f :9f :d :d :d :d\n'.format(vertexAttribute[0],
vertexAttribute[1],
vertexAttribute[2],
vertexAttribute[3],
vertexAttribute[4],
vertexAttribute[5],
vertexAttribute[6],
vertexAttribute[7],
vertexAttribute[8],
vertexAttribute[9],
vertexAttribute[10],
vertexAttribute[11],
vertexAttribute[12],
vertexAttribute[13],
vertexAttribute[14],
vertexAttribute[15]))
file.write('\n')
# Done writing mesh file
file.close()
【讨论】:
以上是关于如何在 Blender 导出脚本中导出每个顶点的 UV 坐标的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Blender 中导出 fbx 纹理以在 Monogame 中使用
创建一个弹出菜单来调用自定义导出器(Blender 2.6 API)?