[Godot Engine] 3D空間にラインを引く方法

2 min読了の目安(約2100字TECH技術記事

Godot Engineについては以前の記事をご覧ください。

今回は3D空間の好きな座標にラインを引く方法を共有します。
CanvasItemを継承するNode2DControlには_draw()メソッドが用意されているので、割と容易にビューポート上にラインを引くことはできるのですが、Spatialには_draw()メソッドはないので、自前でどうにかする必要があります。

自分のプロジェクト用に、ラインを複数引いてグリッドにしてみました。
動的にグリッドサイズを変えたりできていい感じ。

どうにかする方法も色々あるのですが、パッと考えつくのは以下でしょうか。

  1. 3D座標を2D座標に変換してCanvasItemで描画してViewportに表示する
  2. CanvasItemに描画したものを3D空間のポリゴンに貼り付ける
  3. 3D空間に頂点を追加してポリゴンを作る

この記事で紹介するのは3です。

ImmediateGeometryクラスを使用

ImmediateGeometryは単純なポリゴン描画を提供するクラスです。
フレームごとに変化する少量の頂点を扱うのに適したクラスですが、より複雑で最適化が必要となる頂点データを動的に扱う場合は、ArrayMeshMeshToolSurfaceToolを使用する方が良いです。

単純な描画方法

extends ImmediateGeometry

func _ready():
	begin(Mesh.PRIMITIVE_LINES)
	set_color(Color.red)
	add_vertex(0, 0, 0)
	add_vertex(0, 1, 0)
	end()

使い方は非常にシンプルで、begin()end()の間でadd_vertex()で頂点を追加していくだけです。

begin()の引数でMesh.PRIMITIVE_LINESを設定しています。
これで、begin()からend()の間で追加された頂点をラインで描画する という設定になります。

他にもenumで定義されたPrimitiveTypeがありますが、今回はラインを描画するだけなので、PRIMITIVE_LINESを使用します。

頂点を追加する間にset_color(Color)を指定すると、頂点の色を設定できます。
しかしこのままでは色は描画されず、デフォルトのシェーディングになっているので、環境光の影響を受けたポリラインが表示されます。

マテリアルオーバーライド

通常の3Dモデルだとメッシュに対してマテリアルを設定しますが、今回は動的にメッシュを作っているので、ポリラインの生成が終わったジオメトリに対してマテリアルをオーバーライドして描画します。

VisualServerを介してスクリプトで動的にマテリアルを作る事もできますが、処理負荷の割に意味があまりないので、インスペクタ上でSpatialマテリアルを設定します。

設定したらスクリプトに戻ります。

extends ImmediateGeometry

func _ready():
	# 以下はインスペクタ上で設定してもOK
	var _mat = get_override?material()
	_mat.flags_unshaded = true
	_mat.flags_no_depth_test = true
	_mat.vertex_color_use_as_albedo = true
	
	begin(Mesh.PRIMITIVE_LINES)
	set_color(Color.red)
	add_vertex(0, 0, 0)
	add_vertex(0, 1, 0)
	end()

flags_unshadedで環境光などをすべて切って、ジオメトリの色をそのまま表示させます。
vertex_color_use_as_albedoは頂点カラーをアルベドカラーとして表示します。
flags_no_depth_testで最前面に描画させます(別のメッシュで隠れても見えるように)

さいごに

やることはシンプルなんですが、意外と情報が少なかったので備忘録として記事にしました。
ImmediateGeometryは非常にシンプルなポリゴン描画クラスなので、結構使いそうです。
多少描画負荷は上がりますが、デバッグモードで使うことはかなり多いと思います。

これを機にArrayMeshMeshToolに触れながら、プロシージャルにアセットを作るアドオンを作りたいなぁ(野望)

そしてZennではこのくらいショートな記事を今後も作れたらいいなーと思ってます。