<< TOP



リアルタイム シェーダー その1
リアルタイムの トゥーン・シェーダー を作ろう!! (OpenGL)


    リアルタイム シェーダー編について


       ここで紹介する リアルタイム・トゥーン・シェーダー は 他の編同様、
      ”このキャラクターに必要だったもの” としてリアルタイム・シェーダーをもまとめて紹介するかたちにします。
       ですので、このキャラクター専用のシェーディング部分は当然出て来るのですが、
      そこから発展して各自に合った内容に発展できたらと考えます。

       今まで扱っていたものとは違う設定を見ることによって、
      また新たな表現を模索するきっかけになれれば幸いです。

       そんな ←このキャラクターを中心とした、”とりとめもない”事例ですが、どうぞ参考にして頂ければと思います。
       このキャラクターのファンになってくれたら もっとうれしい ^_^ ; です。  


 プロローグ
      今回は、リアルタイム・シェーダーについて、第1ページ目として記述しようかと思います。
     リアルタイム・シェーダーと聞くと実写的なもので、ノウハウとして敷居が高く、
    プログラマーに作ってもらうもの・・・、という印象があるかも知れません。
     そこで、このサイトではトゥーンのキャラクターを扱っているので、
    RT(リアルタイムの略)もToonShaderを模索し、
    グラフィックの人でもここまで作ることができるよ、ということを御紹介したいと考えます。

     最終的にはプログラマーさんにソースを見てもらって、
    同じ効果をより適正化・高速化したものに修正してもらうことが考えられます。
    しかし、プログラマーさんからはどんなRT_Shaderを用意すれば
    グラフィッカーが絵作りに必要十分なものになるのか理解しづらいとも言えるのです。
     まずはグラフィッカーがどういった映像をどのように表現したいのか
    プログラマーさんに実際に動くもので提示できることは映像開発の上で一歩も二歩も先んじていて、
    映像作成の効率化・高品質化につながるのではないか、という思いがあります。

     > ウンチク・前準備 OpenGL
     > Softimageでの設定、表示方法
     > RT_Toon を作ろう



 ウンチク・前準備
      最近、良くリアルタイムのトゥーンシェーダーについて耳にするようになって来たように感じませんでしょうか。
    それは、みなさんが使っているパソコンの性能がグントとアップしていて、
    実は”RTシェーダー”を平気で表示できるような環境を持っている事になっているという点と、
    今までのプレレンダーによる映像製作ではレンダリング時間がかかりずぎるのことへ対応として
    RTを使ってそれらしい映像作りができないだろうか・・・と模索し始めて来ているのではないかと考えます。
     その場で表示している絵がその結果であるRTシェーダーは、
    表示しているそのままの画像を映像素材として扱えれば、
    今までと違った映像製作のワークフローを構築できるのではないか、というアプローチなのです。
     もちろん、パソゲーや高性能な家庭用ゲーム機でも使える技でもありますね。
       さて、この RT_ToonShader って、いやー表記はかっこええですが、
    プログラマブルシェーダー、ピクセルシェーダー、・・・
    ようするにプログラム記述は必要ではありますが、
    シェーダー記述したものを .cgfx とか .fx のファイルとして保存します。

     Softimageでは、そのRenderTree上で上記ファイルをRealTimeシェーダーのノードから読み込んで、
    ビュー画面を OpenGL や DirextX9 に切り替えることによって表示され、
    用意した各機能に値を入れれば、インターラクティブに反映される、ということで実現しています。

    この時点で敷居が高いように感じますが、
    最近はそれをなるべくグラフィカルに設定できるようになっていたり、
    たくさんのサンプルが用意されていたりします。

     例えば、NVIDIA社さんのデベロッパー・ゾーン に FX Composer があります。
    http://developer.nvidia.com/object/fx_composer_home.html (NVIDIA社サイトにリンク)
     これをインストールし、サンプルがたくさん用意されているライブラリーページからD&Dすることによって
    色々な画像効果をその場で見ることができます。


    FX Composer 2.5の画面

     ここでの注意点としては、グラフィックボードを NVIDIA Quadro FX1700 を使用していることから 
    NVIDAのボードで再現できるであろうシェーダーの紹介であることと、
    最新のDirextX DirectX エンド ユーザー ランタイム Web インストーラを参照) はインストールしてあるということがあります。
    http://www.microsoft.com/downloads/details.aspx?displaylang=ja&FamilyID=2da43d38-db71-4c1b-bc6a-9b6652cd92a3 (マイクロソフト社サイトにリンク)
 OpenGL
     そしてもう1つの注意点としては、サンプルとしてココでご紹介する RT_Shader は .cgfx形式 で行こうと考えます。
    つまり OpenGL形式 になります。OpenGLに関しては少し知識が要ります。
     まず正しく3D描画を動作させる為に、The OpenGL Utility Toolkit (GLUT) が必要です。
    Windows版 (95/98/98SE/Me/NT/2000/XP) の最新版が http://www.xmission.com/~nate/glut.html にあります。
    執筆時点で GLUT 3.7.6 for Windows. が最新でした。
    Zipを解凍して中にある glut32.dll を C:\WINDOWS\system32 内にコピーします。
    <コピーに失敗した場合は、Administrator権限でコピーを行う必要があります。>

     OpenGL系の3D描画が正しく行えるかテストをしてくれる便利なものとして 
    OpenGL Extention Viewer というものがあります。執筆時点で Release 3.15 (Wed, 18 Nov 2009) 
    http://www.realtech-vr.com/glview/extensions.html の [ダウンロード] から得られます。
    インストール後、Testタブ内一番下に [Rendering tests] ボタンを押すと、OGL1.1~2.1 までをテストしてくれます。 



     なんか絵描きの分野か?と思いつつも、トゥーンの表現できたらいいよね、と、ワクワクしてきます・・・。


     注意点!! <メールウェア>
      ここで御紹介している FMT Toon Shading は、色々な既存サンプルを参考にしつつも、自ら作成しているものです。
    ですので、FMT Toon Shading をそのまま利用してもらっても結構なのですが、使用に関してはあくまでも自己責任でお願い致します。
     また、そのまま利用の際には、特に商業利用に関しては、できればメールにて事前連絡をして頂けたらうれしいです。
     作品公開可能な方は作品をお送りいただければ、UsersNotes内のギャラリーページに掲載させていただきますよ!
     送り先は、 siun-info@dc.comtec.daikin.co.jp です。

    ココで紹介するものを参考にしつつ、もっとこんなものが製作できたよ--!!!っていう前向きの姿勢を期待するものであります。


