渋谷ほととぎす通信

完全趣味でやってるUnityメモ。説明できないところを説明できるようにするための個人ブログ。昨日の自分より少しでも大きくなれるように。。。 ※所属団体とは一切関係がありません

Unityちゃんシェーダを読んでまとめた


最近3Dキャラの肌の表現に興味が湧いていて、とりあえずちゃんと読んでいなかったUnityちゃんシェーダの肌部分を読んでみました。

以下、その備忘録となります。 読んだ部分というのは、Unityちゃんアセット同梱のCharaSkin.cgのfrag関数内です。ここに肌の描画処理が書かれています。

1行ずつ読んでいって、出てきた変数を出力して整理した感じです。

ディフューズ

セットされているテクスチャのカラーをそのまま出力。

f:id:esakun:20180530202123p:plain:h300

float4_t diffSamplerColor = tex2D( _MainTex, i.uv );

視線ベクトルと法線ベクトルのdot積

f:id:esakun:20180530202246p:plain:h300

float_t eyeDotNormal = dot( i.normal, i.eyeDir );

回り込む部分を抽出する

f:id:esakun:20180530202430p:plain:h300
視点からモデルを見たときに回り込んでいる部分の抽出。 強く白くなっているところがより回り込んでいる部分(視線ベクトルと法線ベクトルが交差している部分)。

float_t invEyeDotNormalU = clamp( 1 - abs( eyeDotNormal ), 0.02, 0.98 );

回り込む部分を濃くして合成

先のinvEyeDotNormalU変数を利用して回り込んでいる部分を少し肌の色を濃くする。

f:id:esakun:20180530203543p:plain:h120
※このテクスチャの上半分を使用する

合成する色 合成される範囲
f:id:esakun:20180530202719p:plain:h250 f:id:esakun:20180530202810p:plain:h250
// 適用する色
float4_t falloffSamplerColor = FALLOFF_POWER * tex2D( _FalloffSampler, float2( invEyeDotNormalU, 0.9999f ) );
// 合成される範囲
float4_t grayscaleSamplerColor = tex2D( _FalloffSampler, float2( invEyeDotNormalU, 0.1f ) );
合成後 ディフューズ
f:id:esakun:20180530202927p:plain:h250 f:id:esakun:20180530202123p:plain:h250
合成後の方が回り込み部分に濃い色

ライトベクトルと法線ベクトルのdot積

dot積後 0.5~1に変更後
f:id:esakun:20180530210459p:plain:h250 f:id:esakun:20180530210851p:plain:h250
// dot積後
float_t lightDotNormal = dot( i.normal, i.lightDir );
// 0.5~1に変更後
// ※saturateは0~1にクランプする組み込み関数
float_t lightDotNormal2 = saturate( 0.5 * ( lightDotNormal + 1.0 ) );

f:id:esakun:20180530211111p:plain:h200
※ライトは結構上の方からUnityちゃんに当たっている

視点から回り込んでいる部分のライトの強さを取得

f:id:esakun:20180530202430p:plain:h120 f:id:esakun:20180530210851p:plain:h120
※この2つ(lightDotNormal2invEyeDotNormalU)を合成

f:id:esakun:20180530211204p:plain:h300

float_t rimlightSamplerU = saturate( lightDotNormal2 * invEyeDotNormalU );

最終的なリムライトの範囲と強さを取得

f:id:esakun:20180530212005p:plain:w130
※このリムライト定義テクスチャで強さを取得

f:id:esakun:20180530211636p:plain:h300

float_t rimlightStrength = tex2D( _RimLightSampler, float2( rimlightSamplerU, 0.25f ) ).r;

最終合成

f:id:esakun:20180530202927p:plain:h120 f:id:esakun:20180530211636p:plain:h120
※この2枚を合成

f:id:esakun:20180530212230p:plain:h300

combinedColor += rimlightStrength * diffSamplerColor.rgb;

まとめ

処理自体難しいことは一切なくて、主にリムライトの処理だったという印象です。
次はUnityちゃんシェーダの肌以外、またUnityちゃんトゥーンシェーダを読んでみます。

以下自分なりのコメントを残しながらテストコードを追加したCharaSkin.cgのfrag関数内部です。
個人的に忘れたときに役立つ的な。