Kısa cevap:
Önem örneklemesi , gerçek işlevin şekline yakın bir tahminci seçerek Monte Carlo Entegrasyonunda varyansı azaltma yöntemidir.
PDF , Olasılık Yoğunluk Fonksiyonunun kısaltmasıdır . Bir pdf(x) olarak oluşturulan rasgele bir numune olasılığını vermektedir x .
Uzun cevap:
Başlamak için, Monte Carlo Entegrasyonunun ne olduğunu ve matematiksel olarak nasıl göründüğünü gözden geçirelim.
Monte Carlo Entegrasyonu, bir integralin değerini tahmin etmek için kullanılan bir tekniktir. Genellikle integrale kapalı bir form çözümü olmadığında kullanılır. Bu gibi görünüyor:
∫f(x)dx≈1N∑i=1Nf(xi)pdf(xi)
İngilizce olarak, bu fonksiyondan art arda rasgele örneklerin ortalamasını alarak bir integrali hesaplayabileceğinizi söylüyor. Olarak N büyük olur, yaklaşım daha yakın ve daha yakın çözeltiye alır. pdf(xi) her rastgele örneğin olasılık yoğunluğu işlevini temsil eder.
Bir örnek yapalım: İntegral I değerini hesaplayın .
I=∫2π0e−xsin(x)dx
Monte Carlo Entegrasyonunu kullanalım:
I≈1N∑i=1Ne−xsin(xi)pdf(xi)
Bunu hesaplamak için basit bir python programı:
import random
import math
N = 200000
TwoPi = 2.0 * math.pi
sum = 0.0
for i in range(N):
x = random.uniform(0, TwoPi)
fx = math.exp(-x) * math.sin(x)
pdf = 1 / (TwoPi - 0.0)
sum += fx / pdf
I = (1 / N) * sum
print(I)
Eğer programı çalıştırırsak, I=0.4986941
Parçalara göre ayırma kullanarak tam çözümü elde edebiliriz:
ben= 12( 1 - e - 2 π ) = 0,4990663
Monte Carlo Çözümünün pek doğru olmadığını göreceksiniz. Bunun nedeni bir tahmin olmasıdır. Bu, N- sonsuzluğa giderken, tahminin doğru cevaba daha yakın ve daha yakın olması gerektiğini söyledi. Zaten N-= 2000 bazı çalışmalar neredeyse doğru cevaplarla aynı.
PDF hakkında bir not: Bu basit örnekte, her zaman düzenli rastgele bir örnek alırız. Düzgün bir rastgele örneklem, her bir numunenin seçilme ihtimalinin tamamen aynı olduğu anlamına gelir. [ 0 , 2 π] aralığında örnek alıyoruz , bu nedenle, p df( x ) = 1 / ( 2 π- 0 )
Önemi örnekleme çalışır değil tekdüze örnekleme. Bunun yerine, sonuca çok katkıda bulunan daha fazla örnek (önemli) ve sonuca yalnızca biraz katkıda bulunan daha az sayıda örnek (daha az önemli) seçmeye çalışıyoruz. Bu nedenle adı, önemi örnekleme.
ff
Yol İzlemede örneklemenin önemine bir örnek, bir yüzeye çarptıktan sonra bir ışın yönünün nasıl seçileceğidir. Yüzey mükemmel bir şekilde speküler değilse (yani bir ayna veya cam), giden ışın yarımkürede herhangi bir yerde olabilir.
Biz olabilir tekdüze yeni ışın üretmek için Yarımküreyi tadın. Bununla birlikte, oluşturma denkleminin içinde bir kosinüs faktörü olduğu gerçeğinden faydalanabiliriz:
LO( p , ωO) = Le( p , ωO) + ∫Ωf( p , ωben, ωO) Lben( p , ωben) | marulθben| dωben
marul( x )
Bununla mücadele etmek için örneklemeyi önem veriyoruz. Eğer kosinüs ağırlıklı bir yarımküreye göre ışınlar üretersek, ufkun üzerinde daha fazla ve daha ufkun yakınında daha fazla ışın üretilmesini sağlıyoruz. Bu, farkı azaltır ve gürültüyü azaltır.
Sizin durumunuzda, Cook-Torrance, microfacet tabanlı BRDF kullanacağınızı belirttiniz. Yaygın şekli:
f( p , ωben, ωO) = F( ωben, h ) G ( ωben, ωO, h ) D ( h )4 cos( θben) çünkü( θO)
nerede
F( ωben, h ) = Fresnel işleviG ( ωben, ωO, h ) = Geometri Maskeleme ve Gölge işleviD ( h ) = Normal Dağılım Fonksiyonu
"A Graphic's Guy's Note" blogunda Cook-Torrance BRDF'lerinin nasıl örnekleneceği ile ilgili mükemmel bir yazı var. Seni blog postasına yönlendireceğim . Bu, aşağıda kısa bir genel bakış oluşturmaya çalışacağımı söyledi:
NDF, genel olarak Cook-Torrance BRDF'nin baskın kısmıdır, bu nedenle örneklemeye önem verirsek, NDF'ye dayanarak örneklemeliyiz.
Cook-Torrance, kullanılacak belirli bir NDF belirtmiyor; hangisini istediğimize göre seçmekte özgürüz. Bununla birlikte, birkaç popüler NDF vardır:
Her NDF'nin kendi formülü vardır, bu nedenle her biri farklı şekilde örneklenmelidir. Sadece her biri için son örnekleme fonksiyonunu göstereceğim. Formülün nasıl elde edildiğini görmek istiyorsanız, blog gönderisine bakın.
GGX şöyle tanımlanır:
DG G X( m ) = α2π( ( α2- 1 ) çünkü2( θ ) + 1 )2
θ
θ = arccos( α2ξ1( α2- 1 ) + 1------------√)
ξ
φ
ϕ = ξ2
Beckmann şöyle tanımlanır:
DB a c k m a n n( m ) = 1πα2marul4( θ )e- tan2( θ )α2
Şunlarla örneklenebilir:
θ = arccos( 11 = α2ln( 1 - ξ1)--------------√)ϕ = ξ2
Son olarak, Blinn şöyle tanımlanır:
DB l i , n v-( m ) = α + 22 π( çünkü( θ ) )α
Şunlarla örneklenebilir:
θ = arccos( 1ξa + 11)ϕ = ξ2
Uygulamaya koymak
Temel bir geriye doğru yol izleyiciye bakalım:
void RenderPixel(uint x, uint y, UniformSampler *sampler) {
Ray ray = m_scene->Camera.CalculateRayFromPixel(x, y, sampler);
float3 color(0.0f);
float3 throughput(1.0f);
// Bounce the ray around the scene
for (uint bounces = 0; bounces < 10; ++bounces) {
m_scene->Intersect(ray);
// The ray missed. Return the background color
if (ray.geomID == RTC_INVALID_GEOMETRY_ID) {
color += throughput * float3(0.846f, 0.933f, 0.949f);
break;
}
// We hit an object
// Fetch the material
Material *material = m_scene->GetMaterial(ray.geomID);
// The object might be emissive. If so, it will have a corresponding light
// Otherwise, GetLight will return nullptr
Light *light = m_scene->GetLight(ray.geomID);
// If we hit a light, add the emmisive light
if (light != nullptr) {
color += throughput * light->Le();
}
float3 normal = normalize(ray.Ng);
float3 wo = normalize(-ray.dir);
float3 surfacePos = ray.org + ray.dir * ray.tfar;
// Get the new ray direction
// Choose the direction based on the material
float3 wi = material->Sample(wo, normal, sampler);
float pdf = material->Pdf(wi, normal);
// Accumulate the brdf attenuation
throughput = throughput * material->Eval(wi, wo, normal) / pdf;
// Shoot a new ray
// Set the origin at the intersection point
ray.org = surfacePos;
// Reset the other ray properties
ray.dir = wi;
ray.tnear = 0.001f;
ray.tfar = embree::inf;
ray.geomID = RTC_INVALID_GEOMETRY_ID;
ray.primID = RTC_INVALID_GEOMETRY_ID;
ray.instID = RTC_INVALID_GEOMETRY_ID;
ray.mask = 0xFFFFFFFF;
ray.time = 0.0f;
}
m_scene->Camera.FrameBuffer.SplatPixel(x, y, color);
}
IE. sahnenin etrafında zıplıyoruz, gittikçe renk ve ışık zayıflıyoruz. Her sıçramada, ışın için yeni bir yön seçmek zorundayız. Yukarıda belirtildiği gibi, biz olabilir düzgün yeni ışın üretmek için Yarımküreyi tadın. Ancak, kod daha akıllıdır; BRDF'yi temel alan yeni yönü örnekler. (Not: Bu giriş yönüdür, çünkü biz geriye doğru bir yol izleyicisiyiz)
// Get the new ray direction
// Choose the direction based on the material
float3 wi = material->Sample(wo, normal, sampler);
float pdf = material->Pdf(wi, normal);
Hangi olarak uygulanabilir:
void LambertBRDF::Sample(float3 outputDirection, float3 normal, UniformSampler *sampler) {
float rand = sampler->NextFloat();
float r = std::sqrtf(rand);
float theta = sampler->NextFloat() * 2.0f * M_PI;
float x = r * std::cosf(theta);
float y = r * std::sinf(theta);
// Project z up to the unit hemisphere
float z = std::sqrtf(1.0f - x * x - y * y);
return normalize(TransformToWorld(x, y, z, normal));
}
float3a TransformToWorld(float x, float y, float z, float3a &normal) {
// Find an axis that is not parallel to normal
float3a majorAxis;
if (abs(normal.x) < 0.57735026919f /* 1 / sqrt(3) */) {
majorAxis = float3a(1, 0, 0);
} else if (abs(normal.y) < 0.57735026919f /* 1 / sqrt(3) */) {
majorAxis = float3a(0, 1, 0);
} else {
majorAxis = float3a(0, 0, 1);
}
// Use majorAxis to create a coordinate system relative to world space
float3a u = normalize(cross(normal, majorAxis));
float3a v = cross(normal, u);
float3a w = normal;
// Transform from local coordinates to world coordinates
return u * x +
v * y +
w * z;
}
float LambertBRDF::Pdf(float3 inputDirection, float3 normal) {
return dot(inputDirection, normal) * M_1_PI;
}
İnputDirection'ı (koddaki 'wi') örnekledikten sonra, bunu BRDF'nin değerini hesaplamak için kullanırız. Ve sonra Monte Carlo formülüne göre pdf ile bölüyoruz:
// Accumulate the brdf attenuation
throughput = throughput * material->Eval(wi, wo, normal) / pdf;
Nerede Eval () sadece BRDF fonksiyonu kendisidir (Lambert, Blinn-Phong, Cook-Torrance, vs.):
float3 LambertBRDF::Eval(float3 inputDirection, float3 outputDirection, float3 normal) const override {
return m_albedo * M_1_PI * dot(inputDirection, normal);
}