AKARI Tech Blog

燈株式会社のエンジニア・開発メンバーによる技術ブログです

Pythonで3次元CGを作りたい人のためのPyVista入門

こんばんは! 今週のAKARI Tech Blogは、DX Solution 事業本部 Dev の小山が担当いたします。
本日は、Pythonで3次元CGを作りたい人のためのPyVista入門と題して、Pythonで3次元CGを作成する方法についてお話しします。
本記事はPyconJP2024で筆者がトークした内容を記事にしたものです。

まずはPythonでCGを作るのに必要なことの概要をお話し、その後、実際に3次元CGを作成する方法を紹介します。
モデリング、テクスチャ、マテリアル、ライティングというCGを作るための基本的な要素について説明します。
次に空間のデータ分析を行うデモを行います。
最後に応用例としてインタラクティブな可視化の方法について説明をします。

PythonでCG作成

CGを作るのが初めての方もいるかもしれません。そこで、まずはPythonでCGを作るのに必要なことについて説明します。

モデリング

まずはモデリングについて説明します。
モデリングは、仮想3次元空間上に個々のポリゴンをつくる作業です。
これは、CGを作る際に最も基本的な作業です。この作業を行うことで、仮想3次元空間上に自分の作成したい物体を配置することができます。

注意:この記事のほとんどの画像はインタラクティブです。回転して全体を確認してください。

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-1_00_00.png

テクスチャ

次にテクスチャについて説明します。
テクスチャは、オブジェクトの質感を表現するための画像です。
先程のモデリングで作成したオブジェクトに、テクスチャを貼り付けることで、CGをよりリアルに表現することができます。

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-2_00_00.png

ライティング

ライティングは、3D空間に光を配置してオブジェクトを照らすことです。
光源を配置することで、モデリングしたオブジェクトに影をつけることができます。これにより、CGをよりリアルに表現することができます。

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-3_00_00.png

PyVistaとは?

以上の要素を組み合わせて、3次元CGを作成します。
これらのCG作成作業をそれぞれPythonで実現をする方法を考えた際に一番今現状で使いやすいライブラリが我々が開発しているPyVistaです。
PyVistaは、MatplotlibやPandasのAPIを意識して作成しているため、これらのライブラリを使える人は簡単に使えます。
皆さんの中でもMatplotlibを使用して描画をされている方はいらっしゃると思います。Matplotlibは2次元のグラフを描画するにはとても強力なライブラリですが、3次元プロットの機能はそれほど強力ではありません。
そのため、3次元の空間情報や物体がどのように変形するかなどの表現をするには機能が不足しています。また、Matplotlibで実現できないCGの表現もPyVistaで実現できます。

インストールは、pipコマンドでインストールすることが可能です。
condaコマンドのパッケージも用意はされていますが、pipでインストールするのが一般的です。標準ではJupyterの拡張機能がインストールされませんが、Allというオプションをつけることで拡張をインストールすることができます。

pip install "pyvista[all]"

モデリング

それでは、始めましょう。まずは、モデリングの方法について説明します。

Pythonを起動して、PyVistaをインポートします。
Pythonのライブラリには、エイリアスをつけることができます。Numpyを np というエイリアスでインポートするのと同じように、PyVistaを pv というエイリアスでインポートします。これはNumpyの np と同じように、PyVistaで慣用的に使われるエイリアスです。
次に、 Cylinder()という関数を使って円柱のモデルを作成します。
PyVistaでは、様々なモデルを作成する関数が用意されています。この関数を使って、簡単にモデルを作成することができます。
作成したモデルを plot() 関数で表示すると、円柱のモデルが表示されます。こちらは、今回のスライドの目玉機能なのですが、こちらのInteractive Sceneというタブをクリックすると、3Dモデルを自由に回転させることができます。
これが、どのような仕組みで動いているかは、後半の応用編で説明をします。

# PyVistaをインポートする。
import pyvista as pv

# 円柱のモデルを作成する。
mesh = pv.Cylinder()

# 円柱のモデルを描画する。
mesh.plot()

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-4_00_00.png

