CG・コンテンツ制作
  1. CG・コンテンツ制作トップ
  2. DAIKIN CG Channel
  3. UsersNotes
  4. シェーダ
  5. RealtimeShader : リアルタイム・トゥーン・シェーダーを作ろう!!
SUITE USERS NOTES
リアルタイムシェーダー その1
リアルタイムのトゥーン・シェーダーを作ろう!!(OpenGL)
リアルタイムシェーダー編について
Emi_banner4
ここで紹介するリアルタイム・トゥーン・シェーダー は 他の編同様、
”このキャラクターに必要だったもの”としてリアルタイム・シェーダーをもまとめて紹介するかたちにします。
ですので、このキャラクター専用のシェーディング部分は当然出て来るのですが、
そこから発展して各自に合った内容に発展できたらと考えます。

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

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

fmt_ritaro_ml

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

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

ウンチク・前準備OpenGL
Softimageでの設定、表示方法
RT_Toon を作ろう
 >> 1, 基本色;デフューズ色の設定
 >> 2, 輪郭線;シェーダーの複製
 >> 3, 影の色;シェード色の設定
 >> 4, 色のグラデーション幅とその位置設定
 >> 5, テキスチャー設定
パソコンのスペックWindowsXP SP3、Intel Core2Duo 3G、RAM; 3G、NVIDIA Quadro FX1700



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

rt_toon_shader_01さて、このRT_ToonShader って、いやー表記はかっこええですが、
プログラマブルシェーダー、ピクセルシェーダー、・・・
ようするにプログラム記述は必要ではありますが、
シェーダー記述したものを.cgfxとか.fxのファイルとして保存します。

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

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

例えば、NVIDIA社さんのデベロッパー・ゾーンに FX Composer があります。
http://developer.nvidia.com/object/fx_composer_home.html(NVIDIA社サイトにリンク)
これをインストールし、サンプルがたくさん用意されているライブラリーページからD&Dすることによって
色々な画像効果をその場で見ることができます。
rt_toon_shader_02s
FX Composer 2.5の画面
ここでの注意点としては、グラフィックボードをNVIDIA Quadro FX1700を使用していることから
NVIDAのボードで再現できるであろうシェーダーの紹介であることと、
最新のDirextXDirectX エンド ユーザー ランタイム Web インストーラを参照)はインストールしてあるということがあります。

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://realtech-vr.com/home/glviewの[ダウンロード]から得られます。
インストール後、Testタブ内一番下に[Rendering tests] ボタンを押すと、OGL1.1~2.1までをテストしてくれます。
rt_toon_shader_08
なんか絵描きの分野か?と思いつつも、トゥーンの表現できたらいいよね、と、ワクワクしてきます・・・。


注意点!!<メールウェア>
ここで御紹介している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としておきます。rt_toon_shader_04
上部 rt_toon_shader_05ボタンからXSI_00.xを指定すると中央に3D表示されます。
rt_toon_shader_06
続いて、左上の Materials 項目部分を右マウスボタンクリック>Add Material From New Effect... から
AddEffectページが表示されるので、CgFxにチェックを入れてNextボタンを押すと基本となるマテリアルが表示されます。
Phongを指定してNextボタンを押し、Finishボタンを押すと、マテリアル項目にPhongが表示されます。
それを中央の3Dモデル上までD&Dすると表示がこのようになります。
実はこれで、Phong.cgfxというファイルがどういう状態のシェーダーであるのか確認できた状態になります。
中をテキストエディターで開いて見ると、これだけでも結構な行が書かれたものであることが解ります。
rt_toon_shader_07
では、Phong.cgfxというファイルをSoftimageの画面で確認してみましょう。
COLLADAファイルの XSI_00_dae.zipファイル>Crosswalk >読み込むから画面表示させます。
RenderTree からノード>リアルタイム > Cg > Cgfxノードを出します。
rt_toon_shader_09s
順番1、
Cgfxノードをダブルクリックし、何もまだ設定されていない Cgfx設定画面を表示します。
その一般CgFXファイルの空欄にPhong.cgfxを設定します。

順番2、
CgfxノードをマテリアルのRealtimeに所に接続します。

順番3、
表示しているView画面右上の表示設定のプルダウンメニューからリアルタイムシェーダー>OpenGLを選択します。
rt_toon_shader_10s
すると、動き出します。もしNoicon.pic がつながっている場合は、何かテキスチャーを設定してください。
(下図例ではサンプルと同じ Default_color.dds がつながっています)
rt_toon_shader_11s
出来たPPGの中でLamp 0 はライトの位置です。
LampColorはライトの色、AmbiColor はマテリアル;アンビエントの色です。
色々値を変化させて、ちゃんと動作するか遊んで見てください。
(コードタブにて、[コンパイル]というボタンを押すと、最初に記述されている項目を正しく表示してくれます)
rt_toon_shader_12

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


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

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

rt_toon_shader_13



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";>;と行末に書いたからです。
rt_toon_shader_14s
このFxComposerは使いやすく、真ん中のタブをEditorにすると、.cgfxをテキスト表示し、
構文にエラーが合った場合、その行が黄色になり、中央下段のTasksにてエラー行とその内容が表示されて便利です。
 < 難点は日本語表示してくれない。 > 
rt_toon_shader_15
さて、ここまでの.cgfxをSoftimage上で表示してみます。
RenderTreeで、ノード>リアルタイム>Cg>Cgfxノードを取り出し、ダブルクリックして表示したPPG内で上記.cgfxファイルを指定します。
CgfxノードをマテリアルノードのRealtimeという所と接続して、画面をOpneGLに設定します。
コードタブ内の [コンパイル]ボタン を押して、表示を更新させます。DiffuseColorの色を変えるとポリゴンオブジェクトの色が変化します。
rt_toon_shader_16s



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で見てみると、右上に輪郭線の太さ/輪郭線の色/デフューズの色欄が出来ています。
rt_toon_shader_17
ここまでの.cgfxをSoftimage上で表示してみます。ご覧の通りです。<スンバらしい
rt_toon_shader_18



RT_Toon を作ろう

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

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

内積>>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 も設定するとこんな色合いになります。
rt_toon_shader_20
では.cgfxをSoftimage上で表示してみます。
1つ目のライトをデータを自動的に取得して陰影を計算しています。
rt_toon_shader_21
まだ仮段階ですが、キャラの肌に設定すると、こんな感じを得ることが出来ます。
rt_toon_shader_22s



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

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

 ハーフランバートの値からShadingBias を 引いてやると
ShadingBias値の0を基準にして-1が暗い方向へ+1が明るい方向へとシフトします。
rt_toon_shader_24
さらにその結果へ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階調シェーディングとさせたかったからです。
rt_toon_shader_25
※普通に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_shader_26s
まだ仮段階ですが、キャラの肌に設定すると、こんな感じを得ることが出来ます。



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

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

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

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

そしてテクスチャを展開するにはUVが必要ですね?
UVは struct appdataTEXCOORD0を追加すること で取得できます。
TEXCOORD0~TEXCOORD7 の 8段 まで取得できます。
TEXCOORD を追加することで シェーダPPGのUIにも TextureProjectionを設定するためのUI が表示されるようになりますね。
rt_toon_shader_28
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に設定します。
rt_toon_shader_29s
rt_toon_shader_30
BaseTextureにBlueA_00.ddsテキスチャーが貼られていて、半透明になっています。(目の影)
rt_toon_shader_31s

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

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

お気軽にお問い合わせください

Daikin CG News お申し込み

CGクリエイター向けのセミナー・イベントやキャンペーン、製品情報をメールマガジンでお届けします。(登録無料)

Twitter

ページの先頭へ