Mayaで自作シェーダーを使う ~ ソフトウェアレンダリング(python plug-in)編 ~

Tech

はじめに

Mayaには標準のマテリアルが用意されていますが、自作シェーダーを使うと、標準機能だけでは難しい表現や、制作目的に合わせた独自の描画処理を実装できます。

この記事では、Mayaのソフトウェアレンダリングに対応した自作シェーダーを、Python Plug-inとして作成する手順を解説します。(この記事ではシェーダーの中身についてはほとんど触れません。)

「Mayaで自作シェーダーを使う」シリーズの他の記事はこちらです。

Pythonファイルの用意

拡張子を .py にしたPythonファイルを用意します。

以下は、デフォルト値が赤単色のシンプルなサンプルシェーダーです。

通常のノードプラグインを作成する場合とほぼ同じ構造です。主な違いは以下の2点です。

  • registerNode の引数に "shader/surface:drawdb/shader/surface/..." を指定する
  • outColor アトリビュートを作成する必要がある

必要な情報をアトリビュートとして受け取り、compute メソッド内で自由に出力色を計算します。

パスを通す

任意のフォルダにプラグインパスを通します。たとえば、C:\Users\<ユーザー名>\Documents\maya\<バージョン>\Maya.env に以下を追記します。右辺にはパスを通したい任意のディレクトリを指定でき、セミコロンで区切って複数設定できます。

ファイル名: Maya.env

MAYA_PLUG_IN_PATH=C:\Users\<user名>\Documents\maya\2025\plug-ins;

他にも、.mod ファイル等を用意してパスを通す方法もあります。

パスを通したディレクトリの直下に、用意した .py ファイルを配置してください。

Plug-inの読み込み

Mayaを起動します。正しくパスが通っていれば、Plug-in Managerに sampleShader.py が追加されているので、「Loaded」にチェックを入れます。

※ パスを通さなくても、左下の「Browse」ボタンから任意のディレクトリのプラグインを読み込むことも可能です。

MayaのPlug-in ManagerでsampleShader.pyを読み込む操作

レンダリング

プラグインが正しく読み込まれていれば、ノード作成画面に自動で SampleShader が追加されています。作成してオブジェクトに割り当てます。

MayaでSampleShaderノードを作成してオブジェクトへ割り当てる操作

レンダービューを開き、Maya Softwareでレンダリングします。

Maya SoftwareでSampleShaderをレンダリングした結果

inColor アトリビュートの値を変更すると、出力色が変わります。

補足: ライト情報の取得

MPxSurfaceShadingNodeOverride を使用することで、ビューポート2.0での表示にはHLSLやGLSLを使いつつ、Maya Softwareに対応するマテリアルを作成することもできます。

また、シーンのライト情報は以下のように取得できます。

light情報の取得(initialize内)
def initialize():

    :
    :        

    n_attr = om.MFnNumericAttribute()
    SampleShader.light_dir = n_attr.createPoint('lightDirection', 'ld')
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True
    n_attr.default = (1.0, 1.0, 1.0)

    SampleShader.light_intensity = n_attr.createColor('lightIntensity', 'li')
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True
    n_attr.default = (1.0, 1.0, 1.0)

    SampleShader.light_ambient = n_attr.create('lightAmbient', 'la', om.MFnNumericData.kBoolean)
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True

    SampleShader.light_diffuse = n_attr.create('lightDiffuse', 'ldf', om.MFnNumericData.kBoolean)
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True

    SampleShader.light_specular = n_attr.create('lightSpecular', 'ls', om.MFnNumericData.kBoolean)
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True
    
    SampleShader.light_sdw_fraction = n_attr.create('lightShadowFraction', 'lsf', om.MFnNumericData.kFloat)
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True
    
    SampleShader.pre_sdw_intensity = n_attr.create('preShadowIntensity', 'psi', om.MFnNumericData.kFloat)
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True
    
    SampleShader.light_blind_data = n_attr.createAddr('lightBlindData', 'lbld')
    n_attr.storable = False
    n_attr.writable = False
    n_attr.hidden = True

    l_attr = om.MFnLightDataAttribute()
    SampleShader.light_data = l_attr.create(
        'lightDataArray', 'ltd', 
        SampleShader.light_dir, 
        SampleShader.light_intensity,
        SampleShader.light_ambient,
        SampleShader.light_diffuse,
        SampleShader.light_specular,
        SampleShader.light_sdw_fraction,
        SampleShader.pre_sdw_intensity,
        SampleShader.light_blind_data
    )
    l_attr.array = True 
    l_attr.storable = False
    l_attr.hidden = True
    l_attr.default = ( (1.0, 0.0, 0.0), (0.0, 0.0, 0.0), True, True, False, 0.0, 1.0, 1)

    om.MPxNode.addAttribute(SampleShader.light_data)

    om.MPxNode.attributeAffects(SampleShader.light_dir, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.light_intensity, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.light_ambient, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.light_diffuse, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.light_specular, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.light_sdw_fraction, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.pre_sdw_intensity, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.light_blind_data, SampleShader.out_color)
    om.MPxNode.attributeAffects(SampleShader.light_data, SampleShader.out_color)
light情報の使用(compute内)
def compute(self, plug, block):
    :
    :
    light_data = block.inputArrayValue( SampleShader.light_data )

    for _ in range(len(light_data)):
        current_light = light_data.inputValue()
        
        light_intensity = current_light.child( SampleShader.light_intensity ).asFloatVector()
        light_dir = current_light.child( SampleShader.light_dir).asFloatVector()
        is_diffuse = current_light.child(SampleShader.light_diffuse).asBool()
        is_ambient = current_light.child(SampleShader.light_ambient).asBool()
        is_specular = current_light.child(SampleShader.light_specular).asBool()
        blind_data = current_light.child( SampleShader.light_blind_data ).asAddr()

        if not light_data.next(): break

    :
    :

その他、既定のアトリビュート名にすることで、カメラ情報なども受け取ることができます。アトリビュート名の詳細は Mayaの開発者ヘルプ を参照してください。

まとめ

Python Plug-inとしてシェーダーを作成すれば、Maya Softwareレンダリングに対応した自作マテリアルを実装できます。通常のノードプラグインとの違いは registerNode の分類指定と outColor アトリビュートの追加だけなので、ノードプラグインの作成経験がある方ならスムーズに取り組めるはずです。

ビューポート2.0とMaya Softwareの両方に対応したい場合は、MPxSurfaceShadingNodeOverride と組み合わせることで、HLSLやGLSLのビューポート表示とソフトウェアレンダリングを同時にサポートできます。

コメント

タイトルとURLをコピーしました