また複数のモデルを作成することも可能です。
Matplotlibにおいては、複数のグラフを描画する場合、Figureオブジェクトを使って複数のグラフを描画することができます。
PyVistaでも同様に、Plotterオブジェクトを使って複数のモデルを描画することができます。こちらのコードでは、Plotterオブジェクトを使って、円柱を2つ描画しています。
まず、Plotterオブジェクトを作成し、 add_mesh() 関数を使って円柱を追加します。さらに、もう1つ矢印を追加し、追加されたモデルを show() メソッドで表示します。

# Plotterオブジェクト

pl = pv.Plotter()

# 円柱を追加します

mesh = pv.Cylinder()
pl.add_mesh(mesh)

# 矢印を追加します

mesh = pv.Arrow()
pl.add_mesh(mesh)

# 追加されたモデルを描画します

pl.show()

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-5_00_00.png

テクスチャ

次にオブジェクトの質感を表現する「テクスチャ」の方法を紹介します。

テクスチャは、先ほどご説明申し上げた通り、物体をよりリアリティのあるように見せるために表面に画像を追加をするという操作CG上の操作です。
ここでは、テクスチャという方法を使用して、オブジェクトに画像を貼り付けます。これをPyVistaで実現をする場合、 まずはテクスチャーに使用する画像をロードします。その次に画像をテクスチャとして読み込みます。テクスチャの画像はモデルのどの位置に貼り付けるかを定義する必要があります。
今回は説明の時間の都合上、その定義がすでに行われているものを使用します。これを先ほどのPlotterオブジェクトの texture という引数に定義をしてあげると、こちらの右のようにテクスチャーが円筒貼り付けられます。

# テクスチャに使用する画像を読み込み

from pyvista import examples as ex

filename = ex.mapfile

# 画像をテクスチャとして読み込み

texture = pv.read_texture(filename=filename)

# テクスチャをオブジェクトに貼り付け

mesh.plot(texture=texture)

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-6_00_00.png

ライティング

次にライティングについてご説明をします。

先程ご説明した通り、ライティングは今まで作成をしたオブジェクトに光を当てることで、光と影を表現する操作です。
PyVistaには Light クラス が用意されています。これを設定しPlotterオブジェクトに追加することで仮想空間上の3Dオブジェクトに光を当てることが可能になっています。
ちなみに、Plotterオブジェクトにはデフォルトでライティングが有効になっています。そのため、ライティングを新しく定義する場合は lighting='none'というオプションを指定することでデフォルトのライティングを無効にします。
次に仮想3D空間に配置する光の光源位置と光源の種類を定義します。この例では、光源の位置を(0, 1, 0)に設定し、光源の種類を 'scene light' に設定しています。この光をPlotterオブジェクトに設定すると、右のように右斜め手前から光が当てられた状態になります。

# Plotterクラスでlightingを無効にします。
plotter = pv.Plotter(lighting='none')

# 仮想3D空間に光を配置します。
light = pv.Light(
    position=(0, 0, 1),
    light_type='scene light'
)

# Plotterクラスに光を追加します。
pl.add_light(light)
plotter.show()

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-7_00_00.png

# 光源の位置を(0, 0, 1)に変更します
light = pv.Light(
    position=(0, 0, 1),
)

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-8_00_00.png

マテリアル

先ほどテクスチャを使って画像を貼り付けることで質感を表現することをしましたが、あまりリアリティがありません。
そこで、背景を設定して、背景の映り込みをテクスチャとして設定することで、よりリアリティのあるCGを作成してみます。

スカイボックス

まずは、映り込みに使用する背景を表示してみましょう。
ゲームなどのCGを作成する際には、背景にスカイボックスを設定することが一般的です。下の画像がスカイボックスの例です。(https://en.wikipedia.org/wiki/Skybox_(video_games)より引用)

上下左右前後の6つの面の画像を背景に設定することで全方向に背景を表示することができます。
PyVistaでは、 download_sky_box_cube_map() 関数を使って、標準のスカイボックスをダウンロードすることができます。
以下がスカイボックスを表示した例です。中央にサンプルの球が表示されています。
これを使って、背景の映り込みをテクスチャとして設定することで、よりリアリティのあるCGを作成してみます。

from pyvista import examples as ex

# スカイボックスをダウンロードする

cube_map = ex.download_sky_box_cube_map()

cube_map.plot()

質感と背景の映り込み

それでは、質感と背景の映り込みを追加してみましょう。
まずは、スカイボックスを背景に設定します。
その次に、背景の映込をテクスチャとして設定します。映り込みを表現する際にはオブジェクトの表面に反射する光の強さを設定する必要があります。これは物理ベースレンダリングと呼ばれる手法を使って表現することができます。
この機能を使用するにはpbr(Physically Based Renderingの略)のフラグをTrueに設定します。

# スカイボックスを背景に設定する
pl.add_actor(cube_map.to_skybox())
# 背景の映込をテクスチャとして設定する
pl.set_environment_texture(cube_map)

# 物理ベースレンダリングを使用して
# 表面に反射する光の強さを設定する
pl.add_mesh(
   mesh,
   pbr=True,
   metallic=0.8,
   roughness=0.1,
   diffuse=1
)

空間のデータ分析

PythonでCGを表示することができましたが、せっかくPythonを使用しているので3D空間のデータ分析もしてみることをお勧めします。
PyVistaではCGを表示するだけでなく、ポリゴンにデータを持たせPandasのように処理をするメソッドが整備されています。

上記の処理は threshold というPyVistaのフィルタを使用して海域のデータを抽出し、地球儀の上にプロジェクションマッピングをしている様子です。

# 海域の領域を抽出する
sea_region = region.threshold()

空間のデータ可視化

最後に分析結果可視化の方法についてご紹介します。
皆様は、Pythonでコードを書いて、その結果を見るときに、どのような方法を使っていますか?
Pythonのエコシステムは非常に豊富で、様々な結果の処理ツールがあります。ここでは、Sphinx、Jupyter Notebook、Streamlitといったツールを使って、Pythonで3次元CGを作成する方法を紹介します。

Sphinxによる可視化

まずは、Sphinxを使って、Pythonで3次元CGを作成する方法を説明します。
SphinxはPythonのドキュメントを作成するためのツールです。Sphinxを使用するとPythonのコードをドキュメントに埋め込むことができます。
PyVistaをインストールすると、Sphinxのドキュメントにコードを埋め込むのと同じ方法でPyVistaの3D可視化のコードを埋め込むことができます。

  code-block:: rst

     pyvista-plot::
        :include-source: False

        # pyvista-plotディレクティブを使って、
        # Sphinxドキュメントに右のような
        # 3D可視化を追加することができます。

        import pyvista as pv
        mesh = pv.Cylinder()
        mesh.plot()

        # このスライドもSphinxで作成しています。
        # 詳しくはsphinx-revealjsで検索!

以下のプロットはSphinxでビルドしたものです。ここまでの画像も全てSphinxで作成しました。

https://69a17c8b0f20c8735d525079--aesthetic-toffee-c06202.netlify.app/_images/index-11_00_00.png

Jupyterによる可視化

また、Jupyter Notebookを使って、Pythonで3次元CGを作成する方法もあります。
PyVistaは標準でJupyter Notebookでの可視化をサポートしています。Jupyter Notebookを使っている方も多いと思いますが、PyVistaを使えば、Jupyter Notebook上でインタラクティブな可視化が可能です。

Streamlitによる可視化

近年、StreamlitのようなWebUIフレームワークが注目されています。
PyVistaを、StreamlitやPanelを使えば、Webアプリケーションとしても可視化が可能です。これにより、Pythonで3次元CGを作成する際に、より効率的に作業ができるようになります。現在、公式ではこの機能はサポートされていませんが、サードパーティ製のツールを使うことで、Webアプリケーションとしての可視化も可能です。
こちらは、Streamlitを使って作成したWebアプリケーションの例です。stpyvistaというサードパーティ製のツールを使って、PyVistaの3D可視化をStreamlitで表示しています。

最後に

ここまで読んでいただきありがとうございました。
本日は、Pythonで3次元CGを作る方法についてお話ししました。また、空間上のデータを使用して分析する方法や、可視化の方法についても説明しました。この記事が皆様のお役に立てれば幸いです。

PyVistaは以下の開発者によりボランティアでメンテナンスされています。
もしもこのライブラリが気に入ったら、https://github.com/pyvista/pyvistaにスターをつけて私たちを応援して下さい。長期的な開発には皆さんの応援が必要です。

We’re Hiring!

燈では、最新技術を社会実装するAIエンジニアを募集しています! 興味がある方は、ぜひカジュアル面談でお話しましょう!

akariinc.co.jp