チュートリアル / プラグインを作ってみよう!ゲーム開発のためのツール製作講座
第6回:Softimage編 マテリアル情報の取得

  • ゲーム
  • コラム
  • スクリプト・API
  • チュートリアル
  • 上級者
  • 中級者
前回はジオメトリの情報を一通り取得してみました。前回の最後でポリゴンごとのマテリアルを取得していたので、今回はその続きとしてマテリアル内部の情報を見ていきましょう。下のリンクから今回のサンプルプログラムをダウンロードしてください。Softimage2011とVisualStudio2008が使える方はビルドして試すことができます。

[AJ_SISDK_201112.zip]

まずゲーム開発用に質感設定を行う場合、大きく分けて2つの方法があります。SoftimageなどのDCCツールで設定する方法と、独立したゲームエンジン上で設定する方法です。このコラムではSoftimage上で質感設定を行って、それを出力するワークフローを想定しています。とはいえ、質感の基本的な情報しか取得していないので、この部分はゲームエンジンを使う場合でも大きく変わらないでしょう。

Softimageでは「レンダーツリー」で質感の設定を行います。レンダーツリーではシェーダーやテクスチャなどの各種ノードを繋いで質感情報を構築します。今回はこのレンダーツリー内部のツリー構造にアクセスして各種パラメータとテクスチャの情報を取得します。

こちらの画面がレンダーツリーです。一番右側のノードがSoftimage上のマテリアルに相当します。このマテリアルのノードに各種シェーダーやテクスチャのノードが繋がってツリーを構成しています。


最初にSoftimage上のレンダーツリーの構造とSoftimageSDKのクラスの対応関係について説明します。レンダーツリーの見た目とプログラム上のクラス構造が一致していないと、ツリー構造をたどるのが難しくなってしまいます。ここは基本なのでよく読んで理解しておいてください。

こちらの図は先ほどのレンダーツリーの各ノードがSoftimageSDKのどのクラスに相当するかを示したものです。ツリーの一番根本(右側)がMaterialクラスで、そこにShaderクラスが繋がっています。ShaderクラスにTextureクラスが繋がり、TextureにImageClip2クラスが接続されています。Textureクラスはテクスチャを表現するインスタンスで、ImageClip2クラスが実際の画像ファイルに相当するもの、と覚えておきましょう。またTextureクラスの親クラスがShaderクラスです。


ついでにレンダーツリーのノードがExplorer上でどのように表示されるかを理解しておくとデバッグのときに便利です。紫の丸がマテリアルで、そのレンダーツリーに含まれるシェーダーとテクスチャ、イメージクリップが子供として表示されます。


レンダーツリーを構成する各ノードはパラメータを持っています。下の図の各ノードの左側の丸い部分がパラメータで、これらはParameterクラスに相当します。


レンダーツリーの各ノードの識別にはShader::GetProgID()を使います。ProgIDというのは「programmatic identifier」の略で、識別子として使われる文字列のことです。先ほどのレンダーツリーの各ノードのProgIDはこちらの図のようになっています。TextureクラスはShaderクラスから派生しているので、GetProgID()で同様にProgIDを取得できます。このProgIDはSoftimageのバージョンアップによって「たまに」変わるので、バージョンアップの際には注意してください。


対応関係が分かったところで、レンダーツリーの中にアクセスしてみましょう。以下の説明ではこちらのシンプルなレンダーツリーを使って説明します。一番右側のマテリアルから始めて、一番左側のイメージクリップまでアクセスしてみます。


前回のコラムの最後のところでポリゴンにアサインされているマテリアルを取得しているので、Materialクラスが手元にあるものとします。まずMaterialクラスのパラメータを取得します。MaterialクラスはProjectItemクラスから派生しているので、ProjectItem::GetParameters()を使ってパラメータを取得できます。

CParameterRefArray parameters = material.GetParameters();

これでマテリアルノードの左側のパラメータ一覧が取得できました。次にこのパラメータに繋がっている要素をParameter::GetSource()関数で取得します。戻り値はShaderParameterクラスです。パラメータに何も繋がっていない場合、GetSource()関数は空の参照を返します。


for( i=0; i<parameters.GetCount(); i++ ) {
        Parameter param = parameters[ i ];
        ShaderParameter shaderParam( param.GetSource() );
        if( shaderParam.IsValid() ) {
                /* ノードが繋がっていた場合 */
        }
}


このGetSource()の部分はSoftimage2011から仕様が変わったので注意してください。以前はGetSource()から接続元のShaderクラスが直接取得できましたが、Softimage2011以降はGetSource()が返すShaderParameterクラスを経由するようになっています。ShaderParameterクラスからShaderクラスを取得する場合はSIObject::GetParent()を使います。


ShaderParameter shaderParam( param.GetSource() );
if( shaderParam.IsValid() ) {
        Shader shader( shaderParam.GetParent() );
        if(shader.IsValid() ) {
                /* シェーダー情報を取得 */
        }
}


これでMaterialクラスに繋がっているShaderクラスが取得できました。今度はShaderクラスに繋がっているTextureクラスを取得します。ShaderクラスもMaterialクラスと同様にパラメータを持っているので、ProjectItem:: GetParameters()でパラメータを取得します。

Shader shader( shaderParam.GetParent() );
CParameterRefArray parameters = shader.GetParameters();
const int num = parameters.GetCount();
for( i=0; i<num; i++ ) {
        Parameter param = parameters[ i ];
        ShaderParameter shaderParamTex( param.GetSource() );
        if(shaderParamTex .IsValid() ) {
                ...
}


ShaderクラスからTextureクラスが取得できたら、そこからTexture::GetImageClip()を使ってImageClip2クラスを取得します。「ImageClip2クラス」に「2」が付いているのは互換性のためです。古いバージョンの「ImageClipクラス」もありますが、こちらは古いので使ってはいけません。

Texture texture(shaderParamTex.GetParent() );
ImageClip2 imageClip = texture.GetImageClip();


これで最後のイメージクリップノードまで取得できました。基本的にはここで説明した方法でノードの接続状態を取得していきますが、いくつか例外があります。ここでは環境マップと法線マップを使うケースを紹介します。

環境マップを使う場合は以下のようなレンダーツリーになります。この構造ではシェーダーノードに直接イメージクリップノードが繋がっているので、ShaderクラスからShader::GetImageClips()関数を使ってImageClip2クラスを取得します。

Shader shader( source.GetParent() );
ImageClip2 imageClip = shader.GetImageClips()[ 0 ];


法線マップの場合はこちらの図のようにマテリアルに直接NormalMap3ノードが接続されています。NormalMap3ノードはShaderクラスなので、Materialクラスのパラメータから同じように取得することができます。


ここまででレンダーツリーのノード構造にアクセスすることができるようになりました。今度は各シェーダーノードが持っているパラメータの値を取得します。ゲーム開発ではdiffuse,specularなどのカラーとtransparencyの値を使うことが多いでしょう。これらのパラメータの種類はシェーダーノードによって変わってきます。たとえばイルミネーション用のPhoneノードにはambient、diffuse、specular、transparency、incandescenceなどのパラメータがありますが、Lambertノードにはspecularがありません。


ShaderクラスのパラメータはProjectItem::GetParameter()にパラメータの名前を渡して取得します。パラメータが存在しない場合はGetParameter()が無効なオブジェクトを返します。

Parameter paramDiffuse = shader.GetParameter( L"diffuse" );
Parameter paramSpecular = shader.GetParameter( L"specular" );
Parameter paramTransparency = shader.GetParameter( L"transparency" );
Parameter paramIncandescence = shader.GetParameter( L"incandescence" );

Parameterクラスはintやfloat、カラーなどの様々な種類の値を持つことができます。Parameterから値を取得する場合、通常は内部の値の種類をParameter::GetValueType()を使って判別します。ここではカラーが格納されていることがわかっているので、カラーの値を直接取得しています。

Parameterクラスにカラーが入っている場合は、RGBの各成分が子供のParameterとして格納されています(Parameterクラスは入れ子構造が可能)。カラーの場合、"red","green","blue"という名前の子供パラメータが格納されているので、そちらから実際のRGBの値を取得します。

if( paramDiffuse.IsValid() ) {
        const float r = paramDiffuse.GetParameterValue( L"red" );
        const float g = paramDiffuse.GetParameterValue( L"green" );
        const float b = paramDiffuse.GetParameterValue( L"blue" );
}

Softimageのtransparencyはfloatではなく、カラー型なので注意してください。またこちらはRGBではなくRGBAの4要素となっています。今回作成しているエクスポーターでは透明度をfloatとして扱いたいので、"alpha"の値だけ使用しています。

if( paramTransparency.IsValid() ) {
        const float r = paramTransparency.GetParameterValue( L"red" );
        const float g = paramTransparency.GetParameterValue( L"green" );
        const float b = paramTransparency.GetParameterValue( L"blue" );
        const float a = paramTransparency.GetParameterValue( L"alpha" );
}



これでシェーダーノードのパラメータの値を取得することができました。次にパラメータにテクスチャが繋がっているケースを考えて見ましょう。Softimageではdiffuseなどのパラメータにテクスチャを繋いでしまうと、その部分のカラーが変更できなくなります。このときのカラーの値をどう扱うかはエクスポーターの実装によって異なります。テクスチャが繋がっているときは、カラーを白で固定してしまう方法もありますが、今回はテクスチャとカラーの両方を設定できるようにしています。

ここもSoftimage2011から変わった部分なので注意が必要です。以前はパラメータにテクスチャノードを繋ぐとそこのカラーは取得できなかったのですが、Softimage2011から接続前のカラーの値が取得できるようになりました。とは言っても、カラーの値を変更するたびにテクスチャの接続を外すのは大変面倒です。

このような場合にはミキサーノードを使うことができます。ミキサーノードは複数のカラーを混ぜるために使われるノードです。こちらの図のようにシェーダーノードとテクスチャノードの間にミキサーノードを挟み込むと、テクスチャを繋いだままカラーを設定できるようになります。


ミキサーノードはShaderクラスなので識別にはProgIDを使います。ミキサーノードの中で設定できるカラーは"base_color"というパラメータに入っています。

if( shader.GetProgID() == L"Softimage.sib_color_2mix.1.0" ||
        shader.GetProgID() == L"Softimage.sib_color_8mix.1" ) {
        Parameter paramBaseColor = shader.GetParameter( L"base_color" );
        float r = paramBaseColor.GetParameterValue( "red" );
        float g = paramBaseColor.GetParameterValue( "green" );
        float b = paramBaseColor.GetParameterValue( "blue" );
}

同じパラメータにテクスチャを2枚以上接続する場合にもミキサーノード(Mix_8colors)が使えます。カラーの取得方法は同じです。こちらの画像ではミキサーノードを使ってdiffuseに2枚のテクスチャを接続しています。


このようにテクスチャを2枚以上接続するケースではイルミネーション用のシェーダーノードの「レイヤー」という部分を使うこともできます。どちらの方法も一長一短なので、アーティストと相談して決めてください。


最後はテクスチャの情報を取得しましょう。まずイメージクリップノードから画像ファイルの名前を取得します。画像ファイルの名前はImageClip2::GetFileName()からフルパスで取得できます。今回はファイル名だけエクスポートしたいので、パスの部分は削除するようになっています。イメージクリップノードの画像ファイルが設定されていない場合は、GetFileName()は空文字を返します。

CString filename = imageClip.GetFileName();

次にテクスチャが使っているUV座標の名前を取得します。テクスチャが使っているUV座標はこちらの図の「テクスチャプロジェクション」の部分で指定します。この情報によって、ジオメトリ側のUV座標の参照ができるようになります。


テクスチャプロジェクションのパラメータはExplorer上では下の図の場所に格納されています。


テクスチャプロジェクションの値はTextureクラスの"tspace_id"というパラメータから取得します。

Parameter paramTSpaceID;
paramTSpaceID = texture.GetParameter( "tspace_id" );
CString uvName = paramTSpaceID.GetValue();

これでUV座標の名前を取得することができました。実は今回のサンプルのUV座標を取得する部分はもう少し複雑になっています。たとえば環境マップノードはUV座標を使用しないのでテクスチャプロジェクションの値を取得できません。またシーンによってはテクスチャプロジェクションで指定されているUV座標が実際には存在しないことがあります。この現象はテクスチャプロジェクションを設定した後でポリゴンメッシュ側のUV座標を削除した場合に起こります。サンプルの実装ではポリゴンメッシュ側のUV座標とテクスチャプロジェクションの名前を照合して、適切なUV座標を設定するようになっています。詳細はサンプルのソースを参照してください。

UV座標の次はテクスチャのリピート数を取得してみましょう。Softimageのリピート数は下の画像の「テクスチャリピート」で設定します。

テクスチャのリピート数はTextureクラスの"repeats"パラメータの"x"と"y"に入っています。

Parameter paramRepeats = texture.GetParameter( L"repeats" );
const float repeatX = paramRepeats.GetParameterValue( "x" );
const float repeatY = paramRepeats.GetParameterValue( "y" );

テクスチャのリピート数はテクスチャ自体の繰り返し回数のことです。Softimageではテクスチャのリピート数とは別にポリゴンメッシュのUVに対してスケールとオフセットをかけることができます。

リピート数はテクスチャの属性で、UVのスケールとオフセットはジオメトリの属性です。Softimageでは両方設定できるので、間違えないように注意してください。

これでマテリアルの情報が一通り取得できたと思います。今回はレンダーツリーを使って質感設定を行うワークフローを前提に説明してきましたが、複雑なレンダーツリーを毎回作るのは大変です。そして繋ぎ方を間違えたレンダーツリーを後から修正するのは一苦労です。

これを回避する方法は現状3つあります。一つ目はレンダーツリーを使わない方法です。すべての設定をカスタムパラメータに移してしまえば、レンダーツリーで行うのはテクスチャの接続だけになります。Softimage上でレンダリングする必要がない場合は便利な方法といえます。

二つ目はレンダーツリーの構造をプリセットとして保存しておいて、それを使いまわす方法です。最初にプリセットを作成しておいて、それをチーム内で共有すればミスが大幅に減ります。あるいはレンダーツリーを自動的に構築するスクリプトを作ってもよいでしょう。

最後はシェーダーコンパウンドという機能を使う方法です。これはレンダーツリー上の複数のノードをまとめて、一つのノードにしてしまう機能です。複雑なノードをひと固まりにできるので設定が楽になります。また外部に公開するパラメータを取捨選択できるので、変更する部分だけ公開して、残りの余分なインターフェースを隠すことができます。

最後のシェーダーコンパウンドを使う場合はレンダーツリーへのアクセス方法が変わってしまうので、プログラムの修正が必要です。残念ながら今回のサンプルには含まれていませんので、使用する場合は対応を行ってください。

今回の内容でポリゴンの描画に必要な情報は一通りそろったと言えます。次回はアニメーションとカスタムパラメータの部分を説明してみたいと思います。

製品購入に関するお問い合わせ
オートデスク メディア&エンターテインメント 製品のご購入に関してご連絡を希望される場合は、こちらからお問い合わせください。