महत्व नमूनाकरण क्या है?


33

महत्व का नमूना क्या है? इसके बारे में पढ़े जाने वाले हर लेख में 'पीडीएफ' का उल्लेख है कि वह क्या है?

मैं जो इकट्ठा करता हूं, उससे महत्वपूर्ण नमूना एक गोलार्ध पर केवल नमूना क्षेत्रों के लिए एक तकनीक है जो दूसरों की तुलना में अधिक मायने रखती है। इसलिए, आदर्श रूप से, मुझे शोर को कम करने और गति बढ़ाने के लिए प्रकाश स्रोतों की ओर किरणों का नमूना लेना चाहिए। इसके अलावा, कुछ बीआरडीएफ के चराई कोणों की गणना में कोई अंतर नहीं है, इसलिए इससे बचने के लिए महत्वपूर्ण नमूने का उपयोग करना अच्छा है?

अगर मुझे कुक-टॉरेंस बीआरडीएफ के लिए महत्व के नमूने को लागू करना था तो मैं यह कैसे कर सकता था?


यह एक अच्छा पढ़ा हुआ लिंक है जो बताता है कि पीडीएफ क्या है। टीएल; डीआर एक पीडीएफ एक फ़ंक्शन है जो यादृच्छिक संख्याओं की (निरंतर उर्फ ​​फ्लोटिंग पॉइंट) की संभावना का वर्णन करता है। एक विशिष्ट पीडीएफ से यादृच्छिक संख्या उत्पन्न करना चुनौतीपूर्ण हो सकता है और ऐसा करने के लिए कुछ तकनीकें हैं। यह उनमें से एक के बारे में बात करता है। इसके बाद का लेख दूसरे तरीके के बारे में बात करता है। blog.demofox.org/2017/08/05/…
एलन वोल्फ

जवाबों:


51

संक्षिप्त जवाब:

महत्व नमूना वास्तविक कार्य के आकार के करीब एक अनुमानक को चुनकर मोंटे कार्लो एकीकरण में विचरण को कम करने की एक विधि है।

पीडीएफ संभावना घनत्व समारोह के लिए एक संक्षिप्त नाम है । एक pdf(x) किया जा रहा है उत्पन्न नमूने के तौर पर की संभावना देता है x

लंबा जवाब:

शुरू करने के लिए, आइए देखें कि मोंटे कार्लो एकीकरण क्या है और यह गणितीय रूप से कैसा दिखता है।

मोंटे कार्लो एकीकरण एक अभिन्न के मूल्य का अनुमान लगाने की एक तकनीक है। आमतौर पर इसका उपयोग तब किया जाता है जब इंटीग्रल के लिए बंद फॉर्म समाधान नहीं होता है। यह इस तरह दिख रहा है:

f(x)dx1Ni=1Nf(xi)pdf(xi)

अंग्रेजी में, यह कहता है कि आप फ़ंक्शन के क्रमिक यादृच्छिक नमूनों द्वारा औसत से एक अभिन्न अनुमान लगा सकते हैं। जैसे-जैसे N बड़ा होता जाता है, सन्निकटन घुलता जाता है और घोल के करीब आता जाता है। pdf(xi) प्रत्येक यादृच्छिक नमूने की प्रायिकता घनत्व फ़ंक्शन का प्रतिनिधित्व करता है।

आइए एक उदाहरण करें: अभिन्न I के मूल्य की गणना करें ।

I=02πexsin(x)dx

मोंटे कार्लो एकीकरण का उपयोग करते हैं:

I1Ni=1Nexsin(xi)pdf(xi)

यह गणना करने के लिए एक सरल अजगर कार्यक्रम है:

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)

यदि हम कार्यक्रम चलाते हैं तो हमें I = 0.4986941 मिलता हैI=0.4986941

भागों द्वारा पृथक्करण का उपयोग करके, हम सटीक समाधान प्राप्त कर सकते हैं:

I=12(1e2π)=0.4990663

आप देखेंगे कि मोंटे कार्लो समाधान बिल्कुल सही नहीं है। ऐसा इसलिए है क्योंकि यह एक अनुमान है। कहा कि, जैसा कि N अनंत तक जाता है, अनुमान सही उत्तर के करीब और करीब होना चाहिए। पहले से ही N=2000 कुछ रन सही उत्तर के समान हैं।

पीडीएफ के बारे में एक नोट: इस सरल उदाहरण में, हम हमेशा एक समान यादृच्छिक नमूना लेते हैं। एक समान यादृच्छिक नमूने का अर्थ है कि प्रत्येक नमूने को चुने जाने की सटीक समान संभावना है। हम रेंज में नमूना [0,2π] हां, pdf(x)=1/(2π0)

महत्व नमूना द्वारा काम करता है नहीं समान रूप से नमूने। इसके बजाय हम अधिक नमूने चुनने की कोशिश करते हैं जो परिणाम (महत्वपूर्ण) में बहुत योगदान करते हैं, और कम नमूने जो केवल परिणाम के लिए थोड़ा योगदान करते हैं (कम महत्वपूर्ण)। इसलिए नाम, महत्व का नमूना।

ffComparison of good sampling vs bad sampling

पथ अनुरेखण में महत्व के नमूने का एक उदाहरण यह है कि एक सतह को हिट करने के बाद किरण की दिशा कैसे चुनें। यदि सतह पूरी तरह से स्पेक्युलर नहीं है (यानी एक दर्पण या कांच), तो गोलार्ध में आउटगोइंग किरण कहीं भी हो सकती है।

Outgoing rays can go anywhere in the hemisphere

हम नई किरण उत्पन्न करने के लिए समान रूप से गोलार्ध का नमूना ले सकते हैं । हालाँकि, हम इस तथ्य का फायदा उठा सकते हैं कि रेंडरिंग समीकरण में एक कोसाइन फैक्टर है:

Lo(p,ωo)=Le(p,ωo)+Ωf(p,ωi,ωo)Li(p,ωi)|cosθi|dωi

Specifically, we know that any rays at the horizon will be heavily attenuated (specifically, cos(x) ). So, rays generated near the horizon will not contribute very much to the final value.

To combat this, we use importance sampling. If we generate rays according to a cosine weighted hemisphere, we ensure that more rays are generated well above the horizon, and less near the horizon. This will lower variance and reduce noise.

In your case, you specified that you will be using a Cook-Torrance, microfacet-based BRDF. The common form being:

f(p,ωi,ωo)=F(ωi,h)G(ωi,ωo,h)D(h)4cos(θi)cos(θo)

where

F(ωi,h)=Fresnel functionG(ωi,ωo,h)=Geometry Masking and Shadowing functionD(h)=Normal Distribution Function

The blog "A Graphic's Guy's Note" has an excellent write up on how to sample Cook-Torrance BRDFs. I will refer you to his blog post. That said, I will try to create a brief overview below:

The NDF is generally the dominant portion of the Cook-Torrance BRDF, so if we are going to importance sample, the we should sample based on the NDF.

Cook-Torrance doesn't specify a specific NDF to use; we are free to choose whichever one suits our fancy. That said, there are a few popular NDFs:

  • GGX
  • Beckmann
  • Blinn

Each NDF has it's own formula, thus each must be sampled differently. I am only going to show the final sampling function for each. If you would like to see how the formula is derived, see the blog post.

GGX is defined as:

DGGX(m)=α2π((α21)cos2(θ)+1)2

To sample the spherical coordinates angle θ, we can use the formula:

θ=arccos(α2ξ1(α21)+1)

where ξ is a uniform random variable.

We assume that the NDF is isotropic, so we can sample ϕ uniformly:

ϕ=ξ2

Beckmann is defined as:

DBeckmann(m)=1πα2cos4(θ)etan2(θ)α2

Which can be sampled with:

θ=arccos(11=α2ln(1ξ1))ϕ=ξ2

Lastly, Blinn is defined as:

DBlinn(m)=α+22π(cos(θ))α

Which can be sampled with:

θ=arccos(1ξ1α+1)ϕ=ξ2

Putting it in Practice

Let's look at a basic backwards path tracer:

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. we bounce around the scene, accumulating color and light attenuation as we go. At each bounce, we have to choose a new direction for the ray. As mentioned above, we could uniformly sample the hemisphere to generate the new ray. However, the code is smarter; it importance samples the new direction based on the BRDF. (Note: This is the input direction, because we are a backwards path tracer)

// 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);

Which could be implemented as:

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;
}

After we sample the inputDirection ('wi' in the code), we use that to calculate the value of the BRDF. And then we divide by the pdf as per the Monte Carlo formula:

// Accumulate the brdf attenuation
throughput = throughput * material->Eval(wi, wo, normal) / pdf;

Where Eval() is just the BRDF function itself (Lambert, Blinn-Phong, Cook-Torrance, etc.):

float3 LambertBRDF::Eval(float3 inputDirection, float3 outputDirection, float3 normal) const override {
    return m_albedo * M_1_PI * dot(inputDirection, normal);
}

Nice answer. The OP also asked about Cook-Torrance importance sampling which this answer doesn't touch upon.
PeteUK

6
I updated the answer to add a section about Cook-Torrance
RichieSams

For example GGX, to sample spherical coordinates angle cos(θ) we use the importance sampled formula to calculate the angle and use that in GGX as usual right? Or does the formula replace GGX entirely?
Arjan Singh

3
I added a section to help answer your questions. But, in short, your first method is correct. You use the sampling formula to generate a direction, then you use that new direction in the normal GGX formula and to get the pdf for the Monte Carlo formula.
RichieSams

For GGX how would I calculate/sample wi? I understand how to sample the spherical coordinates angle θ but for the actual direction vector how is that done?
Arjan Singh

11

If you have a 1D function f(x) and you want to integrate this function from say 0 to 1, one way to perform this integration is by taking N random samples in range [0, 1], evaluate f(x) for each sample and calculate the average of the samples. However, this "naive" Monte Carlo integration is said to "converge slowly", i.e. you need a large number of samples to get close to the ground truth, particularly if the function has high frequencies.

With importance sampling, instead of taking N random samples in [0, 1] range, you take more samples in the "important" regions of f(x) that contribute most to the final result. However, because you bias sampling towards the important regions of the function, these samples must be weighted less to counter the bias, which is where the PDF (probability density function) comes along. PDF tells the probability of a sample at given position and is used to calculate weighted average of the samples by dividing the each sample with the PDF value at each sample position.

With Cook-Torrance importance sampling the common practice is to distribute samples based on the normal distribution function NDF. If NDF is already normalized, it can serve directly as PDF, which is convenient since it cancels the term out from the BRDF evaluation. Only thing you need to do then is to distribute sample positions based on PDF and evaluate BRDF without the NDF term, i.e.

f=FGπ(nωi)(nωo)
And calculate average of the sample results multiplied by the solid angle of the domain you integrate over (e.g. 2π for hemisphere).

For NDF you need to calculate Cumulative Distribution Function of the PDF to convert uniformly distributed sample position to PDF weighted sample position. For isotropic NDF this simplifies to 1D function because of the symmetry of the function. For more details about the CDF derivation you can check this old GPU Gems article.

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.