「Blender」は、3Dグラフィックやアニメーション制作などで広く活用されている、オープンソースの無料ソフトウェアです。BlenderはPythonスクリプトをサポートしているため、3Dオブジェクトの制作をプログラミングで自動化できます。
グラフィック制作には、さまざまな反復作業や複雑な操作が必要な部分があります。Pythonスクリプトの機能を活用すれば、初心者でも気軽に3Dモデリングを楽しんだり、制作を効率化したりすることが可能です。本記事では、BlenderのPythonスクリプトの方法や、各機能の設定手順などについて解説します。
目次
Pythonによる「Blender自動化」を行うために必要となる基礎知識について、まずは以下の3つのポイントに分けて解説します。
「Blender」とは、オランダの「Blender Foundation」が開発・提供している、オープンソースの3DCGソフトウェアです。誰でも無料で使用できるうえに、プロフェッショナルな用途でも通用するほど機能性が高いことが魅力。そのため、ゲーム・アニメーション・映画などの3Dモデルやアニメーション制作など、さまざまな用途に活用されています。
Blenderは、プログラミング言語「Python」で開発したスクリプトを利用して、さまざまな操作を自動化できます。「Blender Python API」には、ソースコードでマウスやキーボードで行う操作を再現するための、さまざまな機能が備わっています。
Blender Python APIでは、3Dオブジェクトの生成や配置はもちろん、オブジェクトの質感を設定するための「マテリアル」の設定など、手動で行うと手間がかかる部分を自動化できることがメリットです。このように、Pythonで作成された自動化機能を「アドオン」と呼び、多種多様なものが公開されています。たとえば、人体の3Dモデルを簡単に作成し、アニメーションができるアドオンなどです。
Blender Python APIを活用すると、アドオンを自作して、便利な機能を実装できます。PythonやBlender Python APIの基本知識は必要になりますが、うまく活用すると3DCG制作を大幅に効率化できるでしょう。
BlenderでPythonスクリプトを利用するためには、まずBlender本体をインストールする必要があります。Blenderの公式サイトにアクセスして、OSに適合するダウンロードボタンをクリックしましょう。Blenderをインストールするために必要なファイルがダウンロードされます。
ダウンロードしたファイルをクリックすると、以下のようにインストーラーが起動するため、画面の表示どおりに進めてください。「Next」をクリックすると、次の項目に進みます。
ライセンス条項に同意すると、インストールする内容を選択する画面が表示されるので、そのまま「Next」をクリックしましょう。「Install」を選択すると、Blenderのインストールが完了します。Blenderを起動すると、以下のような初期設定画面が表示されるので、言語を「日本語」に変更してから進むと、日本語でBlenderが使えるようになります。
Pythonスクリプトは、Blender本体で作成・実行できます。画面上部のメニューから「スクリプト作成(Scripting)」を選ぶと、以下のようなPythonスクリプトの画面が表示されます。
「+新規」というボタンをクリックすると、以下のようにソースコードの入力画面が表示されます。Blenderには、デフォルトでPythonの開発環境が含まれているため、これだけの手順で、BlenderでPythonスクリプトを作成・実行できるようになります。
Pythonスクリプトの画面が開いたら、以下のソースコードをコピー&ペーストで入力して実行しましょう。なお、テキストエディタの上部にある三角形の「再生ボタン」をクリックすると、Pythonスクリプトを実行できます。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # numpyライブラリを「np」というエイリアスでimportする import numpy as np # random_position()|ランダムな座標値を生成する def random_position(min, max): return (max - min) * np.random.rand(3) + min # random_size()|ランダムなサイズを生成する def random_size(center, scale): return np.random.normal(center, scale) # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # ICOスフィアの数を設定する num = 300 # 座標の最小値、最大値を設定する min = -3.0 max = +3.0 # サイズの平均値、標準偏差を設定する center = 0.5 scale = 0.25 # すべてのICOスフィアをランダムに生成する for i in range(0, num): bpy.ops.mesh.primitive_ico_sphere_add(radius = random_size(center, scale) / 2.0, location = random_position(min, max), subdivisions = 4) // 実行結果
上記のサンプルプログラムは、300個の球体がランダムな位置・サイズで表示されているのが理解できるでしょう。なお、Blenderで使用できる重要な関数については、後ほど詳しく解説します。
Blenderには、基本的な3Dモデルである「プリミティブ」を生成するための、関数・機能が備わっています。ここでは、代表的なプリミティブ生成関数について、以下の9つを解説しましょう。
「primitive_circle_add()」は、サークル(円)を生成するための関数・機能で、以下の構文で利用できます。
bpy.ops.mesh.primitive_circle_add(vertices = 頂点数, radius = 半径, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), fill_type = "塗りつぶし方法")
「vertices引数」には、サークルの頂点数を指定します。この値が大きいほど綺麗な円になり、小さいと角ばった図形になります。「radius引数」は円の半径、「location引数」は中心座標、「rotation引数」は回転量を設定するためのものです。「fill_type」は塗りつぶしの手法で、「”NOTHING”」「”NGON”」「”TRIFAN”」の3種類から選びます。「”NOTHING”」にすると面が表示されなくなるので、これ以外を指定するのが基本です。
なお、後述する多くの関数は、primitive_circle_add()と同じ名前の引数を持ちます。同名の引数の機能は、とくに解説しない限りは上記のものと同じなので、ぜひ覚えておいてください。primitive_circle_add()関数の使い方は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「サークル」オブジェクトを生成する # 頂点数64・半径5.0・塗りつぶしNGON bpy.ops.mesh.primitive_circle_add(vertices = 64, radius = 5.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), fill_type = "NGON") // 実行結果
primitive_circle_add()では、「fill_type引数」のデフォルト設定が「”NOTHING”」になっているため、「”NGON”」もしくは「”TRIFAN”」を指定することが重要です。
「primitive_cone_add()」は、コーン(円錐台)を生成するための関数・機能で、以下の構文で利用できます。
bpy.ops.mesh.primitive_cone_add(vertices = 頂点数, radius1 = 下部の半径, radius2 = 上部の半径, depth = 深さ, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), scale = (X軸スケール, Y軸スケール, Z軸スケール))
「radius1引数」は下部(下底)の円の半径、「radius2引数」は上部(上底)の円の半径を設定します。「depth引数」はコーンの深さ(高さ)を設定するためのものです。「scale引数」は、コーンのサイズを指し、全体的な大きさを調整できます。primitive_cone_add()の使い方は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「コーン」オブジェクトを生成する # 頂点数64・下部の半径3.0・上部の半径1.0・深さ2.0・スケールは全軸3.0 bpy.ops.mesh.primitive_cone_add(vertices = 64, radius1 = 3.0, radius2 = 1.0, depth = 2.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), scale = (3.0, 3.0, 3.0)) // 実行結果
「primitive_cube_add()」は、キューブ(立方体)を生成するための関数・機能で、以下の構文で利用できます。
bpy.ops.mesh.primitive_cube_add(size = サイズ, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), scale = (X軸スケール, Y軸スケール, Z軸スケール))
引数の使い方については、これまで解説した「primitive_circle_add()」や「primitive_cone_add()」の同名の引数と同じです。primitive_cube_add()の使い方を、以下のサンプルコードで確認しましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「キューブ」オブジェクトを生成する # サイズ3.0 bpy.ops.mesh.primitive_cube_add(size = 3.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), scale = (1.0, 1.0, 1.0)) // 実行結果
なおprimitive_cube_add()関数は、基本的には立方体を生成するための機能ですが、「scale引数」を調整することで直方体にも変形できます。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「キューブ」オブジェクトを生成する # サイズ3.0・スケールはX軸0.5倍・Y軸3.0倍・Z軸1.5倍として直方体に変形する bpy.ops.mesh.primitive_cube_add(size = 3.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), scale = (0.5, 3.0, 1.5)) // 実行結果
scale引数の活用で、オブジェクトを自由に変形できることは、ほかのプリミティブ生成関数でも同じなので、ぜひモデリングやデザインに活用してみましょう。
「primitive_cylinder_add()」は、シリンダー(円柱)を生成するための関数・機能で、以下の構文で利用できます。
bpy.ops.mesh.primitive_cylinder_add(vertices = 頂点数, radius = 半径, depth = 深さ, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), scale = (X軸スケール, Y軸スケール, Z軸スケール))
引数の使い方については、前述した関数群と同じです。primitive_cylinder_add()は、以下のサンプルコードのように使用します。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「シリンダー」オブジェクトを生成する # 頂点数64・半径3.0・深さ2.0・スケールは全軸3.0 bpy.ops.mesh.primitive_cylinder_add(vertices = 64, radius = 3.0, depth = 2.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), scale = (3.0, 3.0, 3.0)) // 実行結果
「primitive_ico_sphere_add()」は、ICOスフィア(球体)を生成するための関数・機能で、以下の構文で利用できます。なお、ICOスフィアは三角ポリゴンで構成する球体です。
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions = 頂点分割数, radius = 半径, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), scale = (X軸スケール, Y軸スケール, Z軸スケール))
「subdivisions引数」は、ポリゴンの分割数を指します。この値が大きいほど滑らかな球体になり、小さい場合は角ばった球体になります。subdivisionsの値を大きくするほど処理に時間がかかるため、基本的には10未満にするのが無難です。primitive_ico_sphere_add()の使い方について、以下のサンプルコードで確認しましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「ICOスフィア」オブジェクトを生成する # 頂点分割数5・半径3.0・スケールは全軸5.0 bpy.ops.mesh.primitive_ico_sphere_add(subdivisions = 5, radius = 3.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), scale = (5.0, 5.0, 5.0)) // 実行結果
なお、「subdivisions引数」の値を小さくすると、以下のようなポリゴン数が少ない角ばった球体となります。そのため、あえて荒い球体にしたい場合は2~3前後にして、滑らかにしたい場合は5~6前後にするといいでしょう。
「primitive_uv_sphere_add()」は、UVスフィア(球体)を生成するための関数・機能で、以下の構文で利用できます。なお、UVスフィアは緯度経度でポリゴンを分割して構成する球体です。
bpy.ops.mesh.primitive_uv_sphere_add(segments = 経度方向の頂点分割数, ring_count = 緯度方向の頂点分割数, radius = 半径, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), scale = (X軸スケール, Y軸スケール, Z軸スケール))
「segments引数」は経度方向の頂点分割数、「ring_count引数」は緯度方向の頂点分割数を設定します。これらの値が大きいほど滑らかで、小さければ角ばった球体となります。primitive_uv_sphere_add()の使い方は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「UVスフィア」オブジェクトを生成する # 経度方向の頂点分割数64・緯度方向の頂点分割数32・半径3.0・スケールは全軸5.0 bpy.ops.mesh.primitive_uv_sphere_add(segments = 64, ring_count = 32, radius = 3.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), scale = (5.0, 5.0, 5.0)) // 実行結果
なお、経度方向の分割数「segments引数」の値を小さくすると、以下のようになります。
一方、緯度方向の分割数「ring_count引数」の値を小さくすると、以下のようになります。
またスフィア(球体)にテクスチャを貼り付けたい場合は、UVスフィアを使用することが重要です。UVスフィアはテクスチャ座標も、経度・緯度方向に正確に分割されるため、1枚のテクスチャを綺麗にマッピングできます。一方、ICOスフィアはテクスチャ座標が乱れるため、テクスチャマッピングには向いていません。
「primitive_plane_add()」は、プレーン(平面)を生成するための関数・機能で、以下の構文で利用できます。
bpy.ops.mesh.primitive_plane_add(size = サイズ, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), scale = (X軸スケール, Y軸スケール, Z軸スケール))
各引数の使い方については、これまでの関数で解説したとおりです。primitive_plane_add()の使い方について、以下のサンプルコードで確認しましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「プレーン」オブジェクトを生成する # サイズ30.0 bpy.ops.mesh.primitive_plane_add(size = 30.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0), scale = (0.0, 0.0, 0.0)) // 実行結果
「primitive_torus_add()」は、トーラス(ドーナツ型)を生成するための関数・機能で、以下の構文で利用できます。
bpy.ops.mesh.primitive_torus_add(major_segments = 64, minor_segments = 16, major_radius = 5.0, minor_radius = 1.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0))
「major_segments」と「minor_segments」は、いずれも頂点の分割数を示す変数で、値が大きいほど表面が滑らかになります。「major_radius」と「minor_radius」は、それぞれ大きい部分・小さい部分の半径を設定できます。primitive_torus_add()の詳細は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「トーラス」オブジェクトを生成する # 大きい頂点の分割数64・小さい頂点の分割数16・大きい部分の半径5.0・小さい部分の半径1.0 bpy.ops.mesh.primitive_torus_add(major_segments = 64, minor_segments = 16, major_radius = 5.0, minor_radius = 1.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0)) // 実行結果
なお、頂点分割数の値を小さくすると、以下のようになります。「major_segments引数」と「minor_segments引数」の関係性がわかりやすいのではないでしょうか。
「mesh.primitive_monkey_add()」は、モンキー(猿型モデル)を生成するための関数・機能で、以下の構文で利用できます。このプリミティブは、Blenderならではのユニークな機能だといえるでしょう。
bpy.ops.mesh.primitive_monkey_add(size = サイズ, location = (中心座標X, 中心座標Y, 中心座標Z), rotation = (X軸回転, Y軸回転, Z軸回転), scale = (X軸スケール, Y軸スケール, Z軸スケール))
猿型の3Dモデルが生成されること以外は、ほかの関数と同じです。mesh.primitive_monkey_add()のサンプルコードを確認しましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「モンキー」オブジェクトを生成する # サイズ10.0・X軸回転-0.3・Z軸回転1.0 bpy.ops.mesh.primitive_monkey_add(size = 10.0, location = (0.0, 0.0, 0.0), rotation = (-0.3, 0.0, 1.0), scale = (0.0, 0.0, 0.0)) // 実行結果
Pythonによる3Dモデリングの基本的な知識・テクニックについて、以下の8つのものを解説します。
BlenderのPythonスクリプトでは、先ほど解説したプリミティブ生成関数を呼び出せば、新しいメッシュオブジェクトを追加できます。ただし、Blender Python APIの各種機能を利用するためには、以下のようにライブラリをimportするように注意しましょう。
# Blender関連のオブジェクトをimportする import bpy import math import bmesh
「bpy」はBlender Python APIの基本機能、「bmesh」はメッシュ関連を扱うためのライブラリです。「math」はPythonの標準ライブラリですが、Blenderでは数学演算を頻繁に行うため必須です。また、Blenderは起動時に以下のようなキューブが表示されているため、これを削除しないといけません。
既存オブジェクトの削除は、以下のようにforループを回すことで行えます。これは、スクリプトでオブジェクトを多数生成したあとで、一括で消去したいときなどにも便利です。
# すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item)
「bpy.data.meshes.remove()」と「bpy.data.materials.remove()」は、それぞれすべてのメッシュとマテリアルを削除するための関数です。今回は、先ほど解説したデフォルトのプリミティブ生成関数である、「primitive_torus_add()」を利用して、トーラス(ドーナツ)型のメッシュを追加します。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「トーラス」オブジェクトを生成する # 大きい頂点の分割数64・小さい頂点の分割数16・大きい部分の半径5.0・小さい部分の半径1.0 bpy.ops.mesh.primitive_torus_add(major_segments = 64, minor_segments = 16, major_radius = 5.0, minor_radius = 1.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0)) // 実行結果
オブジェクトの平行移動は、以下の構文で「bpy.ops.transform.translate()」関数を呼び出すことで行えます。
bpy.ops.transform.translate(value = (X軸方向の移動量, Y軸方向の移動量, Z軸方向の移動量))
引数には、タプル形式でX軸・Y軸・Z軸それぞれの移動量を指定します。bpy.ops.transform.translate()の使い方は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「トーラス」オブジェクトを生成する # 大きい頂点の分割数64・小さい頂点の分割数16・大きい部分の半径5.0・小さい部分の半径1.0 bpy.ops.mesh.primitive_torus_add(major_segments = 64, minor_segments = 16, major_radius = 5.0, minor_radius = 1.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0)) # オブジェクトを(1.0, 2.0, 3.0)に移動する bpy.ops.transform.translate(value = (1.0, 2.0, 3.0)) // 実行結果
オブジェクトの回転は、以下の構文で「bpy.ops.transform.rotate()」関数を呼び出すことで行えます。
bpy.ops.transform.rotate(value = math.radians(回転角度), orient_axis = "軸")
重要なポイントは、「value引数」で回転角度を「ラジアン」で指定することです。ラジアンとは、半径と等しい長さの弧の中心に対する角度を指しますが、「math.radians()」関数の引数に角度を指定することで簡単に変形できます。「orient_axis引数」には、「”X”」「”Y”」「”Z”」のいずれかで、回転軸を指定します。bpy.ops.transform.rotate()関数の使い方について、以下のサンプルコードで確認しましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # mathライブラリをimportする import math # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「トーラス」オブジェクトを生成する # 大きい頂点の分割数64・小さい頂点の分割数16・大きい部分の半径5.0・小さい部分の半径1.0 bpy.ops.mesh.primitive_torus_add(major_segments = 64, minor_segments = 16, major_radius = 5.0, minor_radius = 1.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0)) # オブジェクトを(1.0, 2.0, 3.0)に移動する bpy.ops.transform.translate(value = (1.0, 2.0, 3.0)) # オブジェクトをX軸まわりで135度回転させる bpy.ops.transform.rotate(value = math.radians(135.0), orient_axis = "X") // 実行結果
なお、Y軸まわりに135度回転させる場合は、以下のようになります。
オブジェクトのリサイズを行いたいときは、「bpy.ops.transform.resize()」関数を以下の構文で呼び出しましょう。
bpy.ops.transform.resize(value = (X軸方向のリサイズ比率, Y軸方向のリサイズ比率, Z軸方向のリサイズ比率))
各軸方向へのリサイズ比率を個別に指定できます。bpy.ops.transform.resize()関数の使い方について、以下のサンプルコードで確認しましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # mathライブラリをimportする import math # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「トーラス」オブジェクトを生成する # 大きい頂点の分割数64・小さい頂点の分割数16・大きい部分の半径5.0・小さい部分の半径1.0 bpy.ops.mesh.primitive_torus_add(major_segments = 64, minor_segments = 16, major_radius = 5.0, minor_radius = 1.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0)) # オブジェクトをX軸方向1.5倍・Y軸方向3.0倍・Z軸方向5.0倍に拡大する bpy.ops.transform.resize(value = (1.5, 3.0, 5.0)) # オブジェクトをY軸まわりで135度回転させる bpy.ops.transform.rotate(value = math.radians(135.0), orient_axis = "Y") # オブジェクトを(1.0, 2.0, 3.0)に移動する bpy.ops.transform.translate(value = (1.0, 2.0, 3.0)) // 実行結果
なお、基本的には「拡大縮小」→「回転」→「平行移動」の順番で行うことが重要です。順番を変えると、以下のように異なる実行結果となるため注意が必要です。必ず回転を行う前に、リサイズすることを意識しましょう。
Blender Python APIでは、オブジェクトやメッシュなどに固有の名前を付けられます。Blenderでは、オブジェクトを操作するときに、対象のオブジェクトを「選択」しておく必要があります。
しかし、複数のオブジェクトが存在する場合、Pythonスクリプトではリストから名前で検索しないといけません。そのため、以下のサンプルコードのような流れで、オブジェクトやメッシュに名前を付けておくことが重要です。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「キューブ」オブジェクトを生成する bpy.ops.mesh.primitive_cube_add(size = 5.0) # 生成したオブジェクトは自動的に選択中となるため、すぐにオブジェクトを取得する object = bpy.context.object # オブジェクトからメッシュを取得する mesh = object.data # オブジェクトに名前を付ける object.name = "Cube Object" # メッシュに名前を付ける mesh.name = "Cube Mesh" // 実行結果
重要なポイントは、オブジェクトの生成後すぐ「bpy.context.object」にアクセスし、オブジェクトデータを取得することです。メッシュはオブジェクトから取得できます。Blenderでは新規オブジェクトは自動的に選択状態となるため、その段階であればオブジェクトを取得可能です。
しかし、別のオブジェクトを生成するとそちらが選択されるため、前のオブジェクトは基本的に選択できなくなります。
オブジェクトやメッシュに正しく名前が付いているかどうかは、画面右上の「シーンコレクション」の一覧で確認しましょう。上記の実行結果では、「Cube Object」と「Cube Mesh」が作成されていることがわかります。
Blender Python APIでは、作成した各種オブジェクトはリストに格納されます。特定の要素を取り出すためには、以下の構文で「名前」を指定する必要があります。
# リストからオブジェクトを取得する object = bpy.data.objects["オブジェクト名"] # リストからメッシュを取得する mesh = bpy.data.meshes["メッシュ名"]
上記の手順で要素を取得したあとは、さまざまな操作を行えます。名前を付けたオブジェクトを取得し、新しい名前を付けなおすサンプルコードを紹介します。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「キューブ」オブジェクトを生成する bpy.ops.mesh.primitive_cube_add(size = 5.0) # 生成したオブジェクトは自動的に選択中となるため、すぐにオブジェクトを取得する object = bpy.context.object # オブジェクトからメッシュを取得する mesh = object.data # オブジェクトに名前を付ける object.name = "Cube Object" # メッシュに名前を付ける mesh.name = "Cube Mesh" # リストからオブジェクトを取得する object = bpy.data.objects["Cube Object"] # リストからメッシュを取得する mesh = bpy.data.meshes["Cube Mesh"] # オブジェクトに別の名前を付ける object.name = "Another Cube Object" # メッシュに別の名前を付ける mesh.name = "Another Cube Mesh" // 実行結果
実行結果の画面右上を見てみると、オブジェクトが「Another Cube Object」、メッシュが「Another Cube Mesh」という名前に変わっていることがわかります。応用的な操作を行う場合は、必要になる知識なので覚えておきましょう。
3DCG制作では、オブジェクトに「マテリアル」を適用するのが一般的です。マテリアルとは、オブジェクトの色彩や質感を表現するためのものです。Blender Python APIでは、以下の手順でオブジェクトにマテリアルを設定できます。
# 新しいマテリアルを作成する マテリアル変数 = bpy.data.materials.new("マテリアル名") # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(マテリアル変数)
オブジェクトを生成したあとに、すぐにマテリアル変数を作成すると、新規オブジェクトにマテリアルを適用できます。詳細は以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「ICOスフィア」オブジェクトを生成する bpy.ops.mesh.primitive_ico_sphere_add(subdivisions = 5, radius = 3.0, location = (0.0, 0.0, 0.0)) # 新しいマテリアルを作成する material = bpy.data.materials.new("Red Material") # ディフューズカラー(表面色)を「赤色」に設定する material.diffuse_color = (1.0, 0.0, 0.0, 1.0) # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果
また、マテリアルにはさまざまな要素があり、上記のサンプルプログラムでは「diffuse_color」つまり表面色のみ指定しました。「metallic」を1.0に近づけると金属的な質感になり、「roughness」を0.0に近づけると滑らかな表現となります。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「ICOスフィア」オブジェクトを生成する bpy.ops.mesh.primitive_ico_sphere_add(subdivisions = 5, radius = 3.0, location = (0.0, 0.0, 0.0)) # 新しいマテリアルを作成する material = bpy.data.materials.new("Red Material") # ディフューズカラー(表面色)を「赤色」に設定する material.diffuse_color = (1.0, 0.0, 0.0, 1.0) # メタリックを「1.0」にして金属的な質感にする material.metallic = 1.0 # ラフネスを「0.0」にして表面を滑らかにする material.roughness = 0.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
なお、マテリアルの効果を確認したいときは、画面上部のメニューから「シェーディング」を選ぶと、オブジェクトの色彩や質感がわかりやすくなります。上記のサンプルプログラムでは、金属的で滑らかな球体を作成できました。
複数のオブジェクトに異なるマテリアルを適用したい場合は、以下のように対象オブジェクトを「選択」状態にする必要があります。
# 新しいマテリアルを作成する マテリアル変数 = bpy.data.materials.new("マテリアル名") # マテリアルを適用したいオブジェクトを「選択」状態にする bpy.context.view_layer.objects.active = bpy.data.objects["オブジェクト名"] # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(マテリアル変数)
重要なポイントは、「bpy.context.view_layer.objects.activeプロパティ」に「bpy.data.objectsリスト」から取得したオブジェクトを、引き渡していることです。この際に「オブジェクト名」を指定する必要があるので、前述した手順でオブジェクトには必ず名前を付けておきましょう。詳細は以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「キューブ」オブジェクトを生成する bpy.ops.mesh.primitive_cube_add(size = 5.0, location = (-3.0, 0.0, 0.0)) # 生成したオブジェクトは自動的に選択中となるため、すぐにオブジェクトを取得する object = bpy.context.object # オブジェクトからメッシュを取得する mesh = object.data # オブジェクトに名前を付ける object.name = "Cube Object" # メッシュに名前を付ける mesh.name = "Cube Mesh" # 「ICOスフィア」オブジェクトを生成する bpy.ops.mesh.primitive_ico_sphere_add(subdivisions = 5, radius = 3.0, location = (3.0, 0.0, 0.0)) # 生成したオブジェクトは自動的に選択中となるため、すぐにオブジェクトを取得する object = bpy.context.object # オブジェクトからメッシュを取得する mesh = object.data # オブジェクトに名前を付ける object.name = "Sphere Object" # メッシュに名前を付ける mesh.name = "Sphere Mesh" # 新しいマテリアルを作成する material = bpy.data.materials.new("Red Material") # ディフューズカラー(表面色)を「赤色」に設定する material.diffuse_color = (1.0, 0.0, 0.0, 1.0) # マテリアルを適用したいオブジェクトを「選択」状態にする bpy.context.view_layer.objects.active = bpy.data.objects["Cube Object"] # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) # 新しいマテリアルを作成する material = bpy.data.materials.new("Blue Material") # ディフューズカラー(表面色)を「青色」に設定する material.diffuse_color = (0.0, 0.0, 1.0, 1.0) # マテリアルを適用したいオブジェクトを「選択」状態にする bpy.context.view_layer.objects.active = bpy.data.objects["Sphere Object"] # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) # すべてのオブジェクトを「非選択」状態にする bpy.context.view_layer.objects.active = None // 実行結果
オブジェクトの生成時に名前を付けておくことで、このように臨機応変な操作ができるようになります。
オブジェクトにマテリアルを適用する際は、「プリンシプルBSDF」を活用すると便利です。プリンシプルBSDFとは、複数の項目をひとつにまとめた使いやすいシェーダーノードです。なお、「BSDF(Bidirectional Scattering Distribution Function)」は双方向散乱分布関数を意味し、物体表面での光の散乱をシミュレートするためのモデルで、コンピューターグラフィックスの分野で活用されています。
Blender Python APIでは、比較的簡単な方法でプリンシプルBSDFを利用できます。プリンシプルBSDFのコーディング方法について、以下の4つのポイントから見ていきましょう。
プリンシプルBSDFは、基本的には以下のような流れで使用します。
# 新しいマテリアルを追加する マテリアル変数 = bpy.data.materials.new("マテリアル名") # シェーダーのノードを使用するために「ノードを使用」を有効化する マテリアル変数.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする インスタンス変数 = マテリアル変数.node_tree.nodes["プリンシプルBSDF"] # プリンシプルBSDFの設定を行う インスタンス変数.inputs[インデックス].default_value = 設定値
重要なポイントは、新しいマテリアルを生成してから、「use_nodesプロパティ」をTrueにすることです。ノードを有効化することで、マテリアル変数から「node_tree.nodes[“プリンシプルBSDF”]」を呼び出して、プリンシプルBSDFのノードが使えるようになります。なお、言語設定が英語になっている場合は、「プリンシプルBSDF」ではなく「Principled BSDF」と記載しないといけません。
マテリアルの色などの設定は、「inputs[インデックス].default_value」で項目を呼び出したうえで、値を設定します。また、シェーダーのノードを利用する場合は、「スクリプト作成」ではなく「シェーディング」の画面で実行結果を確認する必要があります。プリンシプルBSDFの基本的な使い方は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「トーラス」オブジェクトを生成する # 大きい頂点の分割数64・小さい頂点の分割数16・大きい部分の半径5.0・小さい部分の半径1.0 bpy.ops.mesh.primitive_torus_add(major_segments = 64, minor_segments = 16, major_radius = 5.0, minor_radius = 1.0, location = (0.0, 0.0, 0.0), rotation = (0.0, 0.0, 0.0)) # オブジェクトをX軸方向1.5倍・Y軸方向3.0倍・Z軸方向5.0倍に拡大する bpy.ops.transform.resize(value = (1.5, 3.0, 5.0)) # オブジェクトをY軸まわりで135度回転させる bpy.ops.transform.rotate(value = math.radians(135.0), orient_axis = "Y") # オブジェクトを(1.0, 2.0, 3.0)に移動する bpy.ops.transform.translate(value = (1.0, 2.0, 3.0)) # 新しいマテリアルを追加する material = bpy.data.materials.new("Red") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF = material.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」を赤色にする Principled_BSDF.inputs[0].default_value = (1.0, 0.0, 0.0, 1.0) # 「粗さ」を0.0にする Principled_BSDF.inputs[9].default_value = 0.0 # 「伝搬」を10.0にする Principled_BSDF.inputs[17].default_value = 10.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
このように鮮やかなグラフィックを描画できます。なお、マテリアルの値を設定するときは、リストのインデックスを指定しましょう。インデックスごとにどのような設定ができるのかについては、このあとの項目で見ていきます。
先ほどのサンプルプログラムの実行結果は、「シェーディング」の画面で確認できますが、以下のようなシェーダーのノードも表示されています。
このように、最初の項目は「ベースカラー」で、最後が「タンジェント」になっています。
ノードのインデックスは、上から順番に「0」から「24」まで割り当てられています。項目ごとの詳細は以下のとおりです。
リストのインデックス | 項目 | 概要 |
---|---|---|
0 | ベースカラー | オブジェクトの表面色 |
1 | サブサーフェス | サブサーフェス範囲の乗数 |
2 | サブサーフェス範囲 | 光がサーフェス下に散乱する平均距離 |
3 | サブサーフェスカラー | オブジェクトの表面下の色 |
4 | サブサーフェスIOR | サブサーフェスの屈折率 |
5 | サブサーフェス異方性 | サブサーフェスの方向 |
6 | メタリック | 金属の割合を指し、1.0に近づくほど金属的になる |
7 | スペキュラー | 金属質以外の表面反射を指し、1.0に近づくほど光沢感が増す |
8 | スペキュラーチント | スペキュラーへの色付け |
9 | 粗さ | ミクロな表面の粗さ |
10 | 異方性 | ハイライトの設定 |
11 | 異方性の回転 | ハイライトの回転 |
12 | シーン | 布などのマテリアルのシミュレート |
13 | シーンチント | 白とベースカラーをミックス |
14 | クリアコート | 白いスペキュラ階層 |
15 | クリアコートの粗さ | クリアコートの粗さ |
16 | IOR | 透過の屈折率 |
17 | 伝播 | ガラスのような透過値で、1.0に近づくほど透明度が高くなる |
18 | 伝播の粗さ | 透過の粗さを指し、1.0に近づくほど曇り感が出る |
19 | 放射 | オブジェクトから光を放つ設定 |
20 | 放射の強さ | 放つ光の強度 |
21 | アルファ | アルファ値で、0.0に近づくほど透明になる |
22 | ノーマル | ベースレイヤーの法線 |
23 | クリアコート法線 | クリアコートの法線 |
24 | タンジェント | 異方性階層の接線 |
なお、Blenderのバージョンによっては異なる可能性があるため、詳細は自身の「シェーディング」の画面で確認してください。基本的には、「ベースカラー」「メタリック」「スペキュラー」「粗さ」「伝播」などの項目をメインで使用するため、すべての項目について理解しておく必要はありません。
これまでのマテリアル設定では、オブジェクトは基本的に単色となるため、複雑な色彩表現は困難です。しかし、オブジェクトに「テクスチャ(画像)」を貼り付けることで、自由な表現ができるようになります。このように、オブジェクトにテクスチャを適用することを、「テクスチャマッピング」と呼びます。
テクスチャマッピングを行うためには、まずテクスチャ画像を準備しないといけません。以下のPNG画像を「Sky.png」という名称で保存し、以下のサンプルコードを実行しましょう。なお、サンプルでは「C:\Sample」ディレクトリを使用しているため、必要に応じてファイルパスを書き換えてください。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのデータオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「キューブ」オブジェクトを生成する bpy.ops.mesh.primitive_cube_add(size = 10, location = (0.0, 0.0, 0.0)) # 背景用のマテリアルを生成する material = bpy.data.materials.new("sky") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True # マテリアルのノードを取得する nodes = material.node_tree.nodes # デフォルトのノードを削除する nodes.clear() # 「プリンシプルBSDF」のシェーダーノードを追加する node_shader = nodes.new(type = "ShaderNodeBsdfPrincipled") # テクスチャのノードを追加する node_texture = nodes.new("ShaderNodeTexImage") # テクスチャの画像ファイルを読み込む node_texture.image = bpy.data.images.load("C:\Sample\Sky.png") # 出力ノードを追加する node_output = nodes.new(type = "ShaderNodeOutputMaterial") # マテリアルのリンク情報を取得する links = material.node_tree.links # すべてのノードをリンクさせる link = links.new(node_texture.outputs["Color"], node_shader.inputs["Base Color"]) link = links.new(node_shader.outputs["BSDF"], node_output.inputs["Surface"]) # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
重要なポイントは、ノード使用を有効化したあとに、ノードオブジェクトを取得していることです。そのうえで、ノードオブジェクトにプリンシプルBSDF用の「ShaderNodeBsdfPrincipled」ノードと、テクスチャ用の「ShaderNodeTexImage」を追加しましょう。
次に「bpy.data.images.load()」関数でテクスチャ画像を読み込み、テクスチャ用ノードに指定します。さらに、出力用の「ShaderNodeOutputMaterial」ノードを追加し、各ノードをリンクさせます。しかし、一連の流れはわかりづらいため、以下の部分はコピー&ペーストで使いまわすようにするといいでしょう。
# 背景用のマテリアルを生成する material = bpy.data.materials.new("sky") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True # マテリアルのノードを取得する nodes = material.node_tree.nodes # デフォルトのノードを削除する nodes.clear() # 「プリンシプルBSDF」のシェーダーノードを追加する node_shader = nodes.new(type = "ShaderNodeBsdfPrincipled") # テクスチャのノードを追加する node_texture = nodes.new("ShaderNodeTexImage") # テクスチャの画像ファイルを読み込む node_texture.image = bpy.data.images.load("C:\Sample\Sky.png") # 出力ノードを追加する node_output = nodes.new(type = "ShaderNodeOutputMaterial") # マテリアルのリンク情報を取得する links = material.node_tree.links # すべてのノードをリンクさせる link = links.new(node_texture.outputs["Color"], node_shader.inputs["Base Color"]) link = links.new(node_shader.outputs["BSDF"], node_output.inputs["Surface"]) # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material)
複数のオブジェクトをテクスチャマッピングするときは、前述したように各オブジェクトに名前を付けておき、必要な場面で呼び出すことが重要です。以下の画像を「Flower.png」という名称で、先ほどの「Sky.png」と同じディレクトリに保存してから、サンプルコードを実行しましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # set_material()|指定されたテクスチャファイルを読み込み、マテリアルを適用する def set_material(object, material_name, texture_filepath): # マテリアルを生成する material = bpy.data.materials.new(material_name) # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True # マテリアルのノードを取得する nodes = material.node_tree.nodes # デフォルトのノードを削除する nodes.clear() # 「プリンシプルBSDF」のシェーダーノードを追加する node_shader = nodes.new(type = "ShaderNodeBsdfPrincipled") # テクスチャのノードを追加する node_texture = nodes.new("ShaderNodeTexImage") # テクスチャの画像ファイルを読み込む node_texture.image = bpy.data.images.load(texture_filepath) # 出力ノードを追加する node_output = nodes.new(type = "ShaderNodeOutputMaterial") # マテリアルのリンク情報を取得する links = material.node_tree.links # すべてのノードをリンクさせる link = links.new(node_texture.outputs["Color"], node_shader.inputs["Base Color"]) link = links.new(node_shader.outputs["BSDF"], node_output.inputs["Surface"]) # 指定されたオブジェクトを「選択」状態にする bpy.context.view_layer.objects.active = object # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 「キューブ」オブジェクトを生成する bpy.ops.mesh.primitive_cube_add(size = 5.0, location = (-3.0, 0.0, 0.0)) # 生成したオブジェクトは自動的に選択中となるため、すぐにオブジェクトを取得する object = bpy.context.object # オブジェクトからメッシュを取得する mesh = object.data # オブジェクトに名前を付ける object.name = "Cube Object" # メッシュに名前を付ける mesh.name = "Cube Mesh" # 「UVスフィア」オブジェクトを生成する bpy.ops.mesh.primitive_uv_sphere_add(segments = 64, ring_count = 32, radius = 3.0, location = (3.0, 0.0, 0.0)) # 生成したオブジェクトは自動的に選択中となるため、すぐにオブジェクトを取得する object = bpy.context.object # オブジェクトからメッシュを取得する mesh = object.data # オブジェクトに名前を付ける object.name = "Sphere Object" # メッシュに名前を付ける mesh.name = "Sphere Mesh" # キューブオブジェクトにマテリアルを適用する set_material(bpy.data.objects["Cube Object"], "Cube Material", "C:\Sample\Sky.png") # スフィアオブジェクトにマテリアルを適用する set_material(bpy.data.objects["Sphere Object"], "Sphere Material", "C:\Sample\Flower.png") // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
上記のサンプルプログラムでは、マテリアルの作成と適用の部分を関数化しています。オブジェクトやファイルパスを指定すると、オブジェクトを簡単にテクスチャマッピングできます。なお、スフィアオブジェクトをテクスチャマッピングする場合は、前述したように「UVスフィア」を利用しましょう。
BlenderのPythonスクリプトでは、テキスト(ラベル)を描画することもできます。テキストの扱い方について、以下の2つのポイントから見ていきましょう。
Blender Python APIでは、以下の手順でテキスト・ラベルを生成できます。
# テキストオブジェクトを生成して「編集モード」へ移行する bpy.ops.object.text_add(enter_editmode = True) # デフォルトのテキストを削除する bpy.ops.font.delete(type = "PREVIOUS_WORD") # 新しいテキストを挿入する bpy.ops.font.text_insert(text = "生成したいテキスト")
テキストの編集をスムーズに行うために、bpy.ops.object.text_add()関数の「enter_editmode引数」は必ずTrueにしておきましょう。詳細は以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # randomライブラリをimportする import random # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # テキストオブジェクトを生成して「編集モード」へ移行する bpy.ops.object.text_add(enter_editmode = True) # デフォルトのテキストを削除する bpy.ops.font.delete(type = "PREVIOUS_WORD") # 新しいテキストを挿入する bpy.ops.font.text_insert(text = "Sample Text") // 実行結果
以下のサンプルコードのように、テキスト・ラベルにマテリアルを適用することも可能です。基本的な手順は、通常のオブジェクトの場合と変わりません。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # randomライブラリをimportする import random # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # すべてのオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # テキストオブジェクトを生成して「編集モード」へ移行する bpy.ops.object.text_add(enter_editmode = True) # デフォルトのテキストを削除する bpy.ops.font.delete(type = "PREVIOUS_WORD") # 新しいテキストを挿入する bpy.ops.font.text_insert(text = "Sample Text") # 新しいマテリアルを追加する material = bpy.data.materials.new("Text") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF = material.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」を緑にする Principled_BSDF.inputs[0].default_value = (0.0, 1.0, 0.0, 1.0) # 「メタリック」を1.0にする Principled_BSDF.inputs[6].default_value = 1.0 # 「粗さ」を0.0にする Principled_BSDF.inputs[9].default_value = 0.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
これまでは、Pythonのプリミティブ図形の生成機能を活用した、3Dモデリングの方法を解説してきました。一方、Blender Python APIではプログラミングによって、自由な形状のメッシュを生成することも可能です。本章では、Pythonスクリプトでメッシュを作成する方法について、以下の9つのテクニックを解説します。
なお、いずれも最初に「頂点」を定義し、それらを繋ぎ合わせて「面」を構成していきます。頂点はFloat値のリストで定義し、面は頂点リストのインデックスをInt値で並べます。そのうえで、以下の手順でメッシュを生成することが重要です。
# 新しいメッシュを生成する メッシュ変数 = bpy.data.meshes.new("メッシュ名") # 定義した頂点と面のデータからメッシュを生成する メッシュ変数.from_pydata(頂点リスト, [], 面リスト) # メッシュデータからオブジェクトを定義する オブジェクト変数 = bpy.data.objects.new("オブジェクト名", メッシュ変数) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(オブジェクト変数)
Blender Python APIで「平面」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # 4つの頂点を定義する vertices = [(-1.0, -1.0, 0.0), (-1.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, -1.0, 0.0)] # 頂点を繋ぎ合わせてメッシュの「面」を構成する faces = [(0, 1, 2, 3)] # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Plane") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Plane", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) // 実行結果
Blender Python APIで「立方体」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # 8つの頂点を定義する vertices = [(-1.0, -1.0, 0.0), (-1.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 2.0), (1.0, 1.0, 2.0), (1.0, -1.0, 2.0)] # 頂点を繋ぎ合わせてメッシュの「面」を構成する faces = [(0, 1, 2, 3), (7, 6, 5, 4), (0, 4, 5, 1), (1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0)] # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Cube") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Cube", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) // 実行結果
Blender Python APIで「多角形」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # create_polygon()|多角形を生成する def create_polygon(num_vertices, center_position): # 中心点を起点として頂点を配置していく vertices = [center_position] for i in range(num_vertices + 1): # 多角形の座標値を生成する x = (2.0 * math.pi / num_vertices) * i # 中心座標を基準として座標を設定する vertices.append([center_position[0] + math.cos(x), center_position[1] + math.sin(x), 0.0]) # 「中心と円周上の2点」を合わせた全3点で「面」を構成する faces = [] # すべての面を構成していく for i in range(0, num_vertices): # 円周上の2点は隣り合った頂点である faces.append([0, i + 1, i + 2]) # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Polygon") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Polygon", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) # 「編集モード」に移行する bpy.context.view_layer.objects.active = object bpy.ops.object.mode_set(mode = "EDIT") # 重複している頂点を削除する bpy.ops.mesh.remove_doubles() # 元のオブジェクトモードに戻る bpy.ops.object.mode_set(mode = "OBJECT") # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # さまざまなポリゴンを生成する for i in range(0, 10): # オフセット値を加えながらポリゴンを生成する create_polygon(i + 3, (i * 2.0, 0.0, 0.0)) // 実行結果
多角形の生成手順は、三角関数を使用する必要があるため、やや複雑になります。円周上の頂点を定義したあとは、隣り合う2点と円の中心を結ぶ三角ポリゴンを並べていくと、頂点数が多い多角形もコーディングで楽に生成できます。
Blender Python APIで「多角錐」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # create_polygon_pyramid()|多角形を生成する def create_polygon_pyramid(num_vertices, center_position, height): # 中心点を起点として頂点を配置していく vertices = [center_position] # まずは多角錐の底面の頂点を構成する for i in range(num_vertices + 1): # 多角形の座標値を生成する x = (2.0 * math.pi / num_vertices) * i # 中心座標を基準として座標を設定する vertices.append([center_position[0] + math.cos(x), center_position[1] + math.sin(x), 0.0]) # 「中心と円周上の2点」を合わせた全3点で「面」を構成する faces = [] # すべての面を構成していく for i in range(0, num_vertices): # 円周上の2点は隣り合った頂点である faces.append([0, i + 1, i + 2]) # 多角錐の頂点の座標を最後の頂点とする vertices.append((center_position[0], center_position[1], height)) # 多角錐の側面の「面」を構成していく for i in range(1, len(vertices) - 1): # 底面の隣り合う2点と頂点を結びつけると側面ができる faces.append([i + 0, i + 1, len(vertices) - 1]) # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Pyramid") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Pyramid", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) # 「編集モード」に移行する bpy.context.view_layer.objects.active = object bpy.ops.object.mode_set(mode = "EDIT") # 重複している頂点を削除する bpy.ops.mesh.remove_doubles() # 元のオブジェクトモードに戻る bpy.ops.object.mode_set(mode = "OBJECT") # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # さまざまなポリゴンを生成する for i in range(0, 10): # オフセット値を加えながらポリゴンを生成する create_polygon_pyramid(i + 3, (i * 2.0, 0.0, 0.0), 2.0) // 実行結果
多角錐をコーディングで生成する際は、側面の構成に工夫が必要です。底面の隣り合う2点と、多角錐の上部の座標で三角ポリゴンを構成すると、多角錐を生成できます。
Blender Python APIで「多角柱」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # create_polygon_prism()|多角柱を生成する def create_polygon_prism(num_vertices, center_position, height): # 中心点を起点として頂点を配置していく vertices = [center_position] # まずは多角柱の下底の頂点を構成する for i in range(num_vertices + 1): # 多角形の座標値を生成する x = (2.0 * math.pi / num_vertices) * i # 中心座標を基準として座標を設定する vertices.append([center_position[0] + math.cos(x), center_position[1] + math.sin(x), 0.0]) # 多角柱の上底の中心座標を追加する vertices.append((center_position[0], center_position[1], height)) # 続いて多角柱の上底の頂点を構成する for i in range(num_vertices + 1): # 多角形の座標値を生成する x = (2.0 * math.pi / num_vertices) * i # 中心座標を基準として座標を設定する vertices.append([center_position[0] + math.cos(x), center_position[1] + math.sin(x), height]) # 下底と上底は、「中心と円周上の2点」を合わせた全3点で「面」を構成する # 側面は下底と上底の関連し合う全4点で「面」を構成する faces = [] # すべての下底を構成していく for i in range(0, num_vertices): # 円周上の2点は隣り合った頂点である faces.append([0, i + 1, i + 2]) # すべての上底を構成していく for i in range(num_vertices + 2, num_vertices * 2 + 2): # 円周上の2点は隣り合った頂点である faces.append([num_vertices + 2, i + 1, i + 2]) # 多角錐の側面の「面」を構成していく for i in range(1, num_vertices + 1): # 下底の隣り合う2点の真上にある上底の2点を結びつけると側面ができる faces.append([i + 0, i + 1, i + (num_vertices + 3), i + (num_vertices + 2)]) # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Pyramid") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Pyramid", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) # 「編集モード」に移行する bpy.context.view_layer.objects.active = object bpy.ops.object.mode_set(mode = "EDIT") # 重複している頂点を削除する bpy.ops.mesh.remove_doubles() # 元のオブジェクトモードに戻る bpy.ops.object.mode_set(mode = "OBJECT") # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # さまざまなポリゴンを生成する for i in range(0, 10): # オフセット値を加えながらポリゴンを生成する create_polygon_prism(i + 3, (i * 2.0, 0.0, 0.0), 2.0) // 実行結果
多角柱の場合は、上部にも面を構成する必要があるため、より複雑なプログラムになります。側面については、「底面の隣り合う2点」と「それに対応する上面の隣り合う2点」で四角ポリゴンを構成しましょう。このプログラムを活用すると、基本的にはどのような頂点数の多角柱でも簡単に作成できます。
Blender Python APIで「球体」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # 8つの頂点を定義する vertices = [(-1.0, -1.0, 0.0), (-1.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 2.0), (1.0, 1.0, 2.0), (1.0, -1.0, 2.0)] # 頂点を繋ぎ合わせてメッシュの「面」を構成する faces = [(0, 1, 2, 3), (7, 6, 5, 4), (0, 4, 5, 1), (1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0)] # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Cube") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Cube", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) # メッシュに「サブディビジョンサーフェス」を適用する # メッシュが自動的に細かく分割されて球体になる object.modifiers.new("subd", type = "SUBSURF") # サブディビジョンサーフェスのレベルを決定する # この値を大きくするほど分割数が多くなり、綺麗な球体になる object.modifiers["subd"].levels = 5 # 「スムーズシェード」を適用する # 球体のシェーディングが滑らかになる for polygon in mesh.polygons: polygon.use_smooth = True // 実行結果
球体の生成は特殊な手順で行います。まずは、前述した方法で立方体を定義したあとで、「サブディビジョンサーフェス」を適用します。サブディビジョンサーフェスは、頂点を細かく分割するための機能で、分割数が増えるほど滑らかな曲面を構成することが可能です。
なお、サブディビジョンサーフェスはさまざまなオブジェクトで利用できるので、ぜひ覚えておきましょう。
Blender Python APIで「波状のメッシュ」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # 頂点と面を格納するリスト vertices = [] faces = [] # X軸とY軸それぞれの大きさを指定する num_x = 10 num_y = 10 # 波の周波数・高さ・大きさを指定する frequency = 1 height = 1 scale = 1 # 頂点座標をforループで生成する for i in range (0, num_x): for j in range(0, num_y): vertices.append((scale * i, scale * j, scale * ((math.cos(i * frequency) * height) + (math.sin(j * frequency) * height)))) # 面を構成するためのカウンターを準備する count = 0 # 4つの頂点から面を構成していく for i in range (0, num_y * (num_x - 1)): if count < num_y - 1: faces.append((i, i + 1, (i + num_y) + 1, (i + num_y))) count += 1 else: count = 0 # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Wave") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Wave", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) # メッシュに「サブディビジョンサーフェス」を適用する # メッシュが自動的に細かく分割されて球体になる object.modifiers.new("subd", type = "SUBSURF") # サブディビジョンサーフェスのレベルを決定する # この値を大きくするほど分割数が多くなり、綺麗な球体になる object.modifiers["subd"].levels = 5 # 「スムーズシェード」を適用する # 球体のシェーディングが滑らかになる for polygon in mesh.polygons: polygon.use_smooth = True // 実行結果
デザインのために波状のオブジェクトが必要なときに役立ちます。なお、こちらもサブディビジョンサーフェスの機能を使用しています。
Blender Python APIで「ランダムなメッシュ」を生成する基本的な手順は、以下のサンプルコードのとおりです。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # randomライブラリをimportする import random # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # 頂点と面を格納するリスト vertices = [] faces = [] # X軸とY軸それぞれの大きさを指定する num_x = 30 num_y = 30 # 波の周波数・高さ・大きさを指定する height = 0.5 scale = 1 # 頂点座標をforループで生成する for i in range (0, num_x): for j in range(0, num_y): vertices.append((scale * i, scale * j, (i * random.random()) * height)) # 面を構成するためのカウンターを準備する count = 0 # 4つの頂点から面を構成していく for i in range (0, num_y * (num_x - 1)): if count < num_y - 1: faces.append((i, i + 1, (i + num_y) + 1, (i + num_y))) count += 1 else: count = 0 # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Wave") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Wave", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) # メッシュに「サブディビジョンサーフェス」を適用する # メッシュが自動的に細かく分割されて球体になる object.modifiers.new("subd", type = "SUBSURF") # サブディビジョンサーフェスのレベルを決定する # この値を大きくするほど分割数が多くなり、綺麗な球体になる object.modifiers["subd"].levels = 5 # 「スムーズシェード」を適用する # 球体のシェーディングが滑らかになる for polygon in mesh.polygons: polygon.use_smooth = True // 実行結果
基本的な手順は先ほどの波状メッシュと似ていますが、こちらはランダムな地形を生成できます。サブディビジョンサーフェスの機能を使用することで、滑らかな曲面の生成が可能となっています。
メッシュの生成と合わせて、面ごとに異なるマテリアルを適用したい場合は、以下のサンプルコードを参考にしてみてください。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # randomライブラリをimportする import random # create_polygon_prism()|多角柱を生成する def create_polygon_prism(num_vertices, center_position, height): # 中心点を起点として頂点を配置していく vertices = [center_position] # まずは多角柱の下底の頂点を構成する for i in range(num_vertices + 1): # 多角形の座標値を生成する x = (2.0 * math.pi / num_vertices) * i # 中心座標を基準として座標を設定する vertices.append([center_position[0] + math.cos(x), center_position[1] + math.sin(x), 0.0]) # 多角柱の上底の中心座標を追加する vertices.append((center_position[0], center_position[1], height)) # 続いて多角柱の上底の頂点を構成する for i in range(num_vertices + 1): # 多角形の座標値を生成する x = (2.0 * math.pi / num_vertices) * i # 中心座標を基準として座標を設定する vertices.append([center_position[0] + math.cos(x), center_position[1] + math.sin(x), height]) # 下底と上底は、「中心と円周上の2点」を合わせた全3点で「面」を構成する # 側面は下底と上底の関連し合う全4点で「面」を構成する faces = [] # すべての下底を構成していく for i in range(0, num_vertices): # 円周上の2点は隣り合った頂点である faces.append([0, i + 1, i + 2]) # すべての上底を構成していく for i in range(num_vertices + 2, num_vertices * 2 + 2): # 円周上の2点は隣り合った頂点である faces.append([num_vertices + 2, i + 1, i + 2]) # 多角錐の側面の「面」を構成していく for i in range(1, num_vertices + 1): # 下底の隣り合う2点の真上にある上底の2点を結びつけると側面ができる faces.append([i + 0, i + 1, i + (num_vertices + 3), i + (num_vertices + 2)]) # 新しいメッシュを生成する mesh = bpy.data.meshes.new("Pyramid") # 定義した頂点と面のデータからメッシュを生成する mesh.from_pydata(vertices, [], faces) # メッシュデータからオブジェクトを定義する object = bpy.data.objects.new("Pyramid", mesh) # オブジェクトをシーンにリンクさせる bpy.context.scene.collection.objects.link(object) # 「編集モード」に移行する bpy.context.view_layer.objects.active = object bpy.ops.object.mode_set(mode = "EDIT") # 重複している頂点を削除する bpy.ops.mesh.remove_doubles() # 元のオブジェクトモードに戻る bpy.ops.object.mode_set(mode = "OBJECT") # メッシュオブジェクトを取得する set_face_material(object) # set_face_material()|面ごとにマテリアルを設定する def set_face_material(object): # アクティブオブジェクトを設定する bpy.context.view_layer.objects.active = object # 「編集モード」へ移行する bpy.ops.object.mode_set(mode = "EDIT") # メッシュオブジェクトを取得する mesh = bmesh.from_edit_mesh(object.data) # すべての面ごとにマテリアルを設定する for face in mesh.faces: # 新しいマテリアルのスロットを追加して、新規マテリアルを生成する bpy.ops.object.material_slot_add() material = bpy.data.materials.new("マテリアル") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする material.node_tree.nodes["プリンシプルBSDF"].inputs["Base Color"].default_value = (random.random(), random.random(), random.random(), 1.0) # 全メッシュを非選択状態にする bpy.ops.mesh.select_all(action = "DESELECT") # 面の選択を有効化する face.select = True # 新しいマテリアルを選択したうえで、面にマテリアルを適用する object.active_material = material bpy.ops.object.material_slot_assign() # メッシュオブジェクトを解放する mesh.free() # 元の「オブジェクトモード」に戻す bpy.ops.object.mode_set(mode = "OBJECT") # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # さまざまなポリゴンを生成する for i in range(0, 15): # オフセット値を加えながらポリゴンを生成する # 面ごとのマテリアル設定も行う create_polygon_prism(i + 3, (i * 2.0, 0.0, 0.0), 2.0) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
set_face_material()関数の内部が、面ごとに異なるマテリアルを適用する部分です。重要なポイントは、オブジェクトそのものではなく「面」を選択して、個別にマテリアルを適用していることです。
こうした工程を手作業で行おうとすると、非常に煩雑で時間がかかります。しかし、Blender Python APIならスクリプトを実行するだけです。こうした効率性は、Pythonスクリプトの大きなメリットだといえるでしょう。
Blender Python APIは、3Dモデリングやデザインの効率化に役立ちます。本章では、Blender Python APIの活用例を4つ紹介します。
輝くマテリアルを適用したスフィアを円周上に並べると、それだけで美しい画面を構成できます。以下のサンプルコードを実行してみましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのデータオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 生成するスフィア数を設定する num_spheres = 12 # スフィアを配置する半径を設定する sphere_distance = 10.0 # スフィアの半径を設定する sphere_radius = 2.0 # すべてのスフィアを生成する for i in range(0, num_spheres): # スフィアを配置する座標を算出する radian = 2 * math.pi * (i / num_spheres) position_x = sphere_distance * math.cos(radian) position_y = sphere_distance * math.sin(radian) # ICOスフィアを生成する bpy.ops.mesh.primitive_ico_sphere_add(location = (position_x, position_y, 0.0), radius = sphere_radius, subdivisions = 8) # 新しいマテリアルを生成する material = bpy.data.materials.new(f"Glass0{i}") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF = material.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」を緑色にする Principled_BSDF.inputs[0].default_value = (0.0, 1.0, 0.0, 1.0) # 「粗さ」を0.0にする Principled_BSDF.inputs[9].default_value = 0.0 # 「伝搬」を1.0にする Principled_BSDF.inputs[17].default_value = 1.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
円周上にスフィアを配置するために、X座標の生成にcos()関数、Y座標の生成にsin()関数を使用しています。さまざまな場面で役立つテクニックなので、ぜひ覚えておきましょう。なお、マテリアルは「粗さ」を0.0にして、「伝播」を1.0にすることで、上記のような滑らかに輝く質感を表現できます。
キューブを三次元的・立体的に並べることで、機械的・先進的なイメージを表現できます。以下のサンプルコードを実行してみましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 3次元方向に「7×7×7」の配列でキューブを並べる for i in range(0, 7): for j in range(0, 7): for k in range(0, 7): # 新しいマテリアルを生成する material = bpy.data.materials.new("Glass") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True # キューブを生成する bpy.ops.mesh.primitive_cube_add(size = 0.75, location = (i, j, k)) #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF = material.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」を青色にする Principled_BSDF.inputs[0].default_value = (0.0, 0.0, 1.0, 1.0) # 「粗さ」を0.0にする Principled_BSDF.inputs[9].default_value = 0.0 # 「伝搬」を1.0にする Principled_BSDF.inputs[17].default_value = 1.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
オブジェクトを一定間隔で並べて表示する場合は、forループを活用すると便利です。今回は、ループカウンタをそのまま使用して座標を設定していますが、オフセット変数で座標を調整する手法も効果的です。
ドーナツ型の「トーラス」はデザイン性が高いため、さまざまなシーンで活用できるプリミティブです。トーラスのサイズを変えながら立体的に配置すると、以下のような幾何学的な画面を構成できます。以下のサンプルコードを実行してみましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # randomライブラリをimportする import random # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # 10個のトーラスを順番に生成して並べる for i in range(0, 10): # 新しいマテリアルを生成する material = bpy.data.materials.new("Glass") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True # トーラスを生成する # 半径は徐々に大きくしていき、ランダムに回転させる bpy.ops.mesh.primitive_torus_add(rotation = (math.pi * random.random(), math.pi * random.random(), 0.0), major_segments = 64, minor_segments = 4, major_radius = i * 1.5, minor_radius = 0.15) #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF = material.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」を設定する # 最初は青が強く、徐々に緑に変わっていく Principled_BSDF.inputs[0].default_value = (0.0, 0.1 * i, 1.0 - 0.1 * i, 1.0) # 「メタリック」を1.0にする Principled_BSDF.inputs[6].default_value = 1.0 # 「粗さ」を0.0にする Principled_BSDF.inputs[9].default_value = 0.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
上記のサンプルプログラムでは、10個のトーラスを三次元空間に配置しています。いずれも座標値は原点ですが、一定間隔でサイズを大きくしていることや、回転量をランダムに設定していることが特徴です。メタリックで滑らかなマテリアルを採用しているうえに、表面色も青から緑に少しずつ遷移しているため、魅力的な画面を構成できています。
このように、プログラミングで自動的に魅力的なグラフィックを制作できることが、Blender Python APIの大きなメリットです。
トーラスの配置に「リサジュー曲線」を利用すると、さらに幾何学的な画面を演出できます。リサジュー曲線とは、互いに直交する単振動を合成して得られる曲線を指し、変数を調整することでさまざまな形状が得られます。三角関数のsin()とcos()を活用することで、以下のサンプルコードのように、トーラスをリサジュー曲線的に配置可能です。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # randomライブラリをimportする import random # numpyライブラリをnpというエイリアスでimportする import numpy as np # delete_objects()|すべてのオブジェクトを削除する def delete_objects(): # すべてのコレクションを削除する for collection in bpy.data.collections: for item in collection.objects: collection.objects.unlink(item) bpy.data.objects.remove(item) # すべてのシーンオブジェクトを削除する for item in bpy.context.scene.collection.objects: bpy.context.scene.collection.objects.unlink(item) # すべてのデータオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのカメラを削除する for item in bpy.data.cameras: bpy.data.cameras.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # すべてのライトを削除する for item in bpy.data.lights: bpy.data.lights.remove(item) # generate_lissajous_curve()|パラメータに応じていた「リサジュー曲線」を生成する def generate_lissajous_curve(param): return (np.cos(param * 3) * 6, np.sin(param * 3) * 6, np.cos(param * 5) * 6) # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのオブジェクトを削除する delete_objects() # 100個のオブジェクトを生成するために、ベース値をリストに格納する list = np.linspace(0, np.pi, 100) # 100個のオブジェクトを生成する for i in range(len(list)): # オブジェクトの座標を「リサジュー曲線」で設定する position = generate_lissajous_curve(list[i]) # トーラスを生成する bpy.ops.mesh.primitive_torus_add() # オブジェクトを移動する bpy.ops.transform.translate(value = position) # オブジェクトをランダムに回転させる bpy.ops.transform.rotate(value = math.pi * random.random() ,orient_axis = "X") bpy.ops.transform.rotate(value = math.pi * random.random() ,orient_axis = "Y") bpy.ops.transform.rotate(value = math.pi * random.random() ,orient_axis = "Z") # オブジェクトをランダムにリサイズする bpy.ops.transform.resize(value = (0.5 + random.random() / 2.0, 0.5 + random.random() / 2.0, 0.5 + random.random() / 2.0)) # 新しいマテリアルを生成する material = bpy.data.materials.new("Random") # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF = material.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」をランダムに設定する Principled_BSDF.inputs[0].default_value = (random.random(), random.random(), random.random(), 1.0) # 「メタリック」を1.0に設定する Principled_BSDF.inputs[6].default_value = 1.0 # 「粗さ」を0.0に設定する Principled_BSDF.inputs[9].default_value = 0.0 # 「伝搬」を1.0に設定する Principled_BSDF.inputs[17].default_value = 1.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) // 実行結果 「スクリプト作成」の画面 「シェーディング」の画面
各オブジェクトをランダムに回転させることで、より効果的な画面を構成できます。ベースカラー(表面色)もランダムなので、プログラムを実行するたびに異なる描画結果が得られることも魅力です。
これまで解説した内容では、実行結果はすべてPythonのアプリケーション内で確認していました。しかし、実行結果を「画像ファイル」として出力したいこともあるでしょう。作成した3Dグラフィックスを画像に書き出す工程は、「レンダリング」と呼ばれます。
本章では、Blender Python APIでレンダリングを行う方法・手順について、以下の2つのポイントから解説します。
Blenderでレンダリングを行うためには、「ライト」と「カメラ」が必要です。ライトは空間を照らすためのものであり、ライトがなければ画面が暗くなってしまいます。ライトには以下の4種類があり、それぞれ特徴が異なります。
ライトの種類 | ライトの特徴 |
---|---|
POINT | 全方位を照らす「点光源」で、オブジェクトとの距離が近づくほど、明るく照らされる。部屋の中にある蛍光灯のようなイメージ。 |
SUN | 指定した方向を均等に照らす光源。光量が無限大であるため、オブジェクトの距離に関係なく同じ明るさで照らすことができるが、光源の角度の影響は受ける。 |
SPOT | 一部分だけを照らすことができるスポットライトで、オブジェクトとの距離の影響を受けやすい。 |
AREA | 基本的にはポイントランプと同じだが、光源の方向を調整できる。 |
基本的には、距離による明るさの違いを強調したい場合は「POINT」、屋外の自然なライティングを再現したい場合は「SUN」を選ぶといいでしょう。Blender Python APIでは、以下の構文でライトを追加できます。
# ライトを追加する bpy.ops.object.light_add(type = "ライトの種類", align = "WORLD", location = (ライト座標X, ライト座標Y, ライト座標Z), rotation = (X軸回転, Y軸回転, Z軸回転)) # ライトの強さを設定する bpy.context.object.data.energy = ライトの強さ
「type引数」には、前述した「”POINT”」「”SUN”」「”SPOT”」「”AREA”」のいずれかを指定しましょう。SUNとAREAを指定した場合は、「rotation引数」がライティングに影響するので、オブジェクトの方向に向けることを意識しましょう。
レンダリングには「カメラ」も欠かせません。カメラは、いわばオブジェクトを撮影するためのものであり、以下の構文で追加できます。
# カメラを追加する bpy.ops.object.camera_add(align = "VIEW", location = (カメラ座標X, カメラ座標Y, カメラ座標Z), rotation = (X軸回転, Y軸回転, Z軸回転)) # カメラを設定する bpy.context.scene.camera = bpy.context.object
カメラには種類の違いはないため、基本的は上記の流れでカメラを追加し、適切な座標値と回転量を設定すればOKです。
ライトとカメラを準備できたら、レンダリングを行って画像ファイルに出力しましょう。レンダリングの手順・方法は以下のとおりです。
# レンダリングの画像フォーマットを設定する bpy.context.scene.render.image_settings.file_format = "拡張子" # レンダリングの解像度を設定する bpy.context.scene.render.resolution_x = 画像の横幅 bpy.context.scene.render.resolution_y = 画像の高さ bpy.context.scene.render.resolution_percentage = 解像度のパーセンテージ # レンダリングを行う bpy.ops.render.render(use_viewport = True, write_still = True) # レンダリング結果をファイルに出力する bpy.data.images["Render Result"].save_render(出力ファイルパス)
「拡張子」には「PNG」や「JPEG」など、画像の形式を指定します。解像度には画像サイズを指定し、最後に「save_render()」関数でファイルパスを設定することで、レンダリングのファイルを出力できます。
これまで解説してきた知識・テクニックを踏まえて、以下の2つの実践的なレンダリングにチャレンジしてみましょう。サンプルコードをアレンジすることで、さまざまな場面に応用できます。
先ほど解説したライト・カメラの作成方法を踏まえて、テキストのレンダリングを行ってみましょう。以下のサンプルコードを実行すると、立体的なテキストをレンダリングしてファイル出力できます。ただし、デフォルトでは「C:\Sample\Text.png」にファイルが作成されるため、必要に応じてファイルパスを変更してください。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # delete_objects()|すべてのオブジェクトを削除する def delete_objects(): # すべてのコレクションを削除する for collection in bpy.data.collections: for item in collection.objects: collection.objects.unlink(item) bpy.data.objects.remove(item) # すべてのシーンオブジェクトを削除する for item in bpy.context.scene.collection.objects: bpy.context.scene.collection.objects.unlink(item) # すべてのデータオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのカメラを削除する for item in bpy.data.cameras: bpy.data.cameras.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # すべてのライトを削除する for item in bpy.data.lights: bpy.data.lights.remove(item) # create_object()|オブジェクトを生成する def create_object(text_data, text_color, text_thickness, background_color): # テキストオブジェクトを生成する bpy.ops.object.text_add() # テキストオブジェクトを取得する object = bpy.context.object # オブジェクト名を設定する object.data.name = "Sample Text" # レンダリングするテキストを指定する object.data.body = text_data # テキストのサイズを設定する object.location = (-0.27, -0.05, 0.0) # テキストのサイズを設定する object.scale = (0.23, 0.23, 0.23) # テキストの厚みを設定する object.data.extrude = text_thickness # 「メイリオ」フォントを読み込む object.data.font = bpy.data.fonts.load("C:\Windows\Fonts\meiryo.ttc") # テキスト用のマテリアルを生成する material_text = bpy.data.materials.new("Text Material") # シェーダーのノードを使用するために「ノードを使用」を有効化する material_text.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF_text = material_text.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」を設定する Principled_BSDF_text.inputs[0].default_value = (text_color[0], text_color[1], text_color[2], 1.0) # 「メタリック」を設定する Principled_BSDF_text.inputs[6].default_value = text_color[3] # 「粗さ」を設定する Principled_BSDF_text.inputs[9].default_value = text_color[4] # 「伝播」を設定する Principled_BSDF_text.inputs[17].default_value = text_color[5] # マテリアルスロットを追加する bpy.ops.object.material_slot_add() # 追加したマテリアルスロットに、新しいマテリアルを設定する bpy.context.object.active_material = material_text # 背景を追加する # テキストと背景が重ならないように、テキストの厚み分だけ位置を調整する bpy.ops.mesh.primitive_plane_add(size = 2, location=(0.0, 0.0, -text_thickness), scale = (100.0, 100.0, 100.0)) # 背景用のマテリアルを生成する material_background = bpy.data.materials.new("Background Material") # シェーダーのノードを使用するために「ノードを使用」を有効化する material_background.use_nodes = True # 「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF_background = material_background.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」を設定する Principled_BSDF_background.inputs[0].default_value = (background_color[0], background_color[1], background_color[2], 1.0) # 「メタリック」を設定する Principled_BSDF_background.inputs[6].default_value = background_color[3] # 「粗さ」を設定する Principled_BSDF_background.inputs[9].default_value = background_color[4] # 「伝播」を設定する Principled_BSDF_background.inputs[17].default_value = background_color[5] # マテリアルスロットを追加する bpy.ops.object.material_slot_add() # 追加したマテリアルスロットに、新しいマテリアルを設定する bpy.context.object.active_material = material_background # render()|レンダリングを行ってファイルに出力する def render(filepath): # ポイントライトを追加する # POINTライトなので、カメラの少しだけ前にライトが来るように位置を調整する bpy.ops.object.light_add(type = "POINT", align = "WORLD", radius = 1.0, location = (0.0, 0.0, 0.8)) # ポイントライトの強さを設定する bpy.context.object.data.energy = 500 # カメラを追加する # テキストの上部、なおかつライトより少しだけ上部にカメラを配置する bpy.ops.object.camera_add(align = "VIEW", location = (0.0, 0.0, 1.0)) # カメラを設定する bpy.context.scene.camera = bpy.context.object # レンダリングの画像フォーマットを設定する bpy.context.scene.render.image_settings.file_format = "PNG" # レンダリングの解像度を設定する bpy.context.scene.render.resolution_x = 1920 bpy.context.scene.render.resolution_y = 1080 bpy.context.scene.render.resolution_percentage = 100 # 出力先のファイル名を設定する # 必要に応じてファイルパスを変更 output_filepath = filepath # レンダリングを行う bpy.ops.render.render(use_viewport = True, write_still = True) # レンダリング結果をファイルに出力する bpy.data.images["Render Result"].save_render(output_filepath) # main()|プログラムのエントリーポイント if __name__ == "__main__": # レンダリングする文字列を設定する text_data = "Sample" # テキストの色を設定する(赤色, 緑色, 青色, メタリック, 粗さ, 伝播) text_color = (0.0, 0.0, 1.0, 1.0, 0.5, 1.0) # 背景の色を設定する(赤色, 緑色, 青色, メタリック, 粗さ, 伝播) background_color = (0.2, 0.2, 0.2, 1.0, 0.5, 1.0) # すべてのオブジェクトを削除する delete_objects() # オブジェクトを生成する create_object(text_data, text_color, 0.5, background_color) # レンダリングを行ってファイルに出力する render("C:\Sample\Text.png") // 実行結果 「スクリプト作成」の画面 「Text.png」
上記のサンプルプログラムでは、POINTライトを使用しているため、ライトの距離やカメラとの位置関係を細かく調整していることがポイントです。ライトをカメラより手前に出すことで、陰影をはっきり表現できます。
オブジェクトのレンダリングは、以下のサンプルコードのように行いましょう。
// サンプルプログラム # Blender関連のオブジェクトをimportする import bpy import math import bmesh # randomライブラリをimportする import random # numpyライブラリをnpというエイリアスでimportする import numpy as np # delete_objects()|すべてのオブジェクトを削除する def delete_objects(): # すべてのコレクションを削除する for collection in bpy.data.collections: for item in collection.objects: collection.objects.unlink(item) bpy.data.objects.remove(item) # すべてのシーンオブジェクトを削除する for item in bpy.context.scene.collection.objects: bpy.context.scene.collection.objects.unlink(item) # すべてのデータオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # すべてのカメラを削除する for item in bpy.data.cameras: bpy.data.cameras.remove(item) # すべてのメッシュを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # すべてのマテリアルを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) # すべてのライトを削除する for item in bpy.data.lights: bpy.data.lights.remove(item) # generate_lissajous_curve()|パラメータに応じていた「リサジュー曲線」を生成する def generate_lissajous_curve(param): return (np.cos(param * 3) * 6, np.sin(param * 3) * 6, np.cos(param * 5) * 6) # create_objects()|レンダリング対象のオブジェクトを生成する def create_objects(): # 100個のオブジェクトを生成するために、ベース値をリストに格納する list = np.linspace(0, np.pi, 100) # 100個のオブジェクトを生成する for i in range(len(list)): # オブジェクトの座標を「リサジュー曲線」で設定する position = generate_lissajous_curve(list[i]) # トーラスを生成する bpy.ops.mesh.primitive_torus_add() # オブジェクトを移動する bpy.ops.transform.translate(value = position) # オブジェクトをランダムに回転させる bpy.ops.transform.rotate(value = math.pi * random.random(), orient_axis = "X") bpy.ops.transform.rotate(value = math.pi * random.random(), orient_axis = "Y") bpy.ops.transform.rotate(value = math.pi * random.random(), orient_axis = "Z") # オブジェクトをランダムにリサイズする bpy.ops.transform.resize(value = (0.5 + random.random() / 2.0, 0.5 + random.random() / 2.0, 0.5 + random.random() / 2.0)) # 新しいマテリアルを生成する material = bpy.data.materials.new('Random') # シェーダーのノードを使用するために「ノードを使用」を有効化する material.use_nodes = True #「プリンシプルBSDF」のインスタンスを生成する # 言語設定が英語の場合はエラーが出るので、「Principled BSDF」にする Principled_BSDF = material.node_tree.nodes["プリンシプルBSDF"] # 「ベースカラー」をランダムに設定する Principled_BSDF.inputs[0].default_value = (random.random(), random.random(), random.random(), 1.0) # 「メタリック」を1.0に設定する Principled_BSDF.inputs[6].default_value = 1.0 # 「粗さ」を0.5に設定する Principled_BSDF.inputs[9].default_value = 0.5 # 「伝搬」を1.0に設定する Principled_BSDF.inputs[17].default_value = 1.0 # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material) # set_background()|背景を設定する def set_background(): # 背景を追加する # オブジェクトと背景が重ならないように、テキストの厚み分だけ位置を調整する bpy.ops.mesh.primitive_plane_add(size = 35, location = (0.0, 8.0, 0.0), rotation = (math.radians(90), 0.0 ,0.0)) # 背景用のマテリアルを生成する material_background = bpy.data.materials.new("material_background") # シェーダーのノードを使用するために「ノードを使用」を有効化する material_background.use_nodes = True # マテリアルのノードを取得する nodes = material_background.node_tree.nodes # デフォルトのノードを削除する nodes.clear() # 「プリンシプルBSDF」のシェーダーノードを追加する node_shader = nodes.new(type = "ShaderNodeBsdfPrincipled") # テクスチャのノードを追加する node_texture = nodes.new("ShaderNodeTexImage") # テクスチャの画像ファイルを読み込む node_texture.image = bpy.data.images.load("C:\Sample\Sky.png") # 出力ノードを追加する node_output = nodes.new(type = "ShaderNodeOutputMaterial") # マテリアルのリンク情報を取得する links = material_background.node_tree.links # すべてのノードをリンクさせる link = links.new(node_texture.outputs["Color"], node_shader.inputs["Base Color"]) link = links.new(node_shader.outputs["BSDF"], node_output.inputs["Surface"]) # メッシュオブジェクトにマテリアルを適用する bpy.context.object.data.materials.append(material_background) # process_render()|レンダリングのプロセスを設定する def process_render(filepath): # サンライトを追加する # 距離の影響は受けないが、方向が重要なのでオブジェクトに向ける bpy.ops.object.light_add(type = "SUN", align = "WORLD", location = (0.0, 0.0, -30.0), rotation = (math.radians(90), 0.0, 0.0)) # サンライトの強さを設定する bpy.context.object.data.energy = 10 # カメラを追加する # オブジェクト全体が映るように位置と回転量を調整する bpy.ops.object.camera_add(align = "VIEW", location = (0.0, -35.0, 0.0), rotation = (math.radians(90), 0.0, 0.0)) # カメラを設定する bpy.context.scene.camera = bpy.context.object # レンダリングの画像フォーマットを設定する bpy.context.scene.render.image_settings.file_format = "PNG" # レンダリングの解像度を設定する bpy.context.scene.render.resolution_x = 1920 bpy.context.scene.render.resolution_y = 1080 bpy.context.scene.render.resolution_percentage = 100 # 出力先のファイル名を設定する output_filepath = filepath # レンダリングを行う bpy.ops.render.render(use_viewport = True, write_still = True) # レンダリング結果をファイルに出力する bpy.data.images["Render Result"].save_render(output_filepath) # main()|プログラムのエントリーポイント if __name__ == "__main__": # すべてのオブジェクトを削除する delete_objects() # レンダリング対象のオブジェクトを生成する create_objects() # 背景を設定する set_background() # レンダリングのプロセスを設定する process_render("C:\Sample\Lissajous.png") // 実行結果 「スクリプト作成」の画面 「Lissajous.png」
今回のサンプルプログラムでは、SUNライトを使用しているため、光源の位置による影響はありません。しかし、光源の角度によって結果がまったく異なるため、オブジェクトのほうに向けることが重要です。背景に影が描写されるため、屋外で撮影したようなリアルな画面を構成できます。
Blenderの3Dモデリング・CG制作をPythonで行う際は、バージョンによる仕様の変化に注意が必要です。たとえば、Blenderのバージョン2とバージョン3では、「プリンシプルBSDF」のパラメータに対応するインデックスが異なります。これは、バージョン3ではプリンシプルBSDFのアルゴリズムや、設定できる項目数が変更されたからです。
古いバージョン向けのサンプルコードを実行すると、マテリアルの設定が正しく反映されません。また、Blender Python APIのオブジェクトや関数の仕様が変更されて、ソースコード自体が動作しない可能性もあります。公式ドキュメントやシェーディングのノード画面などを確認して、常に最新情報を反映できるようにしておきましょう。
Blenderの各種操作をPythonで自動化できる、「Blender Python API」の使い方や機能について解説しました。Pythonスクリプトを作成・実行することで、メッシュの生成・マテリアルの適用・レンダリングまで一連の工程を自動化できます。今回ご紹介したように、手作業では相当の時間がかかる高度なCG制作を、コーディングで効率化できます。この機会にぜひ、Blender Python APIでのCGプログラミングにチャレンジしてみてください。
2024.06.17
子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介
#プログラミングスクール
2022.01.06
【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説
#プログラミングスクール
2024.01.26
【未経験でも転職可】30代におすすめプログラミングスクール8選!
#プログラミングスクール
2024.01.26
初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説
#JAVA
2024.01.26
忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説
#プログラミングスクール
2022.01.06
【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介
#プログラミングスクール
2024.01.26
【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説
#プログラミングスクール
2024.01.26
【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説
#プログラミングスクール
2024.01.26
【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説
#プログラミングスクール
2024.01.26
【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説
#プログラミングスクール