今回は,時系列データに対応した USD ファイルを作成してみます。
タイムサンプリング
時系列データを USD ファイルに含めるのは簡単です。
- ヘッダに始点タイムコードと終点タイムコードを定義する
- Primvar データに値をセットするときに,タイムコードで区切って与えるようにする
の 2 点だけです。具体的に見てみます。
ステージファイルの修正
まず,タイムコードの始点と終点の設定ですが,これはステージファイルの作成プログラムを修正して行います。ここでは 16 ステップからなる時系列データを想定しています。
from pxr import Usd, UsdGeom, Sdf
def create_stage():
stage = Usd.Stage.CreateInMemory()
UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.y)
UsdGeom.SetStageMetersPerUnit(stage, 0.01)
# タイムコードの始点と終点の設定
stage.SetStartTimeCode(0)
stage.SetEndTimeCode(15)
return stage
メッシュノードの修正
以前の回で,メッシュの頂点を基準にして表示色を設定する例を挙げました。今回はここに時系列データを与えます。前回のシンプルな平板ではつまらないので,少し複雑なトーラスメッシュでテストしてみます。
トーラスメッシュ
トーラスメッシュは次式で定義されます。
\begin{align}
x &= (R + r \cos \theta) \cos(\phi) \\
y &= (R + r \cos \theta) \sin(\phi) \\
z &= r \sin \theta \\
n_x &= \cos(\theta) \cos(\phi) \\
n_y &= \cos(\theta) \sin(\phi) \\
n_z &= \sin(\theta)
\end{align}
$n$ は法線ベクトルです。このメッシュにおいて頂点は何らかの「値」を持っており,それらは等$r$面に沿ったガウス的な分布であると仮定します。つまり,
v = \exp \left(-\frac{s(\theta, \phi, t)^2}{\sigma^2} \right), \sigma は定数
を考えます。ただし,$s$ には $\theta$ と $\phi$ による「捻り」が加わっており,「経時変化」もあるとします。
s(\theta, \phi) = \frac{1}{2} \left(1 + \cos \left(m \theta - n \phi + \omega t \right) \right)
このようにすると,始点 $t = 0$ では次のような分布になるはずです。ここでは $m, n$はそれぞれ $m = 2, n = 1$ としました。黄色で着色されているところが $v$ の高い領域を表します。
コードの中身を具体的に見ていきます。
いま,配列 values に全頂点にわたって時系列データが格納されているとします。格納法は「時間」を外側のループとします。つまり,あるタイムコードにおける全頂点データのあとに次のタイムコードの全頂点データが並んでいるとします。
values をもとに全頂点の色データ(RGB)を作成する例を示します。itertools.batched 関数を利用してタイムコードごとにデータをまとめるようにしています(itertools.batched は Python 3.12 の新機能です)。
color3 = []
for v in values:
color3.append((0.5+0.5*v, 0.5+0.25*v, 0.5-0.5*v)) # Yellow/Grey map
c = [batch for batch in itertools.batched(color3, len(vertices))]
前回と同様にして,PrimvarAPI を使って表示色を設定しますが,色情報とともにタイムコードも入力するところが異なります。ついでに,前回省略した法線ベクトルや表示範囲も設定しておきました。
def create_mesh(stage, path):
mesh = UsdGeom.Mesh.Define(stage, path)
# Points, FaceVertexCounts, FaceVertexIndices の設定は前回までと同様.
...
# 表示色の設定
primvar_api = UsdGeom.PrimvarsAPI(mesh)
primvar = primvar_api.CreatePrimvar('displayColor', Sdf.ValueTypeNames.Color3f, interpolation=UsdGeom.Tokens.varying)
frame_range = np.arange(stage.GetStartTimeCode(), stage.GetEndTimeCode() + 1, 1, dtype='int32')
for frame in frame_range:
time_code = Usd.TimeCode(float(frame))
primvar.GetAttr().Set(c[frame], time_code)
# 法線ベクトルの設定
mesh.CreateNormalsAttr(normals)
# 表示範囲
extent = UsdGeom.Boundable(mesh.GetPrim()).ComputeExtent(Usd.TimeCode(0))
mesh.CreateExtentAttr(extent)
...
return mesh
以上より,USD ファイルは次のようなものになりました。usdchecker によるチェックもパスしています。
#usda 1.0
(
defaultPrim = "World"
endTimeCode = 15
metersPerUnit = 0.01
startTimeCode = 0
upAxis = "Y"
)
def Xform "World"
{
def Mesh "Mesh"
{
uniform bool doubleSided = 0
float3[] extent = [(-2.5, -2.5, -0.5), (2.5, 2.5, 0.5)]
int[] faceVertexCounts = [3, 3, 3, ... ]
int[] faceVertexIndices = [0, 1, 2, ... ]
normal3f[] normals = [(1, 0, 0), (0.980785, 0.19509, 0), ... ]
point3f[] points = [(2.5, 0, 0), (2.45196, 0.487726, 0), ... ]
color3f[] primvars:displayColor (
interpolation = "varying"
)
color3f[] primvars:displayColor.timeSamples = {
0: [(0.5, 0.5, 0.5), (0.5, 0.5, 0.5), ... ]
1: [(0.5, 0.5, 0.5), (0.5, 0.5, 0.5), ... ]
...
15: [(0.5, 0.5, 0.5), (0.5, 0.5, 0.5), ... ]
}
uniform token subdivisionScheme = "none"
}
}
$ usdchecker Mesh.usda
Success!
ビューワで表示すると,着色部分が周回するように変化する様子が確かめられます(上段左から $\omega t = 0, \ \pi/2, \ \pi, \ 3\pi/2$ の図)。
まとめ
時系列データを USD ファイルに含める手法についてまとめました。
参考資料
ft-lab (Yutaka Yoshisaka) さん,あんどうめぐみさん による以下のサイトが理解の助けになりました。