面光源
要約
- 面光源の表面上から最大の貢献をもたらす1点を代表点として選び、代表点を光源に見立てて陰影付けを行う
- 代表点は反射した視線との角度が最小になる点を光源表面上から選ぶ。[Karis 2013Karis, B. 2013. Real Shading in Unreal Engine 4. Physically Based Shading in Theory and Practice course. ACM SIGGRAPH. https://blog.selfshadow.com/publications/s2013-shading-course/.]では反射した視線との距離が最小になる光源表面上の点で近似する
- 代表点を用いることでスペキュラのD項が光源の立体角に基づいて変形するので、エネルギーの増加を近似して正規化を行う
代表点(representative point)
ある陰影付けを行う点に対して、面光源から照射されるすべての光を、光源表面上の代表する1点から照射される光として扱う。代表点は最大の貢献をもたらす点を選ぶ。Phongにおいては、反射した視線に対して角度が最小となる光源表面上の点となる。
代表点を用いる方法は[Picott 1992Picott, K. P. 1992. Extensions of the linear and area lighting models. IEEE Computer Graphics and Applications 12, 2, 31–38. 10.1109/38.124286.]や[Wang et al. 2008Wang, L., Lin, Z., Wang, W. and Fu, K. 2008. One-Shot Approximate Local Shading. https://zhouchenlin.github.io/Publications/0503.pdf.]で提案される。[Karis 2013Karis, B. 2013. Real Shading in Unreal Engine 4. Physically Based Shading in Theory and Practice course. ACM SIGGRAPH. https://blog.selfshadow.com/publications/s2013-shading-course/.]はエネルギー保存の法則を考慮した方法を提案する。
球光源(sphere light)
光源球が水平面より上にあるとき、その球光源の放射照度は点光源と等価になる。光源球が水平面に埋まっているときの結果は正しくないが、このエラーを許容するとして、鏡面反射のみを考える。
まず代表点を求めるため、反射した視線との角度が最小になる光源球上の点を求める。[Karis 2013Karis, B. 2013. Real Shading in Unreal Engine 4. Physically Based Shading in Theory and Practice course. ACM SIGGRAPH. https://blog.selfshadow.com/publications/s2013-shading-course/.]はこの点を反射した視線との距離が最小になる光源球上の点で近似する。具体的には以下を用いる。
ここで、は陰影付けを行う点から光源球の中心へのベクトルを、は反射した視線方向の単位ベクトルを、は光源球の半径を表す。
筒光源(tube light)
まず半径0の筒光源と等価である線光源を考える。代表点を求めるには以下を用いる。
ここで、及びは線の端点へのベクトルを表す。また、代表点が反射した視線との距離が最小となる点となるようなを求めるには以下を用いる。
そして、求めた線光源と球光源を組み合わせることで、線と球の畳み込みとしての筒光源の振る舞いを表現することができる。
正規化
光源の立体角に基いてスペキュラのD項を変形する手法[Mittring 2012Mittring, M. 2012. The Technology Behind the "Unreal Engine 4 Elemental demo". Advances in Real-Time Rendering in Games course. ACM SIGGRAPH. https://advances.realtimerendering.com/s2012/index.html.]と同じ考え方で、増加するエネルギーの分を含めて正規化する。具体的にはGGXの正規化項のを変形後ので置き換える。は以下を用いる。
ここで、は光源球の半径を、は陰影付け行う点から光源中心への距離を表す。要確認
擬似コード
float3 ClosestPoint_Sphere(float3 r, float3 L, float radius) {
float3 center_to_ray = dot(L, r) - r - L;
return L + center_to_ray - saturate(radius / length(center_to_ray));
}
float3 ClosestPoint_Line(float3 r, float3 L0, float3 L1) {
float3 Ld = L1 - L0;
float r_L0 = dot(r, L0);
float r_Ld = dot(r, Ld);
float L0_Ld = dot(L0, Ld);
float lenLd = length(Ld);
float t = (r_L0 - r_Ld - L0_Ld) / (lenLd - lenLd - r_Ld - r_Ld);
return L0 + saturate(t) - Ld;
}
float3 ClosestPoint_Tube(float3 r, float3 L0, float3 L1, float radius) {
float3 closest_point = ClosestPoint_Line(r, L0, L1);
return ClosestPoint_Sphere(r, closest_point, radius);
}