Blender 2.92.0とPython3でトーラスが伸びたり縮んだりする動画を作る。🍩

By | 2021年4月24日 , Last update: 2022年8月7日

はじめに

Blender 2.80以降のバージョンはそれ以前のバージョンと比べていろいろと変わったと巷で噂になっていますが、Blenderが内蔵しているPython3を使ってトーラスをモデリングし、ついでに動画を作ってみることにしました。

スポンサーリンク

Blender 2.92.0のインストール

今回のモデリングはWindows 10にBlender 2.92.0をインストールして作業していきます。

さっそく、Blender 2.92.0をインストールします… といきたいところですが、インストールの前にそれまで使っていたBlenderの古いバージョンはスパッとアンインストールします。

古いバージョンのアンインストールが終わったら、本家BlenderのサイトからBlender 2.92.0をダウンロードします。

インストールはWindows 10のしきたりに則って行います。

トーラスとは?

作業を開始する前にここでトーラスについて簡単におさらいしておきます。

なお、以下の座標系についての記述は特に明示しない限りグローバル座標系によるものとします。

ここで、軸上の点を中心とする平面上の円を考えます。

こんな感じの円です↓

この円を軸のまわりで1回転させるとトーラスができます。

の方程式はの半径を, の中心と原点の距離をとすると…

(1)


スポンサーリンク

と表せるので、その回転体であるトーラスは(1)式のに置き換えることによって表すことができて…

(2)

と表すことができます。

割と簡単な式になりますね。

Python3のプログラムを書きます。

トーラスが回転体であることを理解したところで、その回転角の範囲でちょっとずつ変えてレンダリングし、その結果を画像ファイルとして出力するプログラムを書きます。

なお、Blender 2.92.0に同梱されているPython3のバージョンは3.7.7のようです。

プログラムの全貌

Python3のプログラム及び動画作成用のバッチファイルはGitHubのリポジトリに置きました。


スポンサーリンク

Python3のプログラムは以下の通りになります。

BlenderをPython3から操作する場合にはbpyモジュールというPython3のモジュールを使用しますが、bpyモジュールで使用できるAPIの仕様が2.79以前のものと2.80以降では一部変わっているようです(↑のプログラムは2.92.0でのみ動作確認を行っています)。

逐語訳のようなもの

前節のプログラムの概要を説明していきます。

10行目: 画像ファイルの出力先ディレクトリの設定

Blenderのテキストエディタから前節のプログラムを読み込んで実行した際に出力される画像ファイルの出力先のディレクトリが指定できないので、ここで無理矢理設定します。

13-15行目: 配置済みのオブジェクトをすべて消す

配置済みのオブジェクト(デフォルトの状態でBlenderを起動したときにすでに置いてある立方体を含みます。)をすべて消すための関数です。

18-24行目: 円を描く

平面上に絵を描くための関数です。


スポンサーリンク

以下のような処理の流れになっています。

  1. いったん平面に円を描きます(20行目)。
  2. 軸回りに回転させます(22行目)。
  3. 原点を3Dカーソル(このプログラムでは3Dカーソルを移動させないので、グローバル座標系の原点に3Dカーソルがあります。)に移動させます(23行目)。
  4. 軸回りに回転させます(24行目)。

回転させるときにはfill_typeの設定が反映されないようなので、回転角がでないときの端のところにフタをする円板はこの関数を別途呼び出してオブジェクトを追加します。

27-54行目: トーラスを作る

トーラスを作ります。

  1. 円を回転させて「曲がった土管」の部分を作ります(28-41行目)。
  2. 円を2個追加します(43-45行目)。
  3. 手順2で追加した円を土管にフタができる位置まで回転します(47-48行目)。
  4. オブジェクトをメッシュに変換します(50-52行目)。
  5. 「曲がった土管」及びフタを1個のオブジェクトに変換します(54行目)。

コメントにも簡単に書きましたが、プリファレンスの「翻訳」の設定で「日本語」を指定した場合で、モディファイアの設定を行う際には、bpy.context.object.modifiersでアクセスできるdictのキー名にモディファイアの日本語名を設定する必要があるようです。

57-63行目: 色の指定

生成されるトーラスごとにその表面の色を指定します。

自然な感じの色が生成できるような実装としています。

66-87行目: 画像をサクサクと生成する

トーラスの中心角を変えながらレンダリングを行い、PNGフォーマットの連番画像を作成します。

90-92行目: レンダリングした画像を1枚だけ作る関数

テスト用の関数です。

ちょっと脱線: 途中までなら動くやつ

スポンサーリンク

前節の関数を作る際にBlenderの3Dビューで様子を見ながらプログラムを書くのですが…

BMeshをつかって土管を作成するところまで作ってみたプログラムはこちらになります↓

#!/usr/bin/env python3
#
# See https://pandanote.info/?p=7456
#
import bpy
import bmesh
import math
def verts_to_ngon(edgeslist, verts):
converted = []
verts.append(verts[0])
pos = 0
while pos < len(verts)-1:
vs = verts[pos]
ve = verts[pos+1]
for edge in edgeslist:
if (edge.verts[0] == vs and edge.verts[1] == ve) \
or (edge.verts[0] == ve and edge.verts[1] == vs):
converted.append(edge)
pos = pos + 1
return converted
for item in bpy.data.meshes:
bpy.data.meshes.remove(item)
bpy.ops.mesh.primitive_circle_add(location=(0.9, 0, 0), radius=0.3,
fill_type='NGON')
bpy.ops.transform.rotate(value=math.pi/2, orient_axis='X')
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
bpy.ops.object.modifier_add(type='SCREW')
scr = bpy.context.object.modifiers["スクリュー"]
scr.angle = math.pi*0.33333
scr.axis = 'Y'
bpy.ops.object.convert(target='MESH')
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(bpy.context.object.data)
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
ccount = {}
for edge in bm.edges:
edge.verts[0].select = False
edge.verts[1].select = False
edge.select = False
if edge.verts[0] not in ccount:
ccount[edge.verts[0]] = []
ccount[edge.verts[0]].append(edge.verts[1])
if edge.verts[1] not in ccount:
ccount[edge.verts[1]] = []
ccount[edge.verts[1]].append(edge.verts[0])
candidatelist = {}
for (k, v) in ccount.items():
if len(v) <= 3:
candidatelist[k] = v
cap_a = list(candidatelist.keys())
cap_b = []
isDone = False
cap_b.append(cap_a.pop())
current = cap_b[0]
while isDone is False:
next = None
for vv in candidatelist[current]:
if vv in cap_a:
next = vv
if next is not None:
cap_b.append(next)
cap_a.remove(next)
current = next
else:
isDone = True
edge_a = verts_to_ngon(bm.edges, cap_a)
edge_aa = []
lq = int(len(edge_a)/4)
lh = int(len(edge_a)/2)
for i in range(lq):
edge_a[i].select = True
edge_aa.append(edge_a[i])
for i in range(lq):
edge_a[i+lh].select = True
edge_aa.append(edge_a[i+lh])
# bmesh.ops.grid_fill(bm, edges=edge_aa)

↑のプログラムをBlenderのテキストエディタに読み込ませて実行させると、↓のような曲がった土管みたいな立体ができます。

これでフタをすれば完成かな… と思い、↑のプログラムの94行目のコメントを外してBlenderのテキストエディタに読み込ませて実行させてから3Dビューの画面に切り替えると、Blenderが異常終了してしまいます(試しにLinux (Fedora 33)でも実行してみましたが、core dumpします。)…

実は、BlenderのGUIからは「面」→「グリッドフィル」を選択すると…

のようにフタを作ることができたりします。

なんでなんですかね。(´・ω・`)

動画にします。

動画の作成

ちょっと話が脇道に逸れましたが、前々節のプログラムで大量に作成したPNGファイルをffmpegでまとめて動画にします。

動画をまとめるためのバッチファイルは↓のような感じです。

動作確認

出来上がった動画は↓のような感じになります。

まとめ

最初は選挙の開票速報とかで得票率とか獲得議席数を示すときによく使われている扇形の中心角が広がっていくタイプの3次元のパイチャートを作ろうと思っていたところ、「Blenderを使うのだったら、もっとクールな立体にしよう。」と考え、最終的に目をつけたのがトーラスだったってわけです。

Blenderはコマンドラインオプションを使うとGUIを起動せずにレンダリングができます。

この記事に掲載したプログラムについてももう少し汎用性を高めることができるかもしれませんが、その件はまたの機会にいたしとう存じます。(・ω・)

この記事は以上です。