Softimageでの設定、表示方法
 .cgfxファイルと SoftimageでのOpenGL表示
      今回、ご紹介していくリアルタイムシェーダーのファイル形式を .cgfx とします。
    ここに XSI_00.x というXSIマークの3Dデータを御提供いたします。 >> XSI_00_x.zip

    FX Composer で上部一番右を OpneGL としておきます。 
    上部   ボタンから XSI_00.x を指定すると中央に 3D表示されます。



     続いて、左上の Materials 項目部分を右マウスボタンクリック>Add Material From New Effect... から
    AddEffectページが表示されるので、CgFxにチェックを入れて Nextボタンを押すと基本となるマテリアルが表示されます。
    Phongを指定して Nextボタンを押し、Finishボタンを押すと、マテリアル項目にPhongが表示されます。
    それを中央の3Dモデル上までD&Dすると表示がこのようになります。
     実はこれで、Phong.cgfx というファイルがどういう状態のシェーダーであるのか確認できた状態になります。
    中をテキストエディターで開いて見ると、これだけでも結構な行が書かれたものであることが解ります。



     では、Phong.cgfx というファイルをSoftimageの画面で確認してみましょう。
    COLLADAファイルの XSI_00_dae.zip を ファイル>Crosswalk >読み込む から画面表示させます。
    RenderTree から ノード>リアルタイム > Cg > Cgfxノードを出します。


    順番1、
      Cgfxノードをダブルクリックし、何もまだ設定されていない Cgfx設定画面を表示します。
      その 一般 CgFXファイル の空欄に Phong.cgfx  を設定します。
    順番2、
      Cgfxノードをマテリアルの Realtime に所に接続します。
    順番3、
      表示しているView画面右上の表示設定のプルダウンメニューから リアルタイムシェーダー>OpenGL を選択します。



     すると、動き出します。もし Noicon.pic がつながっている場合は、何かテキスチャーを設定してください。
    (下図例ではサンプルと同じ Default_color.dds がつながっています)



     出来たPPGの中で Lamp 0 はライトの位置です。
    LampColorはライトの色、AmbiColor はマテリアル;アンビエント の色です。 
    色々値を変化させて、ちゃんと動作するか遊んで見てください。
      (コードタブ にて、[コンパイル]というボタンを押すと、最初に記述されている項目を正しく表示してくれます)



     基本的な操作はこのようになります。ここまでは前置きの話でした…。


    では、いよいよ 本題のスタートです!!!

     追加情報
      このCgfxのPPG右上のアイコンから このシェーダーと値の設定された状態を プリセットとして保存できます。
      名前を付けて保存しておくと、次回からはここからいきなり始めることが出来ます。







RT_Toon を作ろう
 1, 基本色;デフューズ色の設定
     さーてはじまり,、はじまり・・・♪
    まずは基本から 単色設定までの記述になります。>> 解説とコードはFMTさんの御協力です、感謝・感謝・・・
    そして、最初から各種設定を織り込む済みからスタートしています。
     単色としてデフューズカラーを設定してみましょう。

//############################################################################//
//■FMT Toon Shading ver.001 ■                                                //
//                                                                            //
//                                                                            //
//■CgFX標準のマトリクス全部入り                                              //
//  使用しなかったとしても後で追加するよりは楽なので・・・                    //
//############################################################################//
float4x4	World 		: World			< string UIWidget="None"; >;
float4x4	WorldI 		: WorldInverse		< string UIWidget="None"; >;
float4x4	WorldIT 		: WorldInverseTranspose	< string UIWidget="None"; >;
float4x4	WorldView 	: WorldView		< string UIWidget="None"; >;
float4x4	WorldViewI 	: WorldViewInverse		< string UIWidget="None"; >;
float4x4	WorldViewIT 	: WorldViewInverseTranspose	< string UIWidget="None"; >;
float4x4	WorldViewProj 	: WorldViewProjection	< string UIWidget="None"; >;
float4x4	WorldViewProjI	: WorldViewProjectionInverse < string UIWidget="None"; >;
float4x4	WorldViewProjIT	: WorldViewProjectionInverseTranspose < string UIWidget="None"; >;
float4x4	View 		: View			< string UIWidget="None"; >;
float4x4	ViewI 		: ViewInverse		< string UIWidget="None"; >;
float4x4	ViewIT 		: ViewInverseTranspose	< string UIWidget="None"; >;
float4x4	ViewProj		: ViewProjection		< string UIWidget="None"; >;
float4x4	ViewProjI		: ViewProjectionInverse	< string UIWidget="None"; >;
float4x4	ViewProjIT	: ViewProjectionInverseTranspose < string UIWidget="None"; >;
float4x4	Proj		: Projection		< string UIWidget="None"; >;
float4x4	ProjI		: ProjectionInverse	< string UIWidget="None"; >;
float4x4	ProjIT		: ProjectionInverseTranspose < string UIWidget="None"; >;
//----------------------------------------------------------------------------//

//############################################################################//
//■パラメータUI作成、設定 //
// マテリアルパラメータ //
//############################################################################//
//ディフューズカラー float4 DiffuseColor : Diffuse と記載することもある
float4 DiffuseColor
<
	string UIName =  "DiffuseColor";
	string UIObject = "RGBA";
	string UIWidget = "Color";
> = {0.7f, 0.7f, 0.7f, 1.0f};
//----------------------------------------------------------------------------//

//############################################################################//
//■XSIからデータを取得し、変数を定義する構造体「appdata」                    //
//############################################################################//
struct appdata
{
	float4 pos : POSITION;	//頂点データ
	float4 nrml : NORMAL0;	//法線データ
};
//----------------------------------------------------------------------------//

//############################################################################//
//■頂点シェーダタからフラグメントデータへ渡す構造体「vertexOutput」          //
// 頂点シェーダのOUTの変数名と一致しなければいけません                       //
//############################################################################//
struct vertexOutput 
{
	float4 	hpos : POSITION;	//頂点データ
	float4	wVec0 : TEXCOORD0;	//法線データ
};
//----------------------------------------------------------------------------//

//############################################################################//
//■頂点シェーダプログラム                                                    //
//############################################################################//
vertexOutput FMT_VertexShader(appdata IN)
{
	vertexOutput OUT;

	// ポジションデータをワールド空間へ変換しOUT
	float4 Po = float4(IN.pos.xyz, 1.0);
	OUT.hpos = mul(WorldViewProj, Po);

	//	ワールド空間に変換した法線をOUT
	float4 N = float4(IN.nrml.xyz, 0.0);
	OUT.wVec0 = mul(WorldIT, N);

	return OUT;
}
//----------------------------------------------------------------------------//

//############################################################################//
//■フラグメントシェーダプログラム                                            //
//############################################################################//
float4 FMT_PixelShader(vertexOutput IN) : COLOR
{	
	float4 OutColor;
	OutColor = DiffuseColor;	//ディフューズカラーを設定
	
	return OutColor;

}
//----------------------------------------------------------------------------//

//############################################################################//
//■描画テクニック                                                            //
//############################################################################//
technique FK_ToonShading000Pass
{
	pass p0
	{
	//シェーダのプロファイルバージョン指定
	// VertexShader,FragmentShader共に4.0を指定
		VertexProgram = compile vp40 FMT_VertexShader();
		FragmentProgram = compile fp40 FMT_PixelShader();

	//アルファ&深度テスト
		AlphaTestEnable = true;
		DepthTestEnable = true;
		DepthFunc = LEqual;
	
	//アルファブレンディング
		BlendEnable = false;
		BlendFunc = int2(SrcAlpha, OneMinusSrcAlpha);
		BlendEquation = FuncAdd;

	//カリング
		CullFaceEnable = true;
		CullFace = back;

   }
}
//----------------------------------------------------------------------------//
//■更新履歴
//001
//・パラメータUIでカラーを一つ追加(Diffuseとする)
//・カラーを出力色(return OutColor)に設定
追加解説
シェーダの流れ 参照



< string UIWidget="None"; >
はパラメータのオプションであり、今回追記した

float4 DiffuseColor
<
string UIName = "DiffuseColor";
string UIObject = "RGBA";
string UIWidget = "Color";
> > = {0.7f, 0.7f, 0.7f, 1.0f};

の部分とも繋がる
ここでも上記部分をオプションとして追加すると
UIが表示されなくなる
例えばMaxのパーサーは
UVや頂点カラーを宣言する時に
string UIWidget="None" を
使用してUIに表示されてしまう
無駄を省くことも出来る






カラーの宣言方法

float4 DiffuseColor
↑Floatが4つの配列ですよという宣言
DiffuseColorは変数名でこれを
シェーダ内で使用

<>内のオプションがそれぞれ
UIName   がUI表示名
UIObject  がオブジェクトタイプ、
UIWidget  がUI表示タイプ
となっているが、
どちらかなくても大丈夫みたいな
そのへんパーサーがあいまいに
実装されているみたい^^

最後の
= {0.7f, 0.7f, 0.7f, 1.0f};
はRGBAのUI上の初期値です。
SoftimageはDiffuseがなぜか
0.7なのでそれに合わせている



変数定義 「appdata」






データ受け渡し「vertexOutput」











/**** vertex program ****/

FMT_VertexShader が
下の描画テクニック 項目で使用














/**** fragment program ****/

FMT_PixelShader が
下の描画テクニック 項目で使用



OutColor に
ディフューズカラーが入る







/**** technique ****/

XSI上の描画テクニック 欄に
FK_ToonShading000Pass
と表示される

FMT_VertexShader
FMT_PixelShader
か処理される

     これがファイルです >>  FMT_ToonShading001.cgfx

    FxComposer で見てみると、右上に DiffuseColor 欄が出来、色選択して変えるとオブジェクト色が変化します。
    右上の項目に設定内容がズラーット表示されないのは < string UIWidget="None"; >; と行末に書いたからです。



     このFxComposerは使いやすく、真ん中のタブを Editor にすると、.cgfxをテキスト表示し、
    構文にエラーが合った場合、その行が黄色になり、中央下段の Tasks にてエラー行とその内容が表示されて便利です。
    <難点は日本語表示してくれない。>


     さて、ここまでの.cgfxをSoftimage上で表示してみます。
    RenderTreeで、ノード>リアルタイム>Cg>Cgfxノードを取り出し、ダブルクリックして表示したPPG内で 上記.cgfxファイルを指定します。
    CgfxノードをマテリアルノードのRealtimeという所と接続して、画面をOpneGLに設定します。
    コードタブ内の [コンパイル]ボタン を押して、表示を更新させます。DiffuseColor の色を変えると ポリゴンオブジェクトの色が変化します。




RT_Toon を作ろう
 2, 輪郭線;シェーダーの複製
     さーて、当初予想より速く進んでいますよ・・・♪(私;Ritaroより・・・、バトンタッチ!!)

     影の内積にしようかとも思ったのですが、最初の流れどおり輪郭に行ってみました。
    というのもシェーディングとかは色々工夫を加えて行きますけど、輪郭は一回作ったらそれでほぼシェーダは固定なので、
    先に実装しておいた方が解り易いかなーっと思いまして・・・。
     で、輪郭なのですが、実際のオブジェクトが持つ頂点を二つに複製しなければいけません。
    既に世にありますツーン風の輪郭線を描画したゲーム(ジェット・・・・) があります通り、ジオメトリを複製すれば簡単なのですが、
    折角プログラマブルシェーダなのでシェーダで複製したいと思います。 <外野コメント すばらしい!!

     頂点シェーダにデータを取るのはサーフェイスと共有できます。元のオブジェクトは同じですからね。
    そこから先を二つに分けます。
    FMT_VertexShader の流れと、FMT_VertexShader_Outline の流れです。
    あ、流れという言葉が出来たので シェーダの流れ を簡単に説明しますと・・・。

    シェーダの流れ
      今回のドキュメントで言うならまず、

        1. ■XSIからデータを取得し、変数を定義する構造体 「appdata」

        2. ■頂点シェーダプログラム

        3. ■頂点シェーダからフラグメントデータへ渡す構造体 「vertexOutput」   

        4. ■フラグメントシェーダプログラム

        5. ■描画テクニック 各パス

    という感じのグループ分けの流れになります。
     で、1.は大概一つにして共有できますが、
    シェーダを頂点シェーダから切り替えたい場合は 2.からいきなり二つの川に分かれます。川中島の合戦ですね。
    頂点シェーダまで共通でいいんだよ~って言う場合は 4.から分かれます。

     シェーダ表現を完全に分けたい場合は 5.テクニックを分け
    一回の描画で複数種類のシェーダを一度に表示したい今回のような場合は、5.テクニックは一つでパスを複数持ちます。
    ツーパスっていう感じですね。
      ※それぞれ分けたいときは構造体名を分けないと途中からおかしなことになります。といってもSIがエラー行を表示してくますが・・・。

     さて、今回のコードからは追加記述分、注目部分の抜粋になります、全コードは.cgfxを見てみてください。


//############################################################################//
//■FMT Toon Shading ver.002 ■                                                //
//----------------------------------------------------------------------------//

//############################################################################//
//■パラメータUI作成、設定 //
// マテリアルパラメータ //
//############################################################################//
//輪郭線の太さ
float Bias
<
	string UIName =  "OutLine Bold";
	string UIWidget = "Slider";
	float UIMin = 0.0;
	float UIMax = 1.0;
	float UIStep = 0.01;
> = 0.02f;

//輪郭カラー
float4 BorderColor	
<
	string UIName =  "Border Color";
	string UIObject = "RGBA";
	string UIWidget = "Color";
> = {0.0f, 0.0f, 0.0f, 1.0f};

//ディフューズカラー float4 DiffuseColor : Diffuse と記載することもある
float4 DiffuseColor	
<
	string UIName =  "Diffuse Color";
	string UIObject = "RGBA";
	string UIWidget = "Color";
> = {0.7f, 0.7f, 0.7f, 1.0f};
//----------------------------------------------------------------------------//

<中略>

//############################################################################//
//■頂点シェーダタからフラグメントデータへ渡す構造体「vertexOutput」          //
// 頂点シェーダのOUTの変数名と一致しなければいけません                       //
//############################################################################//
struct vertexOutput
{
	float4 	hpos : POSITION;	//頂点データ
	float4	wVec0 : TEXCOORD0;	//法線データ
};
//----------------------------------------------------------------------------//

//############################################################################//
//■頂点シェーダタからフラグメントデータへ渡す構造体「vertexOutput」          //
// 頂点シェーダのOUTの変数名と一致しなければいけません                       //
//############################################################################//
struct vertexOutput_Outline
{
	float4 	hpos : POSITION;	//頂点データ
	float4	wVec0 : TEXCOORD0;	//法線データ
};
//----------------------------------------------------------------------------//

<中略>

//############################################################################//
//■輪郭用頂点シェーダプログラム                                              //
//############################################################################//
vertexOutput FMT_VertexShader_Outline(appdata IN)
{
	vertexOutput OUT;

	//	ワールド空間に変換した法線をOUT
	float4 N = float4(IN.nrml.xyz, 0.0);
	// ポジションデータを法線方向に押し出し:法線Nをかける
	// 太さをカメラに対して均一にするためZ(奥行き)をかける:oDepth
	float4 Po = float4(IN.pos.xyz, 1.0);
	float oDepth = abs(mul(View, Po).z);
	Po.xyz += (Bias / 10) * N * oDepth;	//スライダーを扱いやすくするために10で割る
	OUT.hpos = mul(WorldViewProj, Po);

	OUT.wVec0 = mul(WorldIT, N);

	return OUT;
}
//----------------------------------------------------------------------------//


//############################################################################//
//■輪郭用フラグメントシェーダプログラム                                      //
//############################################################################//
float4 FMT_PixelShader_Outline(vertexOutput_Outline IN) : COLOR
{	
	float4 OutColor;
	OutColor = BorderColor;	//輪郭カラーを設定
	
	return OutColor;

}
//----------------------------------------------------------------------------//


//############################################################################//
//■描画テクニック                                                            //
//############################################################################//
technique FK_ToonShading002Pass
{
	//サーフェイス用パス
	pass p0
	{
	//シェーダのプロファイルバージョン指定
	// VertexShader,FragmentShader共に4.0を指定
		VertexProgram = compile vp40 FMT_VertexShader();
		FragmentProgram = compile fp40 FMT_PixelShader();

	//アルファ&深度テスト
		AlphaTestEnable = true;
		DepthTestEnable = true;
		DepthFunc = LEqual;
	
	//アルファブレンディング
		BlendEnable = false;
		BlendFunc = int2(SrcAlpha, OneMinusSrcAlpha);
		BlendEquation = FuncAdd;

	//カリング
		CullFaceEnable = true;
		CullFace = back;

   }
	//輪郭用パス
	pass p1
	{
	//シェーダのプロファイルバージョン指定
	// VertexShader,FragmentShader共に4.0を指定
		VertexProgram = compile vp40 FMT_VertexShader_Outline();
		FragmentProgram = compile fp40 FMT_PixelShader_Outline();

	//アルファ&深度テスト
		AlphaTestEnable = true;
		DepthTestEnable = true;
		DepthFunc = LEqual;
	
	//アルファブレンディング
		BlendEnable = false;
		BlendFunc = int2(SrcAlpha, OneMinusSrcAlpha);
		BlendEquation = FuncAdd;

	//カリング
		CullFaceEnable = true;
		CullFace = front;	//輪郭のカリングは裏ではなく表面に設定します

   }
}
//----------------------------------------------------------------------------//
//■更新履歴
//001
//・パラメータUIでカラーを一つ追加(Diffuseとする)
//・カラーを出力色(return OutColor)に設定
//002/輪郭用の処理を追加
//・2パス目(pass p1)を追加
//・2パス目のCullFaceをfrontに設定
//・輪郭用のパラメータUIを追加 Bias , BorderColor
//・輪郭用の構造体「vertexOutput」を追加
//・輪郭用の頂点シェーダ(FMT_VertexShader_Outline)で頂点の法線方向の押し出しを計算
//・輪郭用のフラグメントシェーダ(FMT_PixelShader_Outline)で輪郭の色を設定
追加解説
シェーダの流れ 参照

宣言文は一緒










OutLine Bold
Border Color
が設定された
























データ受け渡し「vertexOutput」が2分割

vertexOutput
vertexOutput_Outline






















vertexOutput用のシェーダープログラム





















vertexOutput_Outline用のシェーダープログラム














/**** technique ****/

XSI上の描画テクニック 欄に
FK_ToonShading002Pass
と表示される




パスが2つになった

pass p0 
FMT_VertexShader
FMT_PixelShader
が処理されている


pass p1 
FMT_VertexShader_Outline
FMT_PixelShader_Outline
が処理されている

     これがファイルです >>  FMT_ToonShading002.cgfx

    今度はこれを FxComposer で見てみると、右上に 輪郭線の太さ/輪郭線の色/デフューズの色 欄が出来ています。



    ここまでの.cgfxをSoftimage上で表示してみます。ご覧の通りです。<スンバらしい






RT_Toon を作ろう
 3, 影の色;シェード色の設定 (ハーフランバート)
     だんだんシェーダーっぽくなって来ました。

     マテリアルの色の話に戻ります。
    今度は影の色を付ける…ということで、ライトの設定、
    そこから発せられた光から暗くなる部分を計算するのに 内積 の話が出来きます。

      内積 >> XSI ICE活用 頂点カラー :03 頂点カラーでランバート 参照

     そこで、内積に必要な法線ベクトルライトベクトルを用意しなければいけないので、そこの追加から手をつけまししょう。

    ・ディレクショナルライトのセマンティックを追加する:dirlight_vec0
    シェードカラーUI を追加する (シェーディングで明るい所と暗い所の色を設定)
    ・サーフェイスフラグメントシェーダに以下の計算を追加する
     ├ 頂点シェーダから出力した法線ベクトルをIN(nomalize関数で正規化)に設定する
     ├ セマンティック定義したdirlight_vec0 を入力する
     ├ ランバートの手法に基づいて内積を計算する
     ├ 内積ハーフランバートに変更する (1.0~-1.0範囲を1.0~0.0範囲に再設定)
     └ lerp関数を使用して DiffuseColor ShadeColor で色設定を可能にする

     となっております。
     ライトは dirlight_vec0 が Softimageシーン内の Infinitライト の一個目を見てくれます。
    増やしても一個だけです。複数見るのはまたおいおいやっていきましょう~ノシ

     ハーフランバートですが、ご存じない方おります??
    割と有名な手法で、ゲームを紹介するサイトにて " HalfLife2 " の表現手法のところで紹介していましたね。
    テクスチャトゥーンをやる時もこの計算は必要です。

     あとは lerp関数 ですね。
    これはCgリファレンスのドキュメントにも記載されていますが、
    色Aと色Bを普通にアルファブレンディングするための関数です。
    計算式にすると
    色A × アルファ +  × 色B × (1.0 - アルファ)

    となり、アルファの明暗で色Aと色Bをブレンドします。テクスチャブレンディングにも使われますね。^^

     追加記述分、注目部分の抜粋になります、全コードは.cgfxを見てみてください。


//############################################################################//
//■FMT Toon Shading ver.003 ■                                                //
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■Softimageのセマンティック(ユーザガイド:標準の注釈およびセマンティック参照)//
//############################################################################//
//ディレクショナルライト:Infinite
float4 dirlight_vec0 : SAS.DIRECTIONALLIGHT[0].DIRECTION;


//############################################################################//
//■パラメータUI作成、設定 //
// マテリアルパラメータ //
//############################################################################//
<中略>
//シェードカラー
float4 ShadeColor	
<
	string UIName =  "Shade Color";
	string UIObject = "RGBA";
	string UIWidget = "Color";
> = {0.2f, 0.2f, 0.2f, 1.0f};

<中略>
//----------------------------------------------------------------------------//
//############################################################################//
//■頂点シェーダプログラム                                                    //
//############################################################################//
vertexOutput FMT_VertexShader(appdata IN)
{
	vertexOutput OUT;

	// ポジションデータをワールド空間へ変換しOUT
	float4 Po = float4(IN.pos.xyz, 1.0);
	OUT.hpos = mul(WorldViewProj, Po);

	//	ワールド空間に変換した法線をOUT
	float4 N = float4(IN.nrml.xyz, 0.0);
	OUT.wVec0 = mul(WorldIT, N);

	return OUT;
}

//############################################################################//
//■サーフェイスフラグメントシェーダプログラム                                //
//############################################################################//
float4 FMT_PixelShader(vertexOutput IN) : COLOR
{	

	//	頂点シェーダから法線ベクトルをIN
	float3 wNormalVec;
	wNormalVec = normalize( IN.wVec0.xyz );

	// ライトベクトルを取得
	float3 wLightVec;
	wLightVec = normalize( -dirlight_vec0.xyz );

	// 内積を計算
	float dif;
	dif = dot( wNormalVec, wLightVec );
	// ハーフランバートに変更
	float hlambert;
	hlambert = clamp( ( dif + 1.0 ) * 0.5, 0.0, 1.0 );

	// 明るいところをDiffuseColorに暗いところをShadeColorに
	float4 ToonDiffuse;
	ToonDiffuse = lerp( ShadeColor, DiffuseColor, hlambert );

	float4 OutColor;
	OutColor = ToonDiffuse;	//ディフューズカラーを設定
	
	return OutColor;

}
//----------------------------------------------------------------------------//
<中略>

//----------------------------------------------------------------------------//
//■更新履歴
//003/シェーディングの用意(ハーフランバート)
//・ディレクショナルライトのセマンティックを追加:dirlight_vec0
//・シェードカラーUIを追加(シェーディングの明るいところと暗いところを色設定)
//・サーフェイスフラグメントシェーダに以下の計算を追加
//├頂点シェーダから出力した法線ベクトルをIN(nomalize関数で正規化)
//├セマンティック定義したdirlight_vec0を入力
//├ランバートの手法に基づいて内積を計算
//├内積をハーフランバートに変更(1.0~-1.0範囲を1.0~0.0範囲に再設定)
//└lerp関数を使用してDiffuseColorとShadeColorで色設定を可能に

宣言文は一緒







ディレクショナルライトの追加






ShadeColor
が設定された















法線データ取得
 ↓













 ↓
内積を取得
ドットマトリクス




ハーフランバートへの設定


ディフューズ色の設定







     これがファイルです >>  FMT_ToonShading003.cgfx

    今度はこれを FxComposer で見てみると、右上に dirlight_vec0 欄が出来ています。(45 -45 -45 0 という値を仮入力しています)
    ShadeColor も設定するとこんな色合いになります。



    では.cgfxをSoftimage上で表示してみます。
    1つ目のライトをデータを自動的に取得して陰影を計算しています。



     まだ仮段階ですが、キャラの肌に設定すると、こんな感じを得ることが出来ます。





RT_Toon を作ろう
 4, 色のグラデーション幅とその位置設定
       基本色から影の色へのグラデーションの長さと
    その位置が表面のどの角度付近に表示されるか決められると表現力が増します。
    そこで、それらを設定出来るように、
    シェーディングの幅とバイアスを調整できるパラメータを追加していきます。
     Floatパラメータの ShadingWidthShadingBias を追加しました。

    ShadingWidth:シェーディングの幅
    ShadingBias: シェーディングの位置
    です。

     ハーフランバートの値からShadingBias を 引いてやると 
    ShadingBias値 の 0 を基準にして -1 が暗い方向へ、 +1 が明るい方向 へとシフトします。

    さらにその結果へ smoothstep関数 を使います。
    smoothstep関数は 
    smoothstep(min,max, x)
    という構成になっていて、xの値をもとに min値、max値 で 0~1の間 で再スムーズ化されるといった関数になります。
    0以下、1以上は Clamp されるので hlambert を作ったときの再Clampは必要ないみたいです。
        ※NVidiaDeveloperサイトでDL出来るCg_Users_Manual_JP.pdfを参照してみてください。

    ShadingWidth を 2 で割っているのは、GUI上直感的に分かりやすいように、
    ShadingWidth が 0 の時に通常のシェーディング
    ShadingWidth が 1 の時に2階調シェーディング   とさせたかったからです。

        ※普通にsmoothstep( ShadingWidth, 1.0 - ShadingWidth, hlambert );ってしちゃうと
        ShadingWidthが0.5のときに早々に2階調になっちゃいますよね^^計算式にすると
     
     追加記述分、注目部分の抜粋になります、全コードは.cgfxを見てみてください。


//############################################################################//
//■FMT Toon Shading ver.004 ■                                                //
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■パラメータUI作成、設定                                                    //
// マテリアルパラメータ                                                     //
//############################################################################//
<中略>
//■サーフェイス用パラメータ
//ディフューズカラー float4 DiffuseColor : Diffuse と記載することもある
float4 DiffuseColor	
<
	string UIName =  "Diffuse Color";
	string UIObject = "RGBA";
	string UIWidget = "Color";
> = {0.7f, 0.7f, 0.7f, 1.0f};
//シェードカラー
float4 ShadeColor	
<
	string UIName =  "Shade Color";
	string UIObject = "RGBA";
	string UIWidget = "Color";
> = {0.2f, 0.2f, 0.2f, 1.0f};
//シェーディングの敷居値
float ShadingWidth
<
	string UIName =  "Shading Width";
	string UIWidget = "Slider";
	float UIMin = 0.0;
	float UIMax = 1.0;
	float UIStep = 0.01;
> = 0.0f;
//シェーディングのバイアス
float ShadingBias
<
	string UIName =  "Shading Bias";
	string UIWidget = "Slider";
	float UIMin = -1.0;
	float UIMax = 1.0;
	float UIStep = 0.01;
> = 0.0f;
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■サーフェイスフラグメントシェーダプログラム                                //
//############################################################################//
float4 FMT_PixelShader(vertexOutput IN) : COLOR
{	

	//	頂点シェーダから法線ベクトルをIN
	float3 wNormalVec;
	wNormalVec = normalize( IN.wVec0.xyz );

	// ライトベクトルを取得
	float3 wLightVec;
	wLightVec = normalize( -dirlight_vec0.xyz );

	// 内積を計算
	float dif;
	dif = dot( wNormalVec, wLightVec );
	// ハーフランバートに変更
	float hlambert;
	hlambert = clamp( ( dif + 1.0 ) * 0.5, 0.0, 1.0 ) - ShadingBias;

	// トゥーンのシェーディング幅を調整するための式
	hlambert = smoothstep( ShadingWidth / 2, 1.0 - ShadingWidth / 2, hlambert );

	// 明るいところをDiffuseColorに暗いところをShadeColorに
	float4 ToonDiffuse;
	ToonDiffuse = lerp( ShadeColor, DiffuseColor, hlambert );

	float4 OutColor;
	OutColor = ToonDiffuse;	//ディフューズカラーを設定
	
	return OutColor;

}
//----------------------------------------------------------------------------//
<中略>

//----------------------------------------------------------------------------//
//■更新履歴
//004/シェーディングをToon表現にするための工夫
//・ShadingWidthパラメータを追加して、色の境界の絞りを調整可能に
//・ShadingBiasを追加して、境界部分の位置を調整可能に

宣言文は一緒


























シェーディングの幅の設定



シェーディングの位置の設定































ハーフランバートから
ShadingBiasの引き算で
シェーディングの位置決め

2の割り算
シェーディングの幅の設定










     これがファイルです >>  FMT_ToonShading004.cgfx

    では.早々に cgfxをSoftimage上で表示してみます。
    ShadingWidth と Shading Bias のスライダーが追加されました。
    項目の頭に緑色のアイコンが付いていますので、当然アニメーションを設定することが可能です。



     まだ仮段階ですが、キャラの肌に設定すると、こんな感じを得ることが出来ます。




RT_Toon を作ろう
 5, テキスチャー設定
       今回はテクスチャです。
    今回はコメント欄も詳細記述されているので、同じような説明になります。
    追加されたものは、

    //├texture宣言 ;テクスチャファイルを指定することが出来るようになる
    //└sampler型でテクスチャの型を決めてシェーダに渡します。今回は2Dテクスチャなので sampler2D

    です。

    ちなみに sampler2D型 の中で定義している
    MinFilter = LinearMipMapLinear;
    MagFilter = Linear;

    は MinFilter が縮小フィルタで、 MagFilter が拡大フィルタ になっています。
    Nearest にすると ポイントサンプルの効果が得られて、拡大してもテクスチャのドットがぼけません。

     そしてテクスチャを展開するにはUVが必要ですね?
    UVは struct appdataTEXCOORD0 を追加すること で取得できます。
    TEXCOORD0~TEXCOORD7 の 8段 まで取得できます。
    TEXCOORD を追加することで シェーダPPGのUIにも TextureProjectionを設定するためのUI が表示されるようになりますね。


    FX Composerの場合

     頂点シェーダからフラグメントシェーダに渡すために、頂点シェーダにこの TEXCOORD0 を一旦入れてそのまま出力します。
    OUT.tVec1 = IN.uv0;

    そして、vertexOutput の struct に float4 tVec1 : TEXCOORD1; を用意します。
    頂点シェーダからフラグメントシェーダへは法線データもUVデータも同じ頂点のデータとしてしか認識させられないので、
    TEXCOORD に入れて渡すことになります。
      ※ここが今後いろいろ工夫する羽目になるので、覚えておくと良いでしょう。
     
     次に フラグメントシェーダ では tex2D関数 を使用して sampler で入力した テクスチャBaseMap をUVで展開します。
    そのテクスチャとトゥーンの結果を乗算して出力します。
    ToonDiffuse * bTex

     OutColorをRGBとA に分けて上記の乗算結果の RGB を OutColorのRGB に、
    テクスチャのアルファだけを OutColorのA に渡すことで、
    テクスチャのアルファで アルファブレンディング された結果になります。
    そこで、アルファブレンディングをするためには、サーフェイス用パス pass p0 の BlendEnable = true; とすることで、
    これ以下のアルファブレンディング設定に基づき、合成されます。(BlendFunc、FuncAdd
    ※例えばint2(SrcAlpha, OneMinusSrcAlpha);int2(One, One);とすると加算になりますね。

    といったところでシェーディング、テクスチャ、輪郭と要素はそろったので、あとは応用でいろんなことが出来るようになります。


     追加記述分、注目部分の抜粋になります、全コードは.cgfxを見てみてください。


//############################################################################//
//■FMT Toon Shading ver.005 ■                                                //
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■テクスチャ入力、設定                                                      //
//############################################################################//
	//ベーステクスチャ
texture BaseTexture
<
	string ResourceName = "Basemap.dds";
	string ResourceType = "2D";
>;
sampler2D BaseMap = sampler_state
{
	Texture = <BaseTexture>;
	MinFilter = LinearMipMapLinear;
	MagFilter = Linear;
};

<中略>
//############################################################################//
//■XSIからデータを取得し、変数を定義する構造体「appdata」                    //
//############################################################################//
struct appdata
{
	float4 pos : POSITION;	//頂点データ
	float4 nrml : NORMAL0;	//法線データ
	float4 uv0 : TEXCOORD0;	//UVデータ1
};
//----------------------------------------------------------------------------//
//############################################################################//
//■頂点シェーダタからフラグメントデータへ渡す構造体「vertexOutput」          //
// 頂点シェーダのOUTの変数名と一致しなければいけません                       //
//############################################################################//
struct vertexOutput 
{
	float4 	hpos : POSITION;	//頂点データ
	float4	wVec0 : TEXCOORD0;	//法線データ
	float4	tVec1 : TEXCOORD1;	//UVデータ
};
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■サーフェイスフラグメントシェーダプログラム                                //
//############################################################################//
float4 FMT_PixelShader(vertexOutput IN) : COLOR
{	
	float4 bTex;
	bTex = tex2D( BaseMap, IN.tVec1.xy );

	//	頂点シェーダから法線ベクトルをIN
	float3 wNormalVec;
	wNormalVec = normalize( IN.wVec0.xyz );

	// ライトベクトルを取得
	float3 wLightVec;
	wLightVec = normalize( -dirlight_vec0.xyz );

	// 内積を計算
	float dif;
	dif = dot( wNormalVec, wLightVec );
	// ハーフランバートに変更
	float hlambert;
	hlambert = clamp( ( dif + 1.0 ) * 0.5, 0.0, 1.0 ) - ShadingBias;

	// トゥーンのシェーディング幅を調整するための式
	hlambert = smoothstep( ShadingWidth / 2, 1.0 - ShadingWidth / 2, hlambert );

	// 明るいところをDiffuseColorに暗いところをShadeColorに
	float4 ToonDiffuse;
	ToonDiffuse = lerp( ShadeColor, DiffuseColor, hlambert );

	float4 result;
	result = ToonDiffuse * bTex;

	float4 OutColor;
	OutColor.rgb = result.rgb;	//ディフューズカラーを設定
	OutColor.a = bTex.a;

	return OutColor;

}
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■描画テクニック                                                            //
//############################################################################//
technique FK_ToonShadingPass
{
	//サーフェイス用パス
	pass p0
	{
	//シェーダのプロファイルバージョン指定
	// VertexShader,FragmentShader共に4.0を指定
		VertexProgram = compile vp40 FMT_VertexShader();
		FragmentProgram = compile fp40 FMT_PixelShader();

	//アルファ&深度テスト
		AlphaTestEnable = true;
		DepthTestEnable = true;
		DepthFunc = LEqual;
	
	//アルファブレンディング
		BlendEnable = true;
		BlendFunc = int2(SrcAlpha, OneMinusSrcAlpha);
		BlendEquation = FuncAdd;

	//カリング
		CullFaceEnable = true;
		CullFace = back;

   }
	//輪郭用パス
	pass p1
	{
	//シェーダのプロファイルバージョン指定
	// VertexShader,FragmentShader共に4.0を指定
		VertexProgram = compile vp40 FMT_VertexShader_Outline();
		FragmentProgram = compile fp40 FMT_PixelShader_Outline();

	//アルファ&深度テスト
		AlphaTestEnable = true;
		DepthTestEnable = true;
		DepthFunc = LEqual;
	
	//アルファブレンディング
		BlendEnable = false;
		BlendFunc = int2(SrcAlpha, OneMinusSrcAlpha);
		BlendEquation = FuncAdd;

	//カリング
		CullFaceEnable = true;
		CullFace = front;	//輪郭のカリングは裏ではなく表面に設定します

   }
}
//----------------------------------------------------------------------------//
<中略>
//----------------------------------------------------------------------------//
//■更新履歴
//005/ベースマップ扱いのテクスチャをシェーダで使用するための追加
//・テクスチャ入力、設定を追加
//├texture宣言でテクスチャファイルを指定することが出来るようになります。
//└sampler型でテクスチャの型を決めてシェーダに渡します。今回は2Dテクスチャなのでsampler2Dです
//・TEXCOORD0をstruct appdataに追加(頂点データ(UV))
//・OUT.tVec1 = IN.uv0;で頂点シェーダからフラグメントシェーダへ出力しfloat4	tVec1 : TEXCOORD1;
//・bTex = tex2D( BaseMap, IN.tVec1.xy );samplerで入力したテクスチャBaseMapをtVec1.xy(UV)展開
//・result = ToonDiffuse * bTex;ToonDiffuseとテクスチャbTexを乗算して出力
//・OutColor.a = bTex.a;出力のアルファをテクスチャのアルファから取得
//・サーフェイス用パスのBlendEnableをtrueに設定

宣言文は一緒








Textureの設定
仮に"Basemap.dds"となっていて
無い場合は
NoImage
NoIconpic
になる












UVデータ1として
uv0 : TEXCOORD0;









UVデータとして
tVec1 : TEXCOORD1;










tex2D

























bTex


RGB


























アルファブレンディングを有効






     これがファイルです >>  FMT_ToonShading005.cgfx

     では.テキスチャーのアルファーブレンドの表示を見てみましょう。
    BlueA_00.dds というテキスチャーは、アルファーが約50%入っているものです。
    これをテキスチャーの色だけで、グラデーション無しで半透明にするには、
    DiffuseColorを白、ShadingWidthを0、ShadinBiasを-1 に設定します。



    BaseTextureにBlueA_00.ddsテキスチャーが貼られていて、半透明になっています。(目の影)



     さて、その1は一旦ここまでとしたいと思います。
    ここまでのものでも、色々と遊べると思いますが、次は更に色々と面白い表現に挑戦していきたいと思います。



     という訳で、次回は リアルタイム シェーダー その2 です。
      乞う、ご期待!!