XGen によるヘア作成のための操作ガイド

グルームを Alembic ファイルとしてエクスポートして Unreal Engine で使用するための操作方法を説明します。

Choose your operating system:

Windows

macOS

Linux

このガイドでは、「 Alembic for Grooms 」のドキュメントで概説されているサポートされた属性のセットを使用して Unreal Engine にインポートを行うために、Maya の「 従来の XGen ヘア作成システム 」からインポートするグルームを設定する方法を説明します。

このガイドのアセット作成には、Maya 2018.6 を使用しました。

レガシー XGen の記述を変換する

ガイドを NURBS カーブへ変換する

以下の手順を使って、転送するガイドと一致するカーブを保存するためにグルームのガイドをカーブに変換します。

  1. Maya のメニューセットを [Modeling (モデリング)] に設定すると、正しいメニュー オプションが使用可能になります。

    MenuSetSelection.png

  2. メインメニューから [Generate (作成)] のドロップダウンをクリックし、 [XGen Editor (XGen エディタ)] を選択します。

  3. [XGen] ウィンドウで、 [Utilities (ユーティリティ)] タブから [Guides to Curves (カーブの操作)] を選択します。

    XGen_Utilities.png

  4. [Create Curves (カーブを作成)] をクリックします。

完了すると、グルームの出力は次のようになります。

GuidesToNurbsCurvesOutput.png

グルームを XGen Interactive Groom に変換する

従来の XGen Description を使用している場合、グルームを XGen Interactive Groom に変換する必要があります。これを行うには、次のいずれかの方法を実行します。

  1. XGen Description ノードを選択します。

    XGenDescriptionNodes.png

  2. [Modeling (モデリング)] メニュー セットで、メイン メニューから、 [Generate (作成)] ドロップダウンをクリックし、 [Convert to Interactive Groom] を選択します。

    XGenInteractiveGroom.png

スプライン記述を NURBS カーブへエクスポートする

次の手順に従って選択したスプライン記述を、NURBS カーブとして補間されたヘアをインポートすることができる Alembic ファイルでエクスポートします。

  1. XGen Spline Description ノードを選択し、 [Modeling (モデリング)] メニュー セットで、メイン メニューから、 [Generate (作成)] ドロップダウンをクリックします。リストから [Cache (キャッシュ)] > [Export Cache (キャッシュをエクスポート)] を選択します。

    SplineDescToNurbsCurve_ExportCache.png

  2. [Export Cache (キャッシュをエクスポート)] ウィンドウで、以下を設定します。

    • [Cache Time Frame (キャッシュのタイムフレーム)]: [Current Frame (現在のフレーム)] に設定

    • [Multiple Transforms (複数のトランフォーム)]: Disabled (無効)

    • [Write Final Width (最終的な幅を書き込み)]: Enabled

    SplineDescToNurbsCurve_ExportCacheWindow.png

  3. ファイルに名前を付け、ファイル タイプとして [Alembic] を選択します。

    SplineDescToNurbsCurve_ExportCacheFile.png

  4. [Export (エクスポート)] をクリックします

  5. [File] メニューを使って [Import] を選択します。 [Import] ウィンドウが開いて、Alembic ('.abc') ファイルを選んでシーンにインポートすることができます。

    SplineDescToNurbsCurve_ImportAlembic.png

インポートすると、XGen Spline Description は、Alembic ファイルとしてエクスポートされ、補間されたヘアを NURBS カーブとして取り込むためにインポートされます。

SplineDescToNurbsCurve_Output.png

属性を作成する

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 にエクスポートする

  1. Maya で、エクスポートするガイドと Group_ID カーブを選択します。

    各ノードには固有の名前が必要です。

  2. [Modeling (モデリング)] メニューセットで、メイン メニューから [Cache (キャッシュ)] ドロップダウンをクリックし、 [Alembic Cache] > [Export Selection to Alembic] を選択します。

    ExportToAlembic_ExportSelection.png

  3. [Export Selection (選択範囲をエクスポート)] ウィンドウの [General Options (全般オプション)] カテゴリで、 [Cache time range (キャッシュ時間の範囲)] [Current Frame (現在のフレーム)] に設定します

    ExportToAlembic_ExportSelection_CurrentFrame.png

  4. 次に、 [Attributes (属性)] カテゴリの下で、リストに追加する [Attributes (属性)] の名前を入力し、 [Add (追加)] ボタンをクリックします。次のスキーマ属性を追加してください。

    ExportToAlembic_ExportSelection_AddAttributes.png

    • groom_group_id

    • groom_guide

  5. [File name (ファイル名)] テキストボックスでファイルに名前を付け、 [Files of type (ファイルの種類)] [Alembic] に設定します。

    ExportToAlembic_ExportSelection_FileNameType.png

  6. [Export Selection (選択範囲をエクスポート)] ボタンをクリックします。

テクスチャを毛髪の UV に適用する

以下の手順およびスクリプトを使うと、Unreal Engine へエクスポートが可能な独自の XGen による毛髪を設定し、適用したテクスチャを 1 本ごとの髪に表現するのに有用です。

  1. Maya の [Modeling] メニューから、 [Generate (生成)] > [Create Interactive Groom Splines (インタラクティブ グルーム スプラインを作成)] の順に選択します。

    HairUV_XGen.png

  2. ガイドを作成し、プロジェクト用に自由に髪にブラシをかけることができます。準備ができたら、オプションから [Generate] > [Cache] > [Create New Cache (キャッシュを新規作成)] の順に選択してカーブを Alembic Cache としてエクスポートします。

    HairUV_2_XGen.png

  3. XGen による毛髪を非表示にするか削除して取り除きます。ソースメッシュのあるエクスポートした髪のカーブを Maya シーンに再インポートします。

    HairUV_3_XGen.png

  4. 使用したいるシーンによって、最上のカーブ (この例では 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()
  5. Maya の場合、Unreal Engine へのインポートが可能な新しい Alembic ('.abc') ファイルを生成するために変更した値でスクリプトを実行します。

  6. Unreal Engine で、 Hair シェーディング モデルを使って新しいマテリアルを作成します。マテリアル グラフで Hair Attributes 表現式を追加して Root UV を Texture Sample の UV 入力へつなぎます。

    HairUV_4_XGen.png

    groom_root_uv 属性は、ヘアごとに、アタッチされている基礎となっている UV を指定します。この属性は省略することができます。指定しない場合、ルート UV は球状マッピングを使ってエンジン内に自動生成されます。

  7. インポートした髪の Alembic ファイルをコンテンツ ブラウザからレベルにドラッグし、ヘア マテリアルをそれに割り当てます。最終的には以下に示す画像のような結果となります。

    レベルの髪の Alembic ファイルの幅が 0 より大きいことを確認してください。

    HairUV_5_XGen.png

Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
閉じる