プロローグ
|
注意点!! <メールウェア>
ここで御紹介している FMT Toon Shading は、色々な既存サンプルを参考にしつつも、自ら作成しているものです。
ですので、FMT Toon Shading をそのまま利用してもらっても結構なのですが、使用に関してはあくまでも自己責任でお願い致します。
また、そのまま利用の際には、特に商業利用に関しては、できればメールにて事前連絡をして頂けたらうれしいです。
作品公開可能な方は作品をお送りいただければ、UsersNotes内のギャラリーページに掲載させていただきますよ!
送り先は、siun-info@dc.comtec.daikin.co.jp です。
ココで紹介するものを参考にしつつ、もっとこんなものが製作できたよ--!!!っていう前向きの姿勢を期待するものであります。
|
RT_Toon を作ろう
6, 色のグラデーション設定の模索 ;マルチシェーディングの合成手段
さーて、第2段のはじまり,、はじまり・・・♪
今回は3段階実装と共に、If文のサンプルにもなっています。
シェーディングの二段目、三段目は1段目と同じパラメータを複製して使用します。
合成方法は1段目のToonDiffuseに対して、2段目三段目をlerpでアルファブレンディングするように重ねています。
if文の方ですが、UIにbool形式のものを追加して、チェックボックスがTrueかFalseかで切り分けるようにします。
bool useBaseMap
<
string UIName = "Use BaseMap";
> > = true; |
例えば上記のチェックボックスはシェーダの中では
float4 bTex;
if (useBaseMap){
bTex = tex2D( BaseMap, IN.tVec1.xy );
} else {
bTex = float4( 1.0 );
} |
という形で参照され、Trueだった場合はテクスチャがbTex として送り出され、Falseの場合はbTex が白一色として送り出されます。
そうすることで、チェックがオフのときはテクスチャが表示されないということになります。
同様にuseShadingはシェーディングそのものを使用しないようにする。
useShading2、useShading3はそれぞれシェーディングの2段目3段目をオフにするという意味がこめられています。
※マメ知識
VBSでも同じ事ができますが、if 変数{}(VBSの場合はif 変数 then)としたときはBoolとしての比較が出来て、
if 変数 =true {}(VBSの場合はif 変数 = True then)と表記したときと同じ効果があります。
//############################################################################//
//■FMT Toon Shading ver.006 ■ //
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■パラメータUI作成、設定 //
// マテリアルパラメータ //
//############################################################################//
<中略>
//シェードカラー[Base]-----------------------------------------------
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;
//シェードカラー[2]-----------------------------------------------
bool useShading2
<
string UIName = "Use Shading 2";
> = false;
float4 ShadeColor2
<
string UIName = "Shade Color 2nd";
string UIObject = "RGBA";
string UIWidget = "Color";
> = {0.15f, 0.15f, 0.15f, 1.0f};
//シェーディングの敷居値2
float ShadingWidth2
<
string UIName = "Shading Width 2nd";
string UIWidget = "Slider";
float UIMin = 0.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = 1.0f;
//シェーディングのバイアス2
float ShadingBias2
<
string UIName = "Shading Bias 2nd";
string UIWidget = "Slider";
float UIMin = -1.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = -0.5f;
//シェードカラー[3]-----------------------------------------------
bool useShading3
<
string UIName = "Use Shading 3";
> = false;
float4 ShadeColor3
<
string UIName = "Shade Color 3rd";
string UIObject = "RGBA";
string UIWidget = "Color";
> = {0.1f, 0.1f, 0.1f, 1.0f};
//シェーディングの敷居値2
float ShadingWidth3
<
string UIName = "Shading Width 3rd";
string UIWidget = "Slider";
float UIMin = 0.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = 1.0f;
//シェーディングのバイアス3
float ShadingBias3
<
string UIName = "Shading Bias 3rd";
string UIWidget = "Slider";
float UIMin = -1.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = -0.3f;
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■サーフェイスフラグメントシェーダプログラム //
//############################################################################//
float4 FMT_PixelShader(vertexOutput IN) : COLOR
{
<中略>
// 明るいところをDiffuseColorに暗いところをShadeColorに
ToonDiffuse = lerp( ShadeColor, DiffuseColor, hlambert1 );
if (useShading2) { ToonDiffuse = lerp( ShadeColor2, ToonDiffuse, hlambert2 ); }
if (useShading3) { ToonDiffuse = lerp( ShadeColor3, ToonDiffuse, hlambert3 ); }
} else {
ToonDiffuse = DiffuseColor;
}
<中略>
//----------------------------------------------------------------------------//
//■更新履歴
//006/シェーディングを三段階にする処理を追加
//・シェーディングパラメータを二つ追加(一個目と内容は同じ)
//・2段目3段目を使うかどうかのチェックボックスを追加
//・そもそもシェーディングを使うかどうかのチェックボックスを追加
//・BaseMapテクスチャを適用するかどうかのチェックボックスを追加 |
宣言文は一緒
シェーディングを3段階に設定
複数のシェードカラー処理の
if 文
|
これがファイルです >> FMT_ToonShading006.cgfx
では.Softimage上で実験してみましょう。 このようなグラデーションを作成することが出来ます、綺麗ですね。
こんなワークフロー発見!!
だんだん複雑になって来る各種設定値は Presetファイル として保存することが出来ます。006_cgfx-rei01.Preset
この Presetファイル 006_cgfx-rei01.Preset は 読み込むCgFxファイルと各種値が全部 入っています。
なので、このPresetファイルをRenderTree上にD&Dするだけで すべての設定が完了済みで表示できます。
(読み込むべき .cgfxファイルの存在するディレクトリー位置は注意してください)
|
ところが・・・、各色の境目のグラデーション幅を狭めるようにして、色の段差のグラデーションを実現しようとした場合
こんな感じになって、各色調整が難しいことになってしまいました。
で、再度 シェーディング について考えることになりました。
|
RT_Toon を作ろう
7, シェーディングの調整
さーて、ここからの記述は Softimage 2011 になります。2011から リアルタイムシェーダーの管理が楽になりました。
詳しくは、FMTさんが書かれている、リアルタイムシェーダ活用 2011のシェーダ管理 を参考にしてください。
根本から直すのではなく、今まで作って来ている範囲内での調整をしてみました。
1) 輪郭線の値が大きすぎて、使う領域が 0.001〜0.005と小さい〜いので、1単位繰上げしました。
実モデルのデフォルトサイズにもよるのですが、そういえばこの輪郭のバイアスはMAX用のままでしたね。
2) テクスチャのアルファ対応
シェーダの286行目を観ていただければ分かるように、出力色のアルファ値にテクスチャのアルファが入っています。
float4 OutColor;
OutColor.rgb = result.rgb; //ディフューズカラーを設定
//OutColor.a = bTex.a;
OutColor.a = 1.0; |
もうちょっと後の方で再度どれをアルファとしてみるかを再設定にしたいということで、しばらくは1.0Constを入れておきます。
3) 値設定値の順番を変更
基本色、1色目、2色目、ハイライト とかっとアンドペーストで単純に定義順を移動させて並び順を変えることができます。
さて解説ですが、
パラメータの名前を変更して、
ハイライト と命名したシェーディングを加算に、そしてシャドウ3 を乗算にという計算に変更したのみになります。
あとはハイライトは 内積の 0 の方ではなく 1 の方に色が乗るようにしています。
この合成方法をフォトショップのレイヤーに置き換えた画像をアップしました。
これで色と役割が少しわかりやすくなるかも知れません。
//############################################################################//
//■FMT Toon Shading ver.007 ■ //
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■パラメータUI作成、設定 //
// マテリアルパラメータ //
//############################################################################//
<中略>
//############################################################################//
//■パラメータUI作成、設定 //
// マテリアルパラメータ //
//############################################################################//
//■輪郭用パラメータ
//輪郭線の太さ
float Bias
<
string UIName = "OutLine Bold";
string UIWidget = "Slider";
float UIMin = 0.0;
float UIMax = 10.0;
float UIStep = 0.1;
> = 0.2f;
//輪郭カラー
float4 BorderColor
<
string UIName = "Border Color";
string UIObject = "RGBA";
string UIWidget = "Color";
> = {0.0f, 0.0f, 0.0f, 1.0f};
//■サーフェイス用パラメータ
//
bool useBaseMap
<
string UIName = "Use BaseMap";
> = true;
//
bool useShading
<
string UIName = "Use AllShading";
> = true;
//ディフューズカラー float4 DiffuseColor : Diffuse と記載することもある
float4 BaseColor
<
string UIName = "Base Color";
string UIObject = "RGBA";
string UIWidget = "Color";
> = {0.929f, 0.702f, 0.369f, 1.0f};
//シェードカラー[Base]-----------------------------------------------
float4 Layer1Color
<
string UIName = "Layer1 Color";
string UIObject = "RGBA";
string UIWidget = "Color";
> = {0.737f, 0.545f, 0.202f, 1.0f};
//シェーディングの敷居値
float ShadingWidth
<
string UIName = "Weight Bias";
string UIWidget = "Slider";
float UIMin = 0.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = 0.0f;
//シェーディングのバイアス
float ShadingBias
<
string UIName = "Weight Gain";
string UIWidget = "Slider";
float UIMin = -1.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = 0.0f;
//シェードカラー[3]-----------------------------------------------
bool useShading3
<
string UIName = "Use Shading 3";
> = false;
float4 ShadeColor3
<
string UIName = "Shade Color 3rd";
string UIObject = "RGBA";
string UIWidget = "Color";
> = {0.1f, 0.1f, 0.1f, 1.0f};
//シェーディングの敷居値2
float ShadingWidth3
<
string UIName = "Shading Width 3rd";
string UIWidget = "Slider";
float UIMin = 0.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = 0.75f;
//シェーディングのバイアス2
float ShadingBias3
<
string UIName = "Shading Bias 3rd";
string UIWidget = "Slider";
float UIMin = -1.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = -0.3f;
//シェードカラー[2]:ハイライトカラー------------------------------
bool useShading2
<
string UIName = "Use Hilight";
> = false;
float4 HighlightColor
<
string UIName = "Highlight Color";
string UIObject = "RGBA";
string UIWidget = "Color";
> = {1.00f, 0.722f, 0.809f, 0.821f};
//シェーディングの敷居値2
float ShadingWidth2
<
string UIName = "Blur";
string UIWidget = "Slider";
float UIMin = 0.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = 0.75f;
//シェーディングのバイアス2
float ShadingBias2
<
string UIName = "Cover";
string UIWidget = "Slider";
float UIMin = -1.0;
float UIMax = 1.0;
float UIStep = 0.01;
> = 0.4f;
//----------------------------------------------------------------------------//
<中略>
######################################################################//
//■サーフェイスフラグメントシェーダプログラム //
//############################################################################//
float4 FMT_PixelShader(vertexOutput IN) : COLOR
{
float4 bTex;
if (useBaseMap){
bTex = tex2D( BaseMap, IN.tVec1.xy );
} else {
bTex = float4( 1.0 );
}
float4 ToonDiffuse;
if (useShading){
// 頂点シェーダから法線ベクトルをIN
float3 wNormalVec;
wNormalVec = normalize( IN.wVec0.xyz );
// ライトベクトルを取得
float3 wLightVec;
wLightVec = normalize( -dirlight_vec0.xyz );
// 内積を計算
float dif;
dif = dot( wNormalVec, wLightVec );
// ハーフランバートに変更
float hlambert1;
float hlambert2;
float hlambert3;
hlambert1 = clamp( ( dif + 1.0 ) * 0.5, 0.0, 1.0 ) - ShadingBias;
if (useShading2) { hlambert2 = ( clamp( ( dif + 1.0 ) * 0.5, 0.0, 1.0 ) - ShadingBias2 ) * HighlightColor.a; }
if (useShading3) { hlambert3 = clamp( ( dif + 1.0 ) * 0.5, 0.0, 1.0 ) - ShadingBias3; }
// トゥーンのシェーディング幅を調整するための式
hlambert1 = smoothstep( ShadingWidth / 2, 1.0 - ShadingWidth / 2, hlambert1 );
if (useShading2) { hlambert2 = smoothstep( ShadingWidth2 / 2, 1.0 - ShadingWidth2 / 2, hlambert2 ); }
if (useShading3) { hlambert3 = smoothstep( ShadingWidth3 / 2, 1.0 - ShadingWidth3 / 2, hlambert3 ); }
// 明るいところをDiffuseColorに暗いところをShadeColorに
ToonDiffuse = lerp( Layer1Color, BaseColor, float4(hlambert1) );
if (useShading2) { ToonDiffuse += ( HighlightColor * hlambert2 ); }
if (useShading3) { ToonDiffuse *= lerp( ShadeColor3, float4( 1.0 ), hlambert3 ); }
} else {
ToonDiffuse = BaseColor;
}
float4 result;
result = ToonDiffuse * bTex;
float4 OutColor;
OutColor.rgb = result.rgb; //ディフューズカラーを設定
//OutColor.a = bTex.a;
OutColor.a = 1.0;
return OutColor;
}
<中略>
//----------------------------------------------------------------------------//
//■更新履歴
//007/シェーディングの役割を少し変更
//・追加シェーディング2をハイライトという役割に変更し加算演算へ変更
//・追加シェーディング3をシャドウと明確に分けるために乗算に変更
|
輪郭線
1単位繰上げ
値設定値の
順番を変更
ハイライト
を加算に
シャドウ3
を乗算に
ハイライトは
内積 1
1.0Const
|
これがファイルです >> FMT_ToonShading007.cgfx
色の作り込み感覚がまた違うのですが、前回より気分的に楽に綺麗な色が作り上げられます。
色の段差を絞り込んでいっても、ハイライト自身は全体にかかる感じなので、変な色合いになり難いです。
肌の色もぐんと探索できるようになりました。
|
RT_Toon を作ろう
8, マルチ・テキスチャーの設定
マルチテクスチャから再考ということで、まずはもう一枚テクスチャを設定できるように、セマンティックを追加します。
49〜60行目
//レイヤーテクスチャ�
texture LayerTexture001
<
string ResourceName = "Layermap001.dds";
string ResourceType = "2D";
>;
sampler2D LayerMap001 = sampler_state
{
Texture = <LayerTexture001>;
MinFilter = LinearMipMapLinear;
MagFilter = Linear;
}; |
次に struct appdata に追加した分のテクスチャに使うUV値を設定できるように TEXCOORD1 を追加しました。
この時に、どっちがどのテクスチャのUVか分かりやすいように変数名を変更しています。
float4 Basemap_uv : TEXCOORD0; //UVデータ1
float4 Layermap001_uv : TEXCOORD1; //UVデータ2 |
ここまで来ている人はもう書かなくてもお分かりだと思いますが、頂点シェーダでピクセルシェーダへUV値を渡す記述をします。
284行目285行目
OUT.tVec1 = IN.Basemap_uv;
OUT.tVec2 = IN.Layermap001_uv; |
struct vertexOutput にもう一つ分のUV値を追加して、
float4 tVec1 : TEXCOORD1; //UVデータ1
float4 tVec2 : TEXCOORD2; //UVデータ2 |
フラグメントシェーダ内にテクスチャをUV値で展開する記述を追加します。
float4 bTex;
if (useBaseMap){
bTex = tex2D( BaseMap, float2( IN.tVec1.x * BaseU, IN.tVec1.y * BaseV ) );
} else {
bTex = float4( 1.0 );
}
float4 l1Tex;
if (useLayerMap001){
l1Tex = tex2D( LayerMap001, float2( IN.tVec2.x * Layer001U, IN.tVec2.y * Layer001V ) );
} else {
l1Tex = float4( 1.0, 1.0, 1.0, 0.0 );
} |
これで変数 l1Tex が2枚目のテクスチャとなったわけです。
シェーダ内の記述しだいでいかようにも使えます。
ここでちょっと以前と違うと思われた方も多いと思います。
・・・そうです。
float2( IN.tVec2.x * Layer001U, IN.tVec2.y * Layer001V ) と UVの書き方が変わっていますね。
Ritaroさんの要望により、テクスチャのUV値それぞれにリピートを設定するために、
上のほうで追記している UIのセマンティック をそれぞれの U値 V値 に掛け算 しています。
掛け算をすることで、それぞれの最大値が増えていきますのでリピートされていくのです。
意外と簡単ですよね^^ (簡単じゃないよ〜<横やり プスッ>)
そうやってリピートされた上でピクセルに展開されたテクスチャは
310行目の一文で合成されてbTex として使用されます。
| bTex = lerp( bTex, l1Tex, l1Tex.a ); |
lerp関数もここではもうおなじみなのであえて解説するな、という方も多いかと思いますが、一応。
bTex(BaseMap展開) と l1Tex(LayerMap001展開) を l1Tex.a(LayerMap001展開後のアルファ値) で
アルファブレンディング しています。
とりあえずこれでテクスチャ二枚を設定できて、合成するというところまで準備が出来ました。
※注意
ちなみに現時点でそれぞれリソースのアルファの扱いがFIXしていないので、
このバージョンでは
として出力アルファを1つまり抜きなしに設定しています。
このあと頂点アルファでテクスチャブレンディングとか、結構特殊な処理を追加していくので、アルファの扱いが決まったらまた復活させます。
//############################################################################//
//■FMT Toon Shading ver.008 ■ //
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■テクスチャ入力、設定 //
//############################################################################//
//ベーステクスチャ
texture BaseTexture
<
string ResourceName = "Basemap.dds";
string ResourceType = "2D";
>;
sampler2D BaseMap = sampler_state
{
Texture = <BaseTexture>;
MinFilter = LinearMipMapLinear;
MagFilter = Linear;
};
//レイヤーテクスチャ�
texture LayerTexture001
<
string ResourceName = "Layermap001.dds";
string ResourceType = "2D";
>;
sampler2D LayerMap001 = sampler_state
{
Texture = <LayerTexture001>;
MinFilter = LinearMipMapLinear;
MagFilter = Linear;
};
//############################################################################//
//■パラメータUI作成、設定 //
// マテリアルパラメータ //
//############################################################################//
<中略>
//■サーフェイス用パラメータ
//テクスチャを表示/非表示
//[Base]-----------------------
bool useBaseMap
<
string UIName = "Use BaseMap";
> = true;
float BaseU
<
string UIName = "-- Repeat U";
string UIWidget = "Slider";
float UIMin = 1.0;
float UIMax = 16.0;
float UIStep = 0.01;
> = 1.0f;
float BaseV
<
string UIName = "-- Repeat V";
string UIWidget = "Slider";
float UIMin = 1.0;
float UIMax = 16.0;
float UIStep = 0.01;
> = 1.0f;
//[Layer001]-----------------------
bool useLayerMap001
<
string UIName = "Use LayerMap001";
> = true;
float Layer001U
<
string UIName = "-- Repeat U";
string UIWidget = "Slider";
float UIMin = 1.0;
float UIMax = 16.0;
float UIStep = 0.01;
> = 1.0f;
float Layer001V
<
string UIName = "-- Repeat V";
string UIWidget = "Slider";
float UIMin = 1.0;
float UIMax = 16.0;
float UIStep = 0.01;
> = 1.0f;
<中略>
//############################################################################//
//■XSIからデータを取得し、変数を定義する構造体「appdata」 //
//############################################################################//
struct appdata
{
float4 pos : POSITION; //頂点データ
float4 nrml : NORMAL0; //法線データ
float4 Basemap_uv : TEXCOORD0; //UVデータ1
float4 Layermap001_uv : TEXCOORD1; //UVデータ2
};
//----------------------------------------------------------------------------//
//############################################################################//
//■頂点シェーダタからフラグメントデータへ渡す構造体「vertexOutput」 //
// 頂点シェーダのOUTの変数名と一致しなければいけません //
//############################################################################//
struct vertexOutput
{
float4 hpos : POSITION; //頂点データ
float4 wVec0 : TEXCOORD0; //法線データ
float4 tVec1 : TEXCOORD1; //UVデータ1
float4 tVec2 : TEXCOORD2; //UVデータ2
};
//----------------------------------------------------------------------------//
<中略>
//############################################################################//
//■サーフェイス頂点シェーダプログラム //
//############################################################################//
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);
OUT.tVec1 = IN.Basemap_uv;
OUT.tVec2 = IN.Layermap001_uv;
return OUT;
}
//----------------------------------------------------------------------------//
//############################################################################//
//■サーフェイスフラグメントシェーダプログラム //
//############################################################################//
float4 FMT_PixelShader(vertexOutput IN) : COLOR
{
float4 bTex;
if (useBaseMap){
bTex = tex2D( BaseMap, float2( IN.tVec1.x * BaseU, IN.tVec1.y * BaseV ) );
} else {
bTex = float4( 1.0 );
}
float4 l1Tex;
if (useLayerMap001){
l1Tex = tex2D( LayerMap001, float2( IN.tVec2.x * Layer001U, IN.tVec2.y * Layer001V ) );
} else {
l1Tex = float4( 1.0, 1.0, 1.0, 0.0 );
}
bTex = lerp( bTex, l1Tex, l1Tex.a );
<中略>
//----------------------------------------------------------------------------//
//■更新履歴
//008/まずはテクスチャの二枚目を設定する
//・テクスチャ入力、設定をもう一つ追加(LayerTexture001)
//・TEXCOORDを追加してUVを二つ設定できるようにする
//・フラグメントシェーダでUV2個めが使えるように
//・UVそれぞれにリピート回数を設定できるように
//・tex2D()で展開するBasemapのUVそれぞれにリピートパラメータをかけてリピート回数を設定できるように
//・暫定処理で2枚目のアルファを元に1枚目のテクスチャとアルファブレンディング★ |
もう一枚テクスチャを設定
Use BaseMap
Use LayerMap001
の設定
TEXCOORD1;
の追加
もう一つ分のUV値
を追加
テクスチャのUV値
テクスチャをUV値で展開
する部分の追記
bTex として合成される
|
これがファイルです >> FMT_ToonShading008.cgfx
シェーディングの上にテキスチャーが追加されました。
2つの異なるUVテキスチャープロジェクションを使い分けられ、UVのリピート値を変更するとインターラクティブに変化していきます。
2枚目のテキスチャーのアルファー値によるブレンディングがこんな感じになっています。
|
という訳で、次回は リアルタイム シェーダー その 3 です。
  |
|