簡単な立体クイズ
デフォルトキューブがあります。当然ながら頂点は 8 個、面は 6 個あります。
mesh = bpy.data.meshes['Cube']
for vert in mesh.vertices:
print(vert.co)
<Vector (1.0000, 1.0000, 1.0000)>
<Vector (1.0000, 1.0000, -1.0000)>
<Vector (1.0000, -1.0000, 1.0000)>
<Vector (1.0000, -1.0000, -1.0000)>
<Vector (-1.0000, 1.0000, 1.0000)>
<Vector (-1.0000, 1.0000, -1.0000)>
<Vector (-1.0000, -1.0000, 1.0000)>
<Vector (-1.0000, -1.0000, -1.0000)>
さて、このキューブの UV 頂点は何個あるでしょうか?
頂点と1対1対応しているので、8個でしょうか。
違います。
展開図にしてみるとわかりますが、繋ぎ目になっている部分だけ、展開図上では切り離されて数が増えます。
まあこんなひっかけは予想済ですよね。正解は 14 個!
違います。
UV 頂点へのアクセス
気になる正解の前に、まず UV 頂点のアクセスへの仕方について。
Blender では、メッシュデータは「UVレイヤー」というデータを持っています。どのレイヤーの UV を使うのかを選択できます。正直、使い分けたことはないですし、エラーの温床にしかならないような気もしますが……。メッシュからuv_layers
でbpy.types.MeshUVLoopLayer
というデータへのリストにアクセスできます。(余談ながら、bpy_prop_collection
という表示が出てきた時は、なんらかのリストだということを覚えておくと、API の発掘がしやすいかも)
mesh = bpy.data.meshes['Cube']
layer = mesh.uv_layers[0]
# <bpy_collection[1], UVLoopLayers>
print(layer)
# <bpy_struct, MeshUVLoopLayer("UVMap") at 0x0000017152B3A208>
リファレンスを見ると、data
という属性でbpy.types.MeshUVLoop
のリストにアクセスできることがわかります。
uv_verts = layer.data
print(uv_verts)
#<bpy_collection[24], MeshUVLoopLayer.data>
という訳で答えは 24 でした。本当かよ、ということで中身の座標も見ていきます。
for vert in uv_verts:
print(vert.uv)
<Vector (0.6250, 0.5000)>
<Vector (0.8750, 0.5000)>
<Vector (0.8750, 0.7500)>
<Vector (0.6250, 0.7500)>
<Vector (0.3750, 0.7500)>
<Vector (0.6250, 0.7500)>
<Vector (0.6250, 1.0000)>
<Vector (0.3750, 1.0000)>
<Vector (0.3750, 0.0000)>
<Vector (0.6250, 0.0000)>
<Vector (0.6250, 0.2500)>
<Vector (0.3750, 0.2500)>
<Vector (0.1250, 0.5000)>
<Vector (0.3750, 0.5000)>
<Vector (0.3750, 0.7500)>
<Vector (0.1250, 0.7500)>
<Vector (0.3750, 0.5000)>
<Vector (0.6250, 0.5000)>
<Vector (0.6250, 0.7500)>
<Vector (0.3750, 0.7500)>
<Vector (0.3750, 0.2500)>
<Vector (0.6250, 0.2500)>
<Vector (0.6250, 0.5000)>
<Vector (0.3750, 0.5000)>
これを注意深く数えると(実際に数えなくていいです)、一部重複しているものがあることがわかります。先程の「14個」はこの、重複を除いたものと考えることができます。つまり、すべての面の4隅に、(例え座標が重複していようが)頂点のデータは設定されていたのでした。
なぜこのようになっているのか?
それは UV が常に連続であるとは限らない(非連続にもできる)からです。
端的に説明すると、
こうやって切り離すことも可能にするためです(理論上は、全てのエッジにシームが入る可能性がある以上、すべての面が UV 上で分離する可能性はあります)。
そもそも頂点は 8 個なのか
こう考えると、当初の前提にあった、そもそも頂点は 8 個であるという認識も怪しくなってきます。実際に、polygons
からpolygon
(面)のデータを辿ってみます。
for face in mesh.polygons:
print(face)
<bpy_struct, MeshPolygon at 0x0000017152BB0458>
<bpy_struct, MeshPolygon at 0x0000017152BB0464>
<bpy_struct, MeshPolygon at 0x0000017152BB0470>
<bpy_struct, MeshPolygon at 0x0000017152BB047C>
<bpy_struct, MeshPolygon at 0x0000017152BB0488>
<bpy_struct, MeshPolygon at 0x0000017152BB0494>
6 枚あります。これはさすがに異論の余地はないと思います。これにはそれぞれloop_start
とloop_total
という属性があります。ループ開始の頂点番号とループ長とかと思いきや、ちょっと違います。
for face in mesh.polygons:
print(face.loop_start,face.loop_total)
0 4
4 4
8 4
12 4
16 4
20 4
各面ごとに、4 つずつ番号が増えていくことがわかります。頂点は 8 個しかないのに! ここを詳しく見ていくために、vertices
という別の属性を見ていきます。
for face in mesh.polygons:
print(*face.vertices)
0 4 6 2
3 2 6 7
7 6 4 5
5 1 3 7
1 0 2 3
5 4 0 1
ここでやっと頂点番号らしきものが出てきました。座標を確認してみます。(*
についてはPythonでタプルやリストをアンパック(複数の変数に展開して代入)を参考)
vertices = mesh.vertices
for face in mesh.polygons:
for vert in face.vertices:
print(vertices[vert].co)
<Vector (1.0000, 1.0000, 1.0000)>
<Vector (-1.0000, 1.0000, 1.0000)>
<Vector (-1.0000, -1.0000, 1.0000)>
<Vector (1.0000, -1.0000, 1.0000)>
<Vector (1.0000, -1.0000, -1.0000)>
<Vector (1.0000, -1.0000, 1.0000)>
<Vector (-1.0000, -1.0000, 1.0000)>
<Vector (-1.0000, -1.0000, -1.0000)>
<Vector (-1.0000, -1.0000, -1.0000)>
<Vector (-1.0000, -1.0000, 1.0000)>
<Vector (-1.0000, 1.0000, 1.0000)>
<Vector (-1.0000, 1.0000, -1.0000)>
<Vector (-1.0000, 1.0000, -1.0000)>
<Vector (1.0000, 1.0000, -1.0000)>
<Vector (1.0000, -1.0000, -1.0000)>
<Vector (-1.0000, -1.0000, -1.0000)>
<Vector (1.0000, 1.0000, -1.0000)>
<Vector (1.0000, 1.0000, 1.0000)>
<Vector (1.0000, -1.0000, 1.0000)>
<Vector (1.0000, -1.0000, -1.0000)>
<Vector (-1.0000, 1.0000, -1.0000)>
<Vector (-1.0000, 1.0000, 1.0000)>
<Vector (1.0000, 1.0000, 1.0000)>
<Vector (1.0000, 1.0000, -1.0000)>
座標は 8 種類しかないですが、頂点のデータとしては 24 個あることがわかります。UV 座標の時は 14 種類まで重複していました。
結論
面が存在する以上、データ上の頂点(bpy.types.MeshPolygon
)はそれを囲む頂点の数(多くの場合は 3 か 4)だけ存在します。それらの3次元空間上の位置はいわゆる「頂点」(bpy.types.MeshVertex
)として存在しますが、複数のMeshPolygon
から同一のMeshVertex
が指定されることもあります。というか、ポリゴンは普通つながっているので、そちらの方が圧倒的に多いです。
「頂点」の考え方によりますが、デフォルトキューブの頂点は 8 個ではなく、24 個だが 8 種類である、といった方が適切なのかもしれません。(例えば、頂点カラーは面が繋がっていても、接する面によって違う色に塗ることもできます)
また、UV マップ上の 2 次元空間上の位置はMeshUVLoop
の中のuv
という属性で管理されています。どの(3次元空間の)点がどの(UV マップ上の)点に対応するかは、MeshPolygon で示されたループの通りに記述されており、この対応関係が 3 次元空間のメッシュと 2 次元の UV マップをつなぐ、画像テクスチャの重要な部分となります。
おまけ
これらのことがわかると、3D ビューで頂点を選択するだけではなぜ UV マップに点が選択されないのかもわかります。
(面を選択して、初めて UV マップ上で選択した部分が現れる)
空間上の 1 つの頂点に対して複数の UV 点が対応している可能性があるので、特定できないためです。面で選択すればそのような曖昧性はなくなるので、UV マップに即座に対応する部分が現れます。