LoginSignup
0
0

Maya でフラットライトでレンダリングする方法

Posted at

概要

Maya で自動で動画を撮影したい。
モーション確認用だからフラットライトでレンダリングしたい。
こんな需要に2パターンの手法でトライしたので、備忘録的に残します。

前提

  • Maya 2020.x
  • Lambert マテリアルが既に接続されている
    • フラットライト設定のビュー上で得たい見た目になっている状態

目次

2つの手法の比較

2つの手法を試してみましたが、カメラを事前に準備できる場合には Playblast を使う方が楽だと思いました。

surfaceShader Playblast
メリット ・好きなレンダラーが使える ・シーンのノード構成を汚さない(カメラには変更が入るのでリストアは必要)
・シーン構成に左右されない
デメリット ・シーンのノード構成に変更が入る(リストアは可能) ・レンダラーはビューで使用できる物に限られる

surafaceShader を使う方法

マテリアルを SurfaceShader で差し替えてしまう方法です。
シーンに変更が入ってしまいますが、好きなレンダラーで撮影が可能です。

処理概要

  • 対象のメッシュから ShadingEngine(シェーディンググループ) ノードを取得
  • ShadingEngine から接続されているマテリアルを取得し、さらに接続された Color と Transparency を取得
  • surfaceShader ノードを作成し、接続
    • マテリアルから取得した color, transparency インプットに接続
    • outColor を ShadingEngine に接続(マテリアル差し替え)

あとはお好みのレンダラーで撮影してください。
差し替え前のマテリアルと ShadingEngine ノードを保持しておけばリストアも可能です。

サンプルコード

コード

※ OpenMayaApi 2.0 を使ってますが、 cmds でも問題なく実現できると思います。

from maya.api import OpenMaya as om2

def connect_surface_shader():
    """対象のメッシュを取得し、マテリアルを Surface Shader に変更する。"""
    sg_object_list = []

    # シーンから Mesh Dag イテレーターを取得
    mesh_dag_itr = om2.MItDag(om2.MItDag.kDepthFirst, om2.MFn.kMesh)
    while not mesh_dag_itr.isDone():
        mesh_path: om2.MDagPath = mesh_dag_itr.getPath()
        mesh_dag_itr.next()

        dag_fn = om2.MFnDagNode(mesh_path)
        mesh_plugs = dag_fn.getConnections()
        for mesh_plug in mesh_plugs:
            destinations = mesh_plug.destinations()
            if not destinations:
                continue
            # シェーディングエンジンが接続されているかチェック
            for dest in destinations:
                node = dest.node()
                if node.apiType() == om2.MFn.kShadingEngine:
                    is_has_shading_engine = True
                    # シェーディングエンジンをリストに追加
                    if node not in sg_object_list:
                        sg_object_list.append(node)

    for sg_object in sg_object_list:
        sg_dnode_fn = om2.MFnDependencyNode(sg_object)
        # Shading Group からマテリアルを取得
        surface_shader_plug = sg_dnode_fn.findPlug("surfaceShader", True)
        # マテリアルが接続されていない場合はスキップ
        if not surface_shader_plug.isConnected:
            continue
        # マテリアルが Surface Shader の場合はスキップ
        material_node = surface_shader_plug.source().node()
        material_dnode_fn = om2.MFnDependencyNode(material_node)
        if material_dnode_fn.typeName == "surfaceShader":
            continue
        # カラーと透明度を取得
        color_plug = material_dnode_fn.findPlug("color", True)
        transparency_plug = material_dnode_fn.findPlug("transparency", True)

        # Surface Shader ノードを作成
        surface_shader_node_name = cmds.shadingNode(
            "surfaceShader",
            name=f"{material_dnode_fn.name()}_SurfaceShader",
            asShader=True, skipSelect=True)
        # カラーと透明度を接続
        if color_plug.isConnected:
            cmds.connectAttr(
                color_plug.source().name(),
                f"{surface_shader_node_name}.outColor",
                force=True)
        if transparency_plug.isConnected:
            cmds.connectAttr(
                transparency_plug.source().name(),
                f"{surface_shader_node_name}.outTransparency",
                force=True)
        # Surface Shader を Shading Group に接続
        cmds.connectAttr(
            f"{surface_shader_node_name}.outColor",
            f"{sg_dnode_fn.name()}.surfaceShader",
            force=True)

Playblast を使う方法

Playblast はビューの見た目をそのまま撮影できるため、シーンを汚さなくて済みます。
ただし、ビューの状態はユーザによって様々です。
そのため、撮影用のビューを生成してしまうのが楽です。

処理概要

  • 撮影用の ModelPanel を作成
  • ビューの設定
    • HUD, 各種表示等
  • カメラの設定
    • フィルムゲート、 MSAA 等
  • Playblst 撮影
  • カメラ設定のリストア

サンプルコード

コード
import maya.cmds as cmds

def rec_playblast(
        window_name: str, panel_name: str, rec_camera: str,
        output_path_without_ext: str, width: int, height: int):
    """
    Playblast で撮影する
    Args:
        window_name (str): 撮影用 Window の名前
        panel_name (str): 撮影用 Panel の名前
        rec_camera (str): 撮影に使用するカメラ
        output_path_without_ext (str): 出力ファイルのパス(拡張子なし)
        width (int): 横幅
        height (int): 縦幅
    """
    # 同名の Window や Panel が存在する場合は削除
    if cmds.window(window_name, exists=True):
        cmds.deleteUI(
            window_name, window=True)
    if cmds.modelPanel(panel_name, exists=True):
        cmds.deleteUI(
            panel_name, panel=True)

    # Window 作成
    model_window = cmds.window(window_name)
    # Window のレイアウト作成
    layout = cmds.formLayout()
    # Model Panel 作成
    model_panel = cmds.modelPanel(panel_name)
    # Model Panel を Window に配置
    cmds.formLayout(
        layout, edit=True,
        attachForm=(
            (model_panel, 'top', 0),
            (model_panel, 'left', 0),
            (model_panel, 'bottom', 0),
            (model_panel, 'right', 0)))
    # Window が閉じられた時に ModelPanel も削除する CloseCommand を設定
    cmds.window(
        model_window, edit=True,
        closeCommand=f"cmds.deleteUI('{model_panel}', panel=True)")
    # Model Editor を取得
    model_editor = cmds.modelPanel(model_panel, query=True, modelEditor=True)
    # Window を表示
    cmds.showWindow(model_window)

    # ビューポートの HUD を非表示
    cmds.modelEditor(model_editor, edit=True, headsUpDisplay=False)
    # ポリゴンのみ表示
    cmds.modelEditor(model_editor, edit=True, allObjects=False)
    cmds.modelEditor(model_editor, edit=True, polymeshes=True)
    # 各種ビュー設定
    cmds.modelEditor(
        model_editor, edit=True,
        grid=False,                         # グリッド非表示
        displayAppearance="smoothShaded",   # スムーズシェーディング
        displayLights="flat",               # フラットライト
        displayTextures=True,               # テクスチャ表示
        xray=False,                         # X線表示 OFF
        activeComponentsXray=False,         # X線アクティブコンポーネント表示 OFF
        jointXray=False,                    # X線ジョイント表示 OFF
        selectionHiliteDisplay=False        # 選択ハイライト表示 OFF
    )

    # カメラの表示オプションとアトリビュートのバックアップ
    pre_film_gate = cmds.camera(rec_camera, query=True, displayFilmGate=True)
    pre_resolution_gate = cmds.camera(rec_camera, query=True, displayResolution=True)
    pre_gate_mask = cmds.camera(rec_camera, query=True, displayGateMask=True)
    pre_field_chart = cmds.camera(rec_camera, query=True, displayFieldChart=True)
    pre_safe_action = cmds.camera(rec_camera, query=True, displaySafeAction=True)
    pre_safe_title = cmds.camera(rec_camera, query=True, displaySafeTitle=True)
    pre_msaa = cmds.getAttr("hardwareRenderingGlobals.multiSampleEnable")
    pre_ssao = cmds.getAttr("hardwareRenderingGlobals.ssaoEnable")

    # カメラの表示オプションとアトリビュートの設定
    # FilmGate ~ displaySafeTitle までの表示を OFF
    cmds.camera(
        rec_camera, edit=True,
        displayFilmGate=False,      # フィルムゲート表示 OFF
        displayResolution=False,    # 解像度ゲート表示 OFF
        displayGateMask=False,      # ゲートマスク表示 OFF
        displayFieldChart=False,    # フィールドチャート表示 OFF
        displaySafeAction=False,    # セーフアクション表示 OFF
        displaySafeTitle=False      # セーフタイトル表示 OFF
    )
    # MASS, SSAO を有効化
    cmds.setAttr("hardwareRenderingGlobals.multiSampleEnable", True)
    cmds.setAttr("hardwareRenderingGlobals.ssaoEnable", True)

    # カメラを割り当て
    cmds.modelEditor(model_editor, camera=rec_camera, edit=True)

    # 撮影
    # MEMO: viewer=True にすると何故か返り値から拡張子が消えるので filename を指定した方が良い
    cmds.playblast(
        filename=output_path_without_ext,
        forceOverwrite=True,
        editorPanelName=model_panel,
        widthHeight=[width, height],
        format="avi",
        compression="none",
        percent=100,
        quality=100,
        offScreen=True,
        viewer=False)

    # カメラの表示オプションとアトリビュートのリストア
    cmds.camera(
        rec_camera, edit=True,
        displayFilmGate=pre_film_gate,
        displayResolution=pre_resolution_gate,
        displayGateMask=pre_gate_mask,
        displayFieldChart=pre_field_chart,
        displaySafeAction=pre_safe_action,
        displaySafeTitle=pre_safe_title)
    cmds.setAttr("hardwareRenderingGlobals.multiSampleEnable", pre_msaa)
    cmds.setAttr("hardwareRenderingGlobals.ssaoEnable", pre_ssao)

    # 撮影用 Window を削除
    cmds.deleteUI(model_window, window=True)

余談(Arnold Renderer)

Arnold Render のシェーダーオーバーライド機能 でシェーダーを SurfaceShader に切り替えたらサクッとできるのでは?と考えたんですが、オーバーライド前のマテリアルに接続されたインプット(テクスチャの接続等)は無視されるので期待する動作になりませんでした。
まぁ、シェーダー間でアトリビュートの名前が統一されてないので無理ですよね。。。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0