Choose your operating system:
Windows
macOS
Linux
このガイドでは、「
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):
# get curves under xgGroom
curves = cmds.listRelatives(group_name, ad=True, type='nurbsCurve')
# tag group with group id
cmds.addAttr(group_name, longName=attr_name, attributeType='short', defaultValue=groom_group_id, keyable=True)
# add attribute scope
# forces Maya's alembic to export data as 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 ファイルにガイドが指定されていない場合、補間されたヘアのパーセンテージは、UE4 へのインポート プロセス中にガイドとして内部でタグ付けされます。
ガイドなしでグルームをインポートする場合、「ガイド」とタグ付けされた補間されたヘアの割合は、「[Groom Import Options (グルームのインポートオプション)(WorkingWithContent/Hair/Reference)」で設定できます。デフォルトでは、ヘア数の 10% のみがガイドとして使用されます。
ガイド属性を作成するときは、次のスクリプトを使用します。
from maya import cmds
attr_name = 'groom_guide'
# get curves under xgGroom
curves = cmds.listRelatives('xgGroom', ad=True, type='nurbsCurve')
# create new group
guides_group = cmds.createNode('transform', name='guides')
# tag group as groom_guide
cmds.addAttr(guides_group, longName=attr_name, attributeType='short', defaultValue=1, keyable=True)
# forces Maya's alembic to export curves as one group.
cmds.addAttr(guides_group, longName='riCurves', attributeType='bool', defaultValue=1, keyable=True)
# add attribute scope
# forces Maya's alembic to export data as 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')
# parent curves under guides group
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. ''' # check curves group if not cmds.objExists(curves_group): raise RuntimeError('Group not found: "{}"'.format(curves_group)) # get curves in 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 # get curve roots points = list() for curve_shape in curve_shapes: point = cmds.pointPosition('{}.cv[0]'.format(curve_shape), world=True) points.append(point) # get 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) ) # create attribute 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. ''' # check mesh if not cmds.objExists(mesh_node): raise RuntimeError('Node not found: "{}"'.format(mesh_node)) # check 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)) # get mesh as dag-path selection_list = OpenMaya.MSelectionList() selection_list.add(mesh_node) mesh_dagpath = OpenMaya.MDagPath() selection_list.getDagPath(0, mesh_dagpath) mesh_dagpath.extendToShape() # get mesh function set 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 より大きいことを確認してください。