このガイドでは、「Alembic for Grooms」のドキュメントで概説されているサポートされた属性のセットを使用して Unreal Engine にインポートを行うために、Maya の「従来の XGen ヘア作成システム」からインポートするグルームを設定する方法を説明します。
このガイドのアセット作成には、Maya 2018.6 を使用しました。
レガシー XGen の記述を変換する
ガイドを NURBS カーブへ変換する
以下の手順を使って、転送するガイドと一致するカーブを保存するためにグルームのガイドをカーブに変換します。
-
Maya のメニューセットを [Modeling (モデリング)] に設定すると、正しいメニュー オプションが使用可能になります。
-
メインメニューから [Generate (作成)] のドロップダウンをクリックし、 [XGen Editor (XGen エディタ)] を選択します。
-
[XGen] ウィンドウで、[Utilities (ユーティリティ)] タブから [Guides to Curves (カーブの操作)] を選択します。
-
[Create Curves (カーブを作成)] をクリックします。
完了すると、グルームの出力は次のようになります。

グルームを XGen Interactive Groom に変換する
従来の XGen Description を使用している場合、グルームを XGen Interactive Groom に変換する必要があります。これを行うには、次のいずれかの方法を実行します。
-
XGen Description ノードを選択します。
-
[Modeling (モデリング)] メニュー セットで、メイン メニューから、[Generate (作成)] ドロップダウンをクリックし、[Convert to Interactive Groom] を選択します。
スプライン記述を NURBS カーブへエクスポートする
次の手順に従って選択したスプライン記述を、NURBS カーブとして補間されたヘアをインポートすることができる Alembic ファイルでエクスポートします。
-
XGen Spline Description ノードを選択し、[Modeling (モデリング)] メニュー セットで、メイン メニューから、[Generate (作成)] ドロップダウンをクリックします。リストから [Cache (キャッシュ)] > [Export Cache (キャッシュをエクスポート)] を選択します。
-
[Export Cache (キャッシュをエクスポート)] ウィンドウで、以下を設定します。
- [Cache Time Frame (キャッシュのタイムフレーム)]: [Current Frame (現在のフレーム)] に設定
- [Multiple Transforms (複数のトランフォーム)]:Disabled (無効)
- [Write Final Width (最終的な幅を書き込み)]: Enabled
-
ファイルに名前を付け、ファイル タイプとして [Alembic] を選択します。
- [Export (エクスポート)] をクリックします
-
[File] メニューを使って [Import] を選択します。[Import] ウィンドウが開いて、Alembic ('.abc') ファイルを選んでシーンにインポートすることができます。
インポートすると、XGen Spline Description は、Alembic ファイルとしてエクスポートされ、補間されたヘアを NURBS カーブとして取り込むためにインポートされます。

属性を作成する
ID 属性を作成する
補間されたヘアは、1 つ以上のグループでエクスポートできます。これらのグループは、固有マテリアル割り当てのために Unreal Engine で認識されます。
グループ ID 属性を作成するときは、次のスクリプトを使用します。
from maya import cmds
attr_name = 'groom_group_id'
# NOTE: change the following names to reflect your node's scene.
groups = ['hair_brows_splineDescription1|SplineGrp0', 'hair_lashes_splineDescription1|SplineGrp0', 'hair_head_splineDescription1|SplineGrp0']
for groom_group_id, group_name in enumerate(groups):
# xgGroomでカーブを描きます
curves = cmds.listRelatives(group_name, ad=True, type='nurbsCurve')
# グループIDでグループをタグづけします
cmds.addAttr(group_name, longName=attr_name, attributeType='short', defaultValue=groom_group_id, keyable=True)
# 属性スコープを追加します
# Maya のalembicがGeometryScope::kConstantScopeをデータとしてエクスポートするようにします
cmds.addAttr(group_name, longName='{}_AbcGeomScope'.format(attr_name), dataType='string', keyable=True)
cmds.setAttr('{}.{}_AbcGeomScope'.format(group_name, attr_name), 'con', type='string')
ガイド属性を作成する
グルーミングのガイド属性を作成するとき、ガイド とタグ付けされたカーブのみが、Unreal Engine でのシミュレーションに使用されます。Alembic ファイルにガイドが指定されていない場合、補間されたヘアのパーセンテージは、Unreal Engine へのインポート プロセス中にガイドとして内部でタグ付けされます。
ガイドなしでグルームをインポートする場合、「ガイド」とタグ付けされた補間されたヘアの割合は、「[Groom Import Options (グルームのインポートオプション)(working-with-content/hair-rendering/Reference)」で設定できます。デフォルトでは、ヘア数の 10% のみがガイドとして使用されます。
ガイド属性を作成するときは、次のスクリプトを使用します。
from maya import cmds
attr_name = 'groom_guide'
# xgGroomでカーブを描きます
curves = cmds.listRelatives('xgGroom', ad=True, type='nurbsCurve')
# 新しいグループを作成します
guides_group = cmds.createNode('transform', name='guides')
# groom_guideとしてグループをタグ付けします
cmds.addAttr(guides_group, longName=attr_name, attributeType='short', defaultValue=1, keyable=True)
# Mayaのalembicに1つのグループとしてカーブをエクスポートさせます。
cmds.addAttr(guides_group, longName='riCurves', attributeType='bool', defaultValue=1, keyable=True)
# add attribute scope
# MayaのalembicにGeometryScope::kConstantScopeとしてデータをエクスポートさせます
cmds.addAttr(guides_group, longName='{}_AbcGeomScope'.format(attr_name), dataType='string', keyable=True)
cmds.setAttr('{}.{}_AbcGeomScope'.format(guides_group, attr_name), 'con', type='string')
# guides グループの親カーブ
for curve in curves:
cmds.parent(curve, guides_group, shape=True, relative=True)
Groom_Width Attribute
Alembic for Grooms の仕様 に従ってグルームをビルドするために幅値を取得および使用ができるその他の DCC アプリケーションとは異なり、Maya の場合は幅値には特別な挙動があります。
Maya は幅値をカーブに直接エクスポートすることが可能なので、カスタムの groom_width
属性をエクスポートする必要はありません。インポーターが Maya の幅値を groom_width
属性に変換します。Unreal Engine へのインポート中にグルームに groom_wdith
がない場合は、上書きされません。groom_width
が指定されていない、または幅値から変換できない場合ビルダーが幅値に 1 センチメートルという値を使用するようにフォールバックします。
Maya から Alembic にエクスポートする
-
Maya で、エクスポートするガイドと Group_ID カーブを選択します。
各ノードには固有の名前が必要です。
-
[Modeling (モデリング)] メニューセットで、メイン メニューから [Cache (キャッシュ)] ドロップダウンをクリックし、[Alembic Cache] > [Export Selection to Alembic] を選択します。
-
[Export Selection (選択範囲をエクスポート)] ウィンドウの [General Options (全般オプション)] カテゴリで、[Cache time range (キャッシュ時間の範囲)] を [Current Frame (現在のフレーム)] に設定します
-
次に、[Attributes (属性)] カテゴリの下で、リストに追加する [Attributes (属性)] の名前を入力し、[Add (追加)] ボタンをクリックします。次のスキーマ属性を追加してください。
- groom_group_id
- groom_guide
-
[File name (ファイル名)] テキストボックスでファイルに名前を付け、[Files of type (ファイルの種類)] を [Alembic] に設定します。
-
[Export Selection (選択範囲をエクスポート)] ボタンをクリックします。
テクスチャを毛髪の UV に適用する
以下の手順およびスクリプトを使うと、Unreal Engine へエクスポートが可能な独自の XGen による毛髪を設定し、適用したテクスチャを 1 本ごとの髪に表現するのに有用です。
-
Maya の [Modeling] メニューから、[Generate (生成)] > [Create Interactive Groom Splines (インタラクティブ グルーム スプラインを作成)] の順に選択します。
-
ガイドを作成し、プロジェクト用に自由に髪にブラシをかけることができます。準備ができたら、オプションから [Generate] > [Cache] > [Create New Cache (キャッシュを新規作成)] の順に選択してカーブを Alembic Cache としてエクスポートします。
-
XGen による毛髪を非表示にするか削除して取り除きます。ソースメッシュのあるエクスポートした髪のカーブを Maya シーンに再インポートします。
-
使用したいるシーンによって、最上のカーブ (この例では SplineGrp0) を親に持つ数千ものスプライン カーブができます。以下の Python スクリプトを編集して、以下の値をプロジェクトの値に置き換えます。
- export_directory
- hair_file
- curve_top_group
- uv_mesh
以下のスクリプトは こちら からダウンロードできます。
from maya import cmds
from maya import OpenMaya
import os
def create_root_uv_attribute(curves_group, mesh_node, uv_set='map1'):
'''
Create "groom_root_uv" attribute on group of curves.
'''
# カーブグループを確認します
if not cmds.objExists(curves_group):
raise RuntimeError('Group not found: "{}"'.format(curves_group))
# カーブをグループで取得します
curve_shapes = cmds.listRelatives(curves_group, shapes=True, noIntermediate=True)
curve_shapes = cmds.ls(curve_shapes, type='nurbsCurve')
if not curve_shapes:
raise RuntimeError('Invalid curves group.No nurbs-curves found in group.')
else:
print "found curves"
print curve_shapes
# カーブルートを取得します
points = list()
for curve_shape in curve_shapes:
point = cmds.pointPosition('{}.cv[0]'.format(curve_shape), world=True)
points.append(point)
# uvsを取得します
values = list()
uvs = find_closest_uv_point(points, mesh_node, uv_set=uv_set)
for u, v in uvs:
values.append([u, v, 0])
#print (str(u) + " , " + str(v) )
# 属性を作成します
name = 'groom_root_uv'
cmds.addAttr(curves_group, ln=name, dt='vectorArray')
cmds.addAttr(curves_group, ln='{}_AbcGeomScope'.format(name), dt='string')
cmds.addAttr(curves_group, ln='{}_AbcType'.format(name), dt='string')
cmds.setAttr('{}.{}'.format(curves_group, name), len(values), *values, type='vectorArray')
cmds.setAttr('{}.{}_AbcGeomScope'.format(curves_group, name), 'uni', type='string')
cmds.setAttr('{}.{}_AbcType'.format(curves_group, name), 'vector2', type='string')
return uvs
def find_closest_uv_point(points, mesh_node, uv_set='map1'):
'''
Find mesh UV-coordinates at given points.
'''
# メッシュを確認します
if not cmds.objExists(mesh_node):
raise RuntimeError('Node not found: "{}"'.format(mesh_node))
# uv_setを確認します
uv_sets = cmds.polyUVSet(mesh_node, q=True, allUVSets=True)
if uv_set not in uv_sets:
raise RuntimeError('Invalid uv_set provided: "{}"'.format(uv_set))
# dag-pathとしてメッシュを取得します
selection_list = OpenMaya.MSelectionList()
selection_list.add(mesh_node)
mesh_dagpath = OpenMaya.MDagPath()
selection_list.getDagPath(0, mesh_dagpath)
mesh_dagpath.extendToShape()
# メッシュ関数セットを取得します
fn_mesh = OpenMaya.MFnMesh(mesh_dagpath)
uvs = list()
for i in range(len(points)):
script_util = OpenMaya.MScriptUtil()
script_util.createFromDouble(0.0, 0.0)
uv_point = script_util.asFloat2Ptr()
point = OpenMaya.MPoint(*points[i])
fn_mesh.getUVAtPoint(point, uv_point, OpenMaya.MSpace.kWorld, uv_set)
u = OpenMaya.MScriptUtil.getFloat2ArrayItem(uv_point, 0, 0)
v = OpenMaya.MScriptUtil.getFloat2ArrayItem(uv_point, 0, 1)
uvs.append((u, v))
return uvs
def abc_export(filepath, node=None, start_frame=1, end_frame=1, data_format='otawa', uv_write=True):
job_command = '-frameRange {} {} '.format(start_frame, end_frame)
job_command += '-dataFormat {} '.format(data_format)
job_command += '-attr groom_root_uv '
if uv_write:
job_command += '-uvWrite '
job_command += '-root {} '.format(node)
job_command += '-file {} '.format(filepath)
cmds.AbcExport(verbose=True, j=job_command)
def main():
export_directory = 'D:/Dev/Ref'
hair_file = os.path.join(export_directory, 'hair_export.abc')
curve_top_group= 'description1|SplineGrp0'
uv_mesh='pPlane1'
create_root_uv_attribute( curve_top_group , uv_mesh)
abc_export(hair_file, curve_top_group)
main()
-
Maya の場合、Unreal Engine へのインポートが可能な新しい Alembic ('.abc') ファイルを生成するために変更した値でスクリプトを実行します。
-
Unreal Engine で、Hair シェーディング モデルを使って新しいマテリアルを作成します。マテリアル グラフで Hair Attributes 表現式を追加して Root UV を Texture Sample の UV 入力へつなぎます。
groom_root_uv
属性は、ヘアごとに、アタッチされている基礎となっている UV を指定します。この属性は省略することができます。指定しない場合、ルート UV は球状マッピングを使ってエンジン内に自動生成されます。 -
インポートした髪の Alembic ファイルをコンテンツ ブラウザからレベルにドラッグし、ヘア マテリアルをそれに割り当てます。最終的には以下に示す画像のような結果となります。
レベルの髪の Alembic ファイルの幅が 0 より大きいことを確認してください。