Geometry Nodesは信じられないほど優れたBlenderの機能ですが、BlenderのPython APIでも、他のデータブロックと同じようにGeometry Nodesをスクリプトできることをご存じでしたか?
ノードを作成し、パラメータを設定し、プログラム的に接続することで、数行のコードだけでシーン生成を自動化したり、カスタムツールを作ったり、手作業で何十個ものノードを配線する代わりに、モデルのプロトタイプを素早く作成したりできるようになります。
このチュートリアルでは、Pythonスクリプトだけでジオメトリノードのセットアップをすべて作成する方法を学びます。新しいノードツリーを構築するところから、それをオブジェクトに割り当てるところまで、Blenderのスクリプトエディタにそのまま貼り付けられる明確な例を使って、全手順を解説します。
見逃していた場合は、まずBlenderスクリプトの入門をご覧ください。
なぜGeometry Nodesをスクリプトするのか?
BlenderのGeometry Nodesエディタは、手続き的ツールを作るための優れたビジュアルシステムです。直感的で柔軟性が高く、使い方を掴めば実験もしやすいです。とはいえ、プロジェクトが複雑になってくると、大きなノードネットワークを手作業で管理するのは面倒になり、特に多数の3Dモデリングパイプライン全体で再利用する必要がある場合は、保守が難しくなります。
スクリプトを使うと、ノードを自動生成・変更・接続できます。同じセットアップを複数のプロジェクトで毎回作り直す代わりに、スクリプトを書いておけば必要なときにいつでも再利用でき、時間を節約したり、アニメーションをより一貫したものにしたりできます。
スクリプトされたノードセットアップは、単一の.blendファイルに紐づきません。コードの一部と同じように保存でき、バージョン管理でき、共有もできます。これにより、さまざまなプロジェクトで再利用したり、他のアーティストや開発者と共有したりできる手続き的ツールのライブラリを作りやすくなります。
では、いくつかのコードスニペットで、スクリプトが実際にどのように機能するのか見てみましょう。
このガイドで紹介されている例の統合の完全なソースコードは、GitHub上で確認できます:
🔗 https://github.com/cgwire/blender-scripting-geometry-nodes
1. 新しいノードツリーを作成する
すべてのGeometry Nodesのセットアップはノードツリーとして始まり、ノードとそれらの接続を保持します。このようなツリーは、BlenderのデータAPIを使ってPythonから作成できます:
import bpy
node_tree = bpy.data.node_groups.new("MyGeoNodesTree", 'GeometryNodeTree')
このnode_treeを、すべての手続き的ロジックを保持するデジタルキャンバスだと考えると分かりやすいです。作成したら、ノードを追加し、接続し、プロパティをBlenderのグラフィカルユーザーインターフェースと同じように設定できます。
2. ノードを追加して接続する
次に、いくつかの基本ノードを追加しましょう。Input Geometryノード、Subdivision Surfaceノード、Group Outputノードを作成し、接続してから、その結果をキューブに適用します。
# ADD NODES geo_input = node_tree.interface.new_socket( name="Geometry", in_out='INPUT', socket_type='NodeSocketGeometry' ) geo_output = node_tree.interface.new_socket( name="Geometry", in_out='OUTPUT', socket_type='NodeSocketGeometry' )input_node = node_tree.nodes.new("NodeGroupInput") subdivide_node = node_tree.nodes.new("GeometryNodeSubdivideMesh") output_node = node_tree.nodes.new("NodeGroupOutput")
input_node.location = (-300, 0) subdivide_node.location = (0, 0) output_node.location = (300, 0)
LINK NODES
node_tree.links.new(input_node.outputs'Geometry', subdivide_node.inputs'Mesh') node_tree.links.new(subdivide_node.outputs'Mesh', output_node.inputs'Geometry')
APPLY TO CURRENT OBJECT
obj = bpy.context.object mod = obj.modifiers.new("MyGeoNodesModifier", "NODES") mod.node_group = node_tree
このスクリプトを実行すると、適用した任意のジオメトリをサブディビジョンする、動作する(ただしシンプルな)ジオメトリノードのセットアップが得られます:

3. パラメータを設定し、ジオメトリをオブジェクトにリンクする
ノードのプロパティ経由で、パラメータを直接変更できます。たとえば、サブディビジョンレベルを上げて、このノードグループをオブジェクトに適用してみましょう:
subdivide_node.inputs'Level'.default_value = 3
入力に対するdefault_valueを調整するのは、セットアップをパラメータ化する簡単な方法です。
利用可能なパラメータとタイプの完全な内訳については、公式のBlender Python APIドキュメントを参照してください。
4. カスタム「Cube Crowd Generator」ノードグループをプログラム的に作成する
ここまで、Geometry Nodesをプログラム的に定義する方法は分かりましたが、再利用可能なカスタムノードを作るにはどうすればよいのでしょうか?
では、表面上に多数のキューブを散布する小さな手続き的システムを構築する新しい例に取り組みましょう。このスクリプトは、表面を入力として受け取り、その上に点を散布し、それらの点をランダムにオフセットし、各点にキューブを配置(インスタンス)し、インスタンスを実際のジオメトリに変換し、最終的なメッシュを「Cubes」として出力する、Geometry Nodesのグループを作成します。
1) 新しいノードグループを作成する
まず、Blenderで新しいGeometry Nodeグループを作成し、その名前を"CubeCrowdGenerator"にします。
crowd_group = bpy.data.node_groups.new("CubeCrowdGenerator", "GeometryNodeTree")関数のように、このノードを後でGeometry Nodesモディファイアと一緒に任意のオブジェクトに取り付けられるようにしたいです。
2) グループの入力・出力ノードを追加する(UI/エントリポイント)
いつも通り、キャンバス上に標準の入出力グループを配置します:
group_in = crowd_group.nodes.new("NodeGroupInput") group_out = crowd_group.nodes.new("NodeGroupOutput")
group_in.location = (-600, 0) group_out.location = (600, 0)
group_inとgroup_outは、Geometry Nodesエディタ上で表示されるノードグループのソケットです。- スクリプトは、グラフが読みやすくなるように、それらの位置も配置します。
3) グループインターフェースを定義する(何を受け取り、何を返すか)
入力ソケット「Surface」を公開して、そこに埋め込みたいメッシュ(例:プレーン)を差し込みます。そして、結果として生成されるジオメトリを返す出力ソケット「Cubes」を用意する必要があります。
interface = crowd_group.interface
interface.new_socket(name="Surface", in_out="INPUT", socket_type="NodeSocketGeometry")
interface.new_socket(name="Cubes", in_out="OUTPUT", socket_type="NodeSocketGeometry")実際には、このノードグループをオブジェクトに追加するとき、その表面(オブジェクトの元のジオメトリ)をSurfaceに接続することになります。
4) 内部ノード(構成要素)を作成する
次に、実際の内部ロジックを作っていきます:
distribute = crowd_group.nodes.new("GeometryNodeDistributePointsOnFaces")
rand_vec = crowd_group.nodes.new("FunctionNodeRandomValue")
set_pos = crowd_group.nodes.new("GeometryNodeSetPosition")
cube = crowd_group.nodes.new("GeometryNodeMeshCube")
instance = crowd_group.nodes.new("GeometryNodeInstanceOnPoints")
realize = crowd_group.nodes.new("GeometryNodeRealizeInstances")- GeometryNodeDistributePointsOnFaces: 入力サーフェス上に点を作成します(点の数や分布を制御します)。
- FunctionNodeRandomValue(Float Vector): 各点ごとに使う、オフセット用のランダムな3Dベクトルを生成します。
- GeometryNodeSetPosition: ベクトル(ランダムオフセット)により、各点を移動します。
- GeometryNodeMeshCube: インスタンスとして使われるキューブメッシュを生成します。
- GeometryNodeInstanceOnPoints: 各点にキューブを配置します。これは実際のジオメトリを作るわけではなく、元のキューブの安価なインスタンスを作るだけです。
- GeometryNodeRealizeInstances: インスタンスを実際のメッシュジオメトリに変換し、1つのメッシュとして出力できるようにします。
5) ランダムベクトルノードを設定する
Random Valueノードを、3成分のベクトル として返すように設定し、3D空間内で生成されたキューブをオフセットするために使います:
rand_vec.data_type = "FLOAT_VECTOR"
rand_vec.inputs"Min".default_value = (-0.5, -0.5, 0.0)
rand_vec.inputs"Max".default_value = (0.5, 0.5, 0.5)MinとMaxは各成分の範囲を定義します。たとえばXは-0.5から0.5の間になります。- 結果:各点にわずかに異なるオフセットが割り当てられるため、キューブが互いの上に完全に重なって配置されることはありません。
6) ノードレイアウト(UIのみ)
Blenderでワークフローを確認したいときに分かりやすいように、内部ノードを配置します:
distribute.location = (-400, 0)
rand_vec.location = (-200, -200)
set_pos.location = (-100, 0)
instance.location = (100, 0)
cube.location = (-400, -200)
realize.location = (300, 0)これらのlocationの割り当ては、ノードエディタ上での見た目の配置だけに影響します。グラフが行う処理内容には影響しません。
7) ノードをつなぐ
最後に、データの流れを定義します:
links.new(group_in.outputs"Surface", distribute.inputs"Mesh")
links.new(distribute.outputs"Points", set_pos.inputs"Geometry")
links.new(rand_vec.outputs"Value", set_pos.inputs"Offset")
links.new(set_pos.outputs"Geometry", instance.inputs"Points")
links.new(cube.outputs"Mesh", instance.inputs"Instance")
links.new(instance.outputs"Instances", realize.inputs"Geometry")
links.new(realize.outputs"Geometry", group_out.inputs"Cubes")- Surface → DistributePointsOnFaces: 入力サーフェス(プレーン)が散布用の点を作るために使われます。
- Points → SetPosition(Geometry): set positionは、移動されるジオメトリとして点を受け取ります。
- RandomValue → SetPosition(Offset): 各点にランダムなベクトルのオフセットが付与されます。
- SetPosition → InstanceOnPoints(Points): 移動された点が、インスタンスのアンカーポイントになります。
- Cube Mesh → InstanceOnPoints(Instance): 各点にキューブのインスタンスが割り当てられます。
- InstanceOnPoints → RealizeInstances: インスタンスがメッシュジオメトリに変換されます。
- RealizeInstances → Group Output("Cubes"): 最終結果がグループ出力として利用可能になります。
これが、得られた完全なコードです:
import bpyCreate a new Geometry Node group
crowd_group = bpy.data.node_groups.new("CubeCrowdGenerator", "GeometryNodeTree")
Create input/output nodes
group_in = crowd_group.nodes.new("NodeGroupInput") group_out = crowd_group.nodes.new("NodeGroupOutput")
group_in.location = (-600, 0) group_out.location = (600, 0)
Define group interface sockets
interface = crowd_group.interface interface.new_socket(name="Surface", in_out="INPUT", socket_type="NodeSocketGeometry") interface.new_socket(name="Cubes", in_out="OUTPUT", socket_type="NodeSocketGeometry")
Create internal nodes
distribute = crowd_group.nodes.new("GeometryNodeDistributePointsOnFaces") instance = crowd_group.nodes.new("GeometryNodeInstanceOnPoints") cube = crowd_group.nodes.new("GeometryNodeMeshCube") realize = crowd_group.nodes.new("GeometryNodeRealizeInstances") set_pos = crowd_group.nodes.new("GeometryNodeSetPosition") rand_vec = crowd_group.nodes.new("FunctionNodeRandomValue")
Configure random vector node
rand_vec.data_type = "FLOAT_VECTOR" rand_vec.inputs"Min".default_value = (-0.5, -0.5, 0.0) # minimum offset rand_vec.inputs"Max".default_value = (0.5, 0.5, 0.5) # maximum offset
Layout nodes
distribute.location = (-400, 0) rand_vec.location = (-200, -200) set_pos.location = (-100, 0) instance.location = (100, 0) cube.location = (-400, -200) realize.location = (300, 0)
Create links
links = crowd_group.links links.new(group_in.outputs"Surface", distribute.inputs"Mesh") links.new(distribute.outputs"Points", set_pos.inputs"Geometry") links.new(rand_vec.outputs"Value", set_pos.inputs"Offset") links.new(set_pos.outputs"Geometry", instance.inputs"Points") links.new(cube.outputs"Mesh", instance.inputs"Instance") links.new(instance.outputs"Instances", realize.inputs"Geometry") links.new(realize.outputs"Geometry", group_out.inputs"Cubes")
次に、このスクリプトをスクリプトワークスペースにコピー&ペーストして実行すれば、ジオメトリノードのワークスペースからこのカスタムノードを追加できるようになります:

それが中で何をしているのか確認するには、それをダブルクリックしてノードグループを開きます:

まとめ
数十行のコードだけで、手作業で組み立てるのにずっと時間がかかるようなGeometry Nodesのセットアップをスクリプト化できます。この記事では、Geometry Nodeツリーの作成、ノードの追加と接続をプログラム的に行うこと、パラメータを制御してノードツリーをオブジェクトに割り当てること、そして完全な手続き的システムを構築する方法を学びました。
例を自分で試すには、Githubのコードリポジトリをご覧ください。

このアプローチにより、ツール開発からジェネレーティブアートまで、無限の自動化の可能性が開かれます。


