色のないマテリアル (グレースケール) しかないと仮定しましょう。次に、各ポイントでの BDRF は、単一の値の関数として表すことができます。
float BDRF(phi_in, theta_in, phi_out, theta_out, pointWhereObjWasHit);
ここで、phi
とtheta
は、検討中の 2 つの光線の方位角と天頂角です。純粋なランバート反射の場合、この関数は次のようになります。
float lambertBRDF(phi_in, theta_in, phi_out, theta_out, pointWhereObjWasHit)
{
return albedo*1/pi*cos(theta_out);
}
albedo
範囲は 0 から 1 です。これは、入射光がどれだけ再放出されるかを測定します。この係数1/pi
により、すべての発信ベクトルに対する BRDF の積分が 1 を超えないことが保証されます。ウィキペディアの記事 ( http://en.wikipedia.org/wiki/Path_tracing ) の単純なアプローチでは、この BRDF を次のように使用できます。
Color TracePath(Ray r, depth) {
/* .... */
Ray newRay;
newRay.origin = r.pointWhereObjWasHit;
newRay.direction = RandomUnitVectorInHemisphereOf(normal(r.pointWhereObjWasHit));
Color reflected = TracePath(newRay, depth + 1);
return emittance + reflected*lambertBDRF(r.phi,r.theta,newRay.phi,newRay.theta,r.pointWhereObjWasHit);
}
記事と Ross によって述べられているように、このランダム サンプリングは、多くの光がある方向と同じ確率でほとんど光が反射されない入射方向 (newRay の) を追跡するため、残念です。代わりに、多くの光が観測者に反射される方向を優先的に選択して、すべての方向で最終的な色への寄与ごとに等しいサンプル レートを設定する必要があります。そのためには、確率分布からランダムな光線を生成する方法が必要です。それを行うことができる関数が存在するとしましょう。この関数は、目的の PDF (理想的には BDRF と等しい必要があります) と入力光線を入力として受け取ります。
vector RandomVectorWithPDF(function PDF(p_i,t_i,p_o,t_o,point x), Ray incoming)
{
// this function is responsible to create random Rays emanating from x
// with the probability distribution PDF. Depending on the complexity of PDF,
// this might somewhat involved. It is possible, however, to do it for Lambertian
// reflection (how exactly is math, not programming):
vector randomVector;
if(PDF==lambertBDRF)
{
float phi = uniformRandomNumber(0,2*pi);
float rho = acos(sqrt(uniformRandomNumber(0,1)));
float theta = pi/2-rho;
randomVector = getVectorFromAzimuthZenithAndNormal(phi,zenith,normal(incoming.whereObjectWasHit));
}
else // deal with other PDFs
return randomVector;
}
ルーチンのコードは次のTracePath
ようになります。
newRay.direction = RandomVectorWithPDF(lambertBDRF,r);
Color reflected = TracePath(newRay, depth + 1);
return emittance + reflected;
サンプルの選択では明るい方向が優先されるため、スケーリング係数として BDRF を適用して再度重み付けする必要はありませんreflected
。ただし、何らかの理由で PDF と BDRF が異なる場合、PDF>BDRF の場合は常に出力を縮小し (それぞれの方向から多を選択した場合)、little を選択した場合は出力を強調する必要があります。コード内:
newRay.direction = RandomVectorWithPDF(PDF,r);
Color reflected = TracePath(newRay, depth + 1);
return emittance + reflected*BDRF(...)/PDF(...);
ただし、出力はBDRF/PDF
1 に等しい場合に最適です。
BDRF と正確に等しい完全な PDF を常に選択できないのはなぜですか? まず、一部のランダム分布は他の分布よりも計算が困難です。たとえば、albedo
パラメーターにわずかな変動があった場合でも、均一なサンプリングよりも非ナイーブ サンプリングの方がアルゴリズムははるかに優れていますが、わずかな変動には補正項BDRF/PDF
が必要になります。場合によっては、まったく実行できないことさえあります。赤、緑、青の異なる反射動作を持つ色付きのオブジェクトを想像してください。各色に 1 つずつ、3 つのパスでレンダリングするか、すべての色成分にほぼ適合する平均的な PDF を使用できますが、完全には適合しません。
フォン シェーディングのようなものを実装するにはどうすればよいでしょうか? 簡単にするために、色成分は 1 つだけであり、鏡面反射に対する拡散反射の比率は 60% / 40% であると仮定します (環境光の概念はパス トレーシングでは意味がありません)。次に、私のコードは次のようになります。
if(uniformRandomNumber(0,1)<0.6) //diffuse reflection
{
newRay.direction=RandomVectorWithPDF(lambertBDRF,r);
reflected = TracePath(newRay,depth+1)/0.6;
}
else //specular reflection
{
newRay.direction=RandomVectorWithPDF(specularPDF,r);
reflected = TracePath(newRay,depth+1)*specularBDRF/specularPDF/0.4;
}
return emittance + reflected;