【Substance Designer + Python】コースティクスの連番画像を出力する

2023/07/23に公開

はじめに

Substance 3D Designer を利用して、以下のような連番の画像を出力する方法について紹介します。

また、作成した連番画像をUnityのImageSequencerを利用してテクスチャシートにまとめる方法についても紹介します。

Unityで再生

連番画像を出力する手法について

Pythonを用いて、以下の作業を行います。

STEP1. ノードのパラメータを少しずらす
STEP2. 画像を出力する

ノードのパラメータが0から1になるまで、STEP1 ~ STEP2 の作業を繰り返し実行します。

環境

Adobeから入手できる最新のSubstance 3D Designerを使用します。

Version
 13.0.1 build 6838 commit 0302e789 Release (06/20/2023)
Engine Version
 [Direct3D 11] 9.0.0 commit 0xc70624cd (06/20/2023)
Bakers Version
 2.5.7 commit 0xe9235188

1. 新規Substance Graph の作成

Ctrl + N から、新規のSubstance Graph を作成します

Substance Graphの作成


作成されたSubstance Graph

作成したSubstance Graphは、Ctrl + S で保存しておきましょう。
今回は、 WaterCaustics.sbs というファイル名で保存することにします。

2. コースティクスの作成

次に、コースティクスを出力するノードを組みます。
使用したノードは、以下の3つになります。

  1. Perlin Noise
  2. RT Caustics Grayscale
  3. output

コースティクスのアニメーション

Disorder パラメータを動かしてみる

ここで、Perlin Noise の Disorder というパラメータを動かしてみましょう。
コースティクスのような、水の動きが生まれます。

このDisorderを、Python側で変化させながら画像として出力することで、
コースティクスのアニメーションを連番画像化できます。

今回の記事では、DisorderパラメータをSubstance Graph用のパラメータとしてExposeし、
PythonからDisorderパラメータを動かすことを行います。

DisorderパラメータのExpose

Disorder の右側にあるアイコンをクリックします。

Disorder の右側にあるアイコンをクリックします。

Expose as new graph input を選択します。

Expose as new graph input を選択

パラメータの名前を progress に変更します。

パラメータの設定

パラメータが以下のような表示になります。

追加されたパラメータの確認

Substance Graph をクリックすると、progress というパラメータがあることが確認できます。

WaterCausticsに追加されたパラメータの確認

3. Pythonを用いた連番画像の出力

pythonを利用して、progressのパラメータを0から1まで少しずつ変化させながら、
これを画像として出力させます。

【補足】Pythonスクリプトの実行手順

【補足】Pythonスクリプトの実行手順

Pythonスクリプトの実行手順を紹介します。


STEP1. Python Editor を開く

メニューから、Python Editor を起動します。

Python Editorが開きます。

STEP2. 簡単なPythonを実行してみる

Python Editorへ、以下のスクリプトを入力します。

01_hello_world.py
print('Hello World')

再生マークのアイコンをクリックすると、スクリプトが実行されます。

Pythonの実行


実行結果

Substance の Python API については、ドキュメントが用意されています。

Pythonで画像を出力する

STEP1. 使用するAPI

画像を出力するには、Substance 3D Designer 付属のexport.pyを利用します。

exportSDGraphOutputsというメソッドを利用することで、画像が出力できます。

export.py
def exportSDGraphOutputs(
        aSDGraph,
        aOutputDir = '',
        aFileExt = 'png'):

STEP2. 連番画像を出力するサンプル (Python)

progressパラメータを0から1へ変化させながら、画像を出力するPythonスクリプトは以下になります。

export_sample_01.py
import os
import sd
from pathlib import Path
from sd.api.sduimgr import *
from sd.api.sdproperty import SDPropertyCategory
from sd.api.sdvaluefloat import *
from sd.tools import export
import subprocess

# 現在開いているSubstance Graphを取得
sdContext = sd.getContext()
sdApplication = sdContext.getSDApplication()
uiMgr = sdApplication.getUIMgr()
sdGraph = uiMgr.getCurrentGraph()

# Packageを取得
sdPackage = sdGraph.getPackage()
filePath = sdPackage.getFilePath()

outputDir = os.path.join(Path(filePath).parent, 'output')

print ("SubstanceGraph {}".format(sdGraph.getIdentifier))
print ("outputDir: {}".format(outputDir))

# パラメータの取得
progressProp = sdGraph.getPropertyFromId('progress', SDPropertyCategory.Input)

# 画像を出力
exportCount = 6 # 出力したい数をここに指定
for i in range(exportCount):

	# ファイル名
	fileName = "{}.png".format(str(i).zfill(2))
	print("export: {}".format(fileName))

	# パラメータの値を設定
	progressValue = float(i) / exportCount 
	sdGraph.setPropertyValue(progressProp, SDValueFloat.sNew(progressValue))

	# 画像の出力
	export.exportSDGraphOutputs(sdGraph, outputDir, fileName)
	
# 出力先のディレクトリを開く
subprocess.Popen(['explorer',outputDir],shell=True)

出力結果

以下のようなファイルが出力されます。

画像をシンプルなファイル名で出力する

STEP1. export.pyを改造する

Substance Designer標準のexport.pyでは、ファイル名を指定した画像を出力ができません。

export.pyの場所
 (Substance 3D Desigenrのインストールパス)\resources\python\sd\tools\export.py

export.pyを改造し、好きなファイル名で出力できるようにします (exportSDGraphOutputsWithFileNameというメソッドを追加しました)
https://gist.github.com/rngtm/cac4b705a5c6a62108671ce017ff2a02

export.pyを書き換えて保存した後は、Substance 3D Designer を再起動し、pythonを読み直させます。

STEP2. 連番画像をシンプルな名前で出力するサンプル (Python)

以下のPythonスクリプトを実行すると、連番画像が出力されます。
今回作成したWaterCausticsを開いたままの状態で実行してください。

export_sample_02.py
import os
import sd
from pathlib import Path
from sd.api.sduimgr import *
from sd.api.sdproperty import SDPropertyCategory
from sd.api.sdvaluefloat import *
from sd.tools import export
import subprocess

# 現在開いているSubstance Graphを取得
sdContext = sd.getContext()
sdApplication = sdContext.getSDApplication()
uiMgr = sdApplication.getUIMgr()
sdGraph = uiMgr.getCurrentGraph()

# Packageを取得
sdPackage = sdGraph.getPackage()
filePath = sdPackage.getFilePath()

outputDir = os.path.join(Path(filePath).parent, 'output')

print ("SubstanceGraph {}".format(sdGraph.getIdentifier))
print ("outputDir: {}".format(outputDir))

# パラメータの取得
progressProp = sdGraph.getPropertyFromId('progress', SDPropertyCategory.Input)

# 画像を出力
exportCount = 6 # 出力したい数をここに指定
for i in range(exportCount):

	# ファイル名
	fileName = "{}.png".format(str(i).zfill(2))
	print("export: {}".format(fileName))

	# パラメータの値を設定
	progressValue = float(i) / exportCount 
	sdGraph.setPropertyValue(progressProp, SDValueFloat.sNew(progressValue))

	# 画像の出力
	export.exportSDGraphOutputsWithFileName(sdGraph, outputDir, fileName)
	
# 出力先のディレクトリを開く
subprocess.Popen(['explorer',outputDir],shell=True)

出力結果

おまけ(1/2) : 連番画像からテクスチャシートを作成する (Unity)

今回作成したコースティクスの連番画像をUnityで再生する方法を紹介します。


Unityで再生

環境

  • Unity 2021.3.16f1
  • Universal RP 12.1.8

1. VFXToolboxの導入

Unity公式が配布している、VFXToolboxをUnityへ導入します
https://github.com/Unity-Technologies/VFXToolbox

2. 連番画像をUnityへインポートする

今回はコースティクスの連番画像32枚をUnityへインポートします。

連番画像32枚をUnityへインポート

3. Image Sequencer の作成

メニューから、 Image Seuqncer を開きます。

Image Sequencerを開く

画面中央にある「Create Image Sequence」をクリックし、Image Sequnceを作成します。

Image Sequenceを作成

結果

Image Sequenceを作成すると、ウィンドウが以下のような表示になります。

Image Sequenceの作成結果

4. テクスチャシートの作成

STEP1. 連番画像の登録

連番画像を Image Sequencer へドラッグアンドドロップし、画像を登録します。

Image Sequencer 上で、連番画像を再生して確認できます。

連番画像の再生

STEP2. 連番画像を1枚にまとめる

Processorタブを選択し、Assemble Flipbook プロセッサを追加します。

Processorの追加


Assemble Flipbookの追加

Assemble Flipbookを適用することで、連番画像を1枚のテクスチャシートにまとめることができます。
今回は、ヨコ8枚、タテ4枚 (計:32枚) のテクスチャシートを作成します。

Assemble Flipbookの適用結果

STEP3. テクスチャシートの出力

作成したテクスチャはExportタブから、出力します。

Flipbookの出力

出力結果

以下のようなテクスチャが出力されます。

5. アニメーションの再生 (ShaderGraph)

次に、シェーダー(ShaderGraph)を作成し、テクスチャシートを再生します。

結果


シェーダーを板メッシュに適用

おまけ(2/2) : 色収差をつけてみる (Unity)

Custom Material プロセッサ

Image Sequencerには、Custom Material プロセッサーが用意されています。
Custom Materialを利用することで、シェーダーを利用して画像を加工することができます。

たとえば、Blurシェーダーを利用すると、画像をぼかすことができます。

VFXToolbox/ImageSequencer/Blurシェーダー

Image Sequencerで使用できるシェーダーには、以下のような物が用意されています。

Image Sequenerで使用できるシェーダー

STEP1. 色収差シェーダー

以下のようなシェーダーを作成します。

ChromaticAberration.shader
Shader "VFXToolbox/ImageSequencer/ChromaticAberration[Custom]"
{
	Properties
	{
		_UvShift ("UV Shift", Range(-0.03,0.03)) = 0
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		// No culling or depth
		Cull Off ZWrite Off ZTest Always

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			// ImageSequencer CustomMaterial uniforms
			// ======================================
			//
			// sampler2D _InputFrame;			// Input Frame (from previous sequence)
			//
			// float4 _FrameData;				// Frame Data
			//									//		x, y : width (x) and height (y) in pixels
			//									//		z, w : sequence index (z) and length (w)
			//
			// float4 _FlipbookData;			// Flipbook Data
			//									//		x, y : number of columns (x) and rows (y)
			//									//		z, w : (unused)

			sampler2D _InputFrame;
			float4 _FrameData;
			float4 _FlipbookData;
			float _UvShift;

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}
			
			half4 frag (v2f i) : SV_Target
			{
				const float2 delta = float2(_UvShift, 0);
				half4 col1 = tex2D(_InputFrame, i.uv + delta);
				half4 col2 = tex2D(_InputFrame, i.uv);
				half4 col3 = tex2D(_InputFrame, i.uv - delta);

				return half4(col1.r, col2.g, col3.b, 1);
			}
			ENDCG
		}
	}
}

STEP2. 色収差を適用する

STEP1. のシェーダーを利用すると、画像に色収差が適用されます。

出力結果


色収差の適用結果

Discussion