ラップライティング

wrap-lighting.png
wraplighting.png

説明

散乱を近似するモデルの一つ。
法線が光の方向に直交するときは拡散反射の寄与は0になる。

float diffuseLight=max(dot(_lightpos,_normal),0);

cos90°=0だから
ラップライティングでは、ライティングが通常は暗くなる点を越えて
物体を回りこむ(wrapする)ように、
ディフューズ関数を修正する。
これによって拡散反射のコントラストが減り、
必要なアンビエントフィルライティングの量が減る。
ラップライティングは、粗いつや消し表面をより正確なシミュレートを試みるOren-Nayarライティングモデルの粗野な近似だ。(1)
\begin{align} y=\frac{x+wrap}{1+wrap} \end{align}

シェーダではこんなふうに書く。

float wrap_diffuse=max(0,(dot(L,N)+wrap)/(1+wrap));

高速にするためには
u
光ベクトルと法線の内積

の1Dテクスチャにwrap_diffuseの値を入れておいて参照することだ。
肌をシミュレートするときは、
ライティングが0に近づくときに(dot(L,N)がってこと?)赤方向へ色が近づくようにするとうまくいくらしい。
同じテクスチャで
スペキュラーの指数関数をアルファチャンネルに含むこともできる。

ルックアップテーブルを作るためのコード

gl_Vertex.x
法線と光の内積 法線と光の内積は鏡面反射・拡散反射強度にも使われるアレだ。
gl_Vertex.y
法線と正反射ベクトルの内積?(Hという文字から推測)
vec4 GenerateSkinLUT(){
 float wrap=0.2;
float scatterWidth=0.3;
vec4 scatterColor=vec4(0.15,0.0,0.0,1.0);
float shininess=40.0;
float NdotL=gl_Vertex.x*2.0-1.0;//[0,1]から[-1,1]へ再マップ
float NdotH=gl_Vertex.y*2.0-1.0;
float NdotL_wrap=(NdotL+wrap)/(1+wrap);//ラップ ライティング
float diffuse=max(NdotL_wrap,0.0);
//光から影への遷移部分に色合いを加える
float scatter=smoothstep(0.0,scatterWidth,NdotL_wrap)*smoothstep(scatterWidth*20.,scatterWidth,NsotL_wrap);
float specular=pow(NdotH,shininess);
if(NdotL_wrap<=0) specular=0;
color C;
C.rgb=diffuse+scatter*scatterColor;
C.a=specular;
return C;
}

テクスチャに入れたルックアップテーブルを用いて肌をシェーディング

ShadeSkin(
sampler2D skinLUT,vec3 N,vec3 L,vec3 H,vec3 diffuseColor,vec3 specularColor)
{
vec2 s;
s.x=dot(N,K);
s.y=dot(N,H);
vec4 light=texture2D(skinLUT,s*0.5+0.5);
return diffuseColor*light.rgb+specularColor*light.a;
}
)[}

実装結果

これは光の回折をシミュレートしているのだ、と解釈して
scatterColorには赤っぽい値を入れた(赤は、青よりも波長が長く、回折しやすい)
wraplighting.png
ほんのーーーーり、赤い環ができているのがおわかりだろうか??
本当にこれで肌がレンダリングできるのかな?
他の色のプロパティをもうちょっとちゃんと肌っぽくしないとわからない。


shader

サポートサイト Wikidot.com shader