参照 (Reference)
- "directional derivative" by iq
概要
- iq さんによる「方向微分を利用して, 雲のようなボリュームのランバートシェーディングを高速化」する記事の簡易メモです.
- 雲のようなボリュームを持つものに対して, 計算を軽くするために単純なランバートシェーディングを行うことがあります.
- その際によく行われる方法としては, 密度関数 ρ(x,y) やρ(x,y,z) で近傍の差分を取って法線を求めてから, N dot L をする方法です.
- この際に ρ(x,y,z) の場合は下のソースコードのように法線の計算のために (x,y,z) の周囲の 6 点をサンプリングし, さらに (x,y,z) の地点のサンプリング結果を他の計算に使うので, 合計 6 + 1 = 7 回サンプリングをしています.
//
// Quoted from http://www.iquilezles.org/www/articles/derivative/derivative.htm
//
// 点 x の周辺の 6 点をサンプリングして差分を取って, 法線を求めます
vec3 calcNormal( in vec3 x, in float eps )
{
vec2 e = vec2( eps, 0.0 );
return normalize( vec3( function(x+e.xyy) - function(x-e.xyy),
function(x+e.yxy) - function(x-e.yxy),
function(x+e.yyx) - function(x-e.yyx) ) );
}
void render( void )
{
// ...
// ここで 1 点をサンプリング
float den = function( pos );
// ここで 6 点をサンプリング
vec3 nor = calcNormal( pos, eps );
// N dot L のランバートシェーディング
float dif = clamp( dot(nor,light), 0.0, 1.0 );
// ...
}
- ここで下図の方向微分に関する式を見ると, f(x) が密度関数( x は 3D の位置座標 )で, v が任意の方向を示しているとします.
- 式の右側の v に対して, ライトの方向 l を代入すると 右側の式は 「密度関数の近傍の差分によって求めた法線」dot「ライト方向」となり, これは先ほど上側で求めた 7 点のサンプリングによる N dot L のランバートシェーディングと同じ意味になります.
- つまり, 式の右側の方法の代わりに, 式の左側の方法を使っても同じ結果が得られることになります.
- 式の左側は v 方向に対する f(x) の勾配を示しています.
- 今回は v = l (ライト方向)としているので, ライト方向の密度関数の勾配を求めることで, 結果的に N dot L のランバートシェーディングが求められることになります.
- 下のソースコードのように密度関数を 2 点サンプリングすることで, ランバートシェーディングを行っています.
//
// Quoted from http://www.iquilezles.org/www/articles/derivative/derivative.htm
//
void render( void )
{
// ...
// ここで 1 点サンプリング
float den = function( pos );
// ランバートシェーディング = ライト方向の密度関数の勾配
// ここでも 1 点サンプリング
float dif = clamp( (function(pos+eps*light)-den)/eps, 0.0, 1.0 );
// ...
}
まとめ
- 「密度関数のライト方向の微分」=「密度関数の近傍差分で求めた法線 dot ライト方向」という考え方で, 密度関数 ρ(x,y,z) のサンプリング回数を 7 点 -> 2 点に減らす, という方法でした.