4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Blender の UV データが入り組んでいる件

Posted at

簡単な立体クイズ

image.png

 デフォルトキューブがあります。当然ながら頂点は 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個でしょうか。

 違います。

image.png

 展開図にしてみるとわかりますが、繋ぎ目になっている部分だけ、展開図上では切り離されて数が増えます。

 まあこんなひっかけは予想済ですよね。正解は 14 個!

 違います。

UV 頂点へのアクセス

 気になる正解の前に、まず UV 頂点のアクセスへの仕方について。

image.png

 Blender では、メッシュデータは「UVレイヤー」というデータを持っています。どのレイヤーの UV を使うのかを選択できます。正直、使い分けたことはないですし、エラーの温床にしかならないような気もしますが……。メッシュからuv_layersbpy.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)
console
<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 が常に連続であるとは限らない(非連続にもできる)からです。

 端的に説明すると、

image.png

 こうやって切り離すことも可能にするためです(理論上は、全てのエッジにシームが入る可能性がある以上、すべての面が UV 上で分離する可能性はあります)。

そもそも頂点は 8 個なのか

 こう考えると、当初の前提にあった、そもそも頂点は 8 個であるという認識も怪しくなってきます。実際に、polygonsからpolygon(面)のデータを辿ってみます。

for face in mesh.polygons:
    print(face)
output
<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_startloop_totalという属性があります。ループ開始の頂点番号とループ長とかと思いきや、ちょっと違います。

for face in mesh.polygons:
    print(face.loop_start,face.loop_total)
output
0 4
4 4
8 4
12 4
16 4
20 4

 各面ごとに、4 つずつ番号が増えていくことがわかります。頂点は 8 個しかないのに! ここを詳しく見ていくために、verticesという別の属性を見ていきます。

for face in mesh.polygons:
    print(*face.vertices)
output
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)
output
<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 マップに点が選択されないのかもわかります。

image.png
image.png
(面を選択して、初めて UV マップ上で選択した部分が現れる)

 空間上の 1 つの頂点に対して複数の UV 点が対応している可能性があるので、特定できないためです。面で選択すればそのような曖昧性はなくなるので、UV マップに即座に対応する部分が現れます。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?