การสุ่มตัวอย่างสำคัญคืออะไร


33

การสุ่มตัวอย่างที่สำคัญคืออะไร? ทุกบทความที่ฉันอ่านเกี่ยวกับมันระบุว่า 'PDF' นั่นคืออะไร

จากสิ่งที่ฉันรวบรวมการสุ่มตัวอย่างที่สำคัญคือเทคนิคหนึ่งไปยังพื้นที่ตัวอย่างบนซีกโลกที่มีความสำคัญมากกว่าที่อื่น ๆ ดังนั้นในอุดมคติฉันควรสุ่มตัวอย่างแสงเข้าหาแหล่งกำเนิดแสงเพื่อลดเสียงรบกวนและเพิ่มความเร็ว นอกจากนี้ BRDF บางตัวที่มุมแทะเล็มมีความแตกต่างเล็กน้อยในการคำนวณดังนั้นใช้การสุ่มตัวอย่างที่สำคัญเพื่อหลีกเลี่ยงสิ่งที่ดี?

ถ้าฉันต้องใช้การสุ่มตัวอย่างที่สำคัญสำหรับ Cook-Torrance BRDF ฉันจะทำสิ่งนี้ได้อย่างไร


นี่คือลิงค์การอ่านที่ดีที่อธิบายว่า PDF คืออะไร TL; DR a PDF เป็นฟังก์ชันที่อธิบายความน่าจะเป็นของเลขสุ่ม (ต่อเนื่องหรือเลขทศนิยม) การสร้างตัวเลขสุ่มจาก PDF ที่เฉพาะเจาะจงอาจเป็นเรื่องที่ท้าทายและมีเทคนิคบางประการในการทำเช่นนั้น เรื่องนี้พูดถึงหนึ่งในนั้น บทความหลังจากนี้หนึ่งพูดถึงวิธีอื่น blog.demofox.org/2017/08/05/…
Alan Wolfe

คำตอบ:


51

คำตอบสั้น ๆ :

การสุ่มตัวอย่างความสำคัญเป็นวิธีการลดความแปรปรวนในการรวม Monte Carlo โดยเลือกตัวประมาณใกล้กับรูปร่างของฟังก์ชันจริง

รูปแบบไฟล์ PDFเป็นตัวย่อสำหรับฟังก์ชั่นน่าจะเป็นความหนาแน่น ให้น่าจะเป็นของตัวอย่างที่สุ่มที่สร้างขึ้นเป็นxpdf(x)x

คำตอบยาว:

ในการเริ่มต้นให้ตรวจสอบการรวม Monte Carlo คืออะไรและดูเหมือนว่าทางคณิตศาสตร์

การบูรณาการ Monte Carlo เป็นเทคนิคในการประมาณค่าของอินทิกรัล โดยทั่วไปจะใช้เมื่อไม่มีโซลูชันแบบปิดสำหรับอินทิกรัล ดูเหมือนว่านี้:

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

ในภาษาอังกฤษสิ่งนี้บอกว่าคุณสามารถประมาณค่าอินทิกรัลได้โดยการสุ่มตัวอย่างแบบต่อเนื่องเฉลี่ยจากฟังก์ชัน เมื่อมีขนาดใหญ่การประมาณจะเข้าใกล้โซลูชันมากขึ้น หมายถึงฟังก์ชันความหนาแน่นของความน่าจะเป็นของตัวอย่างแต่ละตัวอย่างNpdf(xi)

ขอทำตัวอย่าง: การคำนวณมูลค่าของการหนึ่งฉันI

I=02πexsin(x)dx

มาใช้ Monte Carlo Integration:

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=12(1e2π)=0.4990663

คุณจะสังเกตเห็นว่าโซลูชัน Monte Carlo นั้นไม่ถูกต้องนัก นี่เป็นเพราะมันเป็นค่าประมาณ ที่กล่าวว่าเมื่อไปที่อนันต์การประมาณควรใกล้เคียงกับคำตอบที่ถูกต้องมากขึ้น ตอนนี้ที่วิ่งบางครั้งเกือบเหมือนคำตอบที่ถูกต้องNN=2000

หมายเหตุเกี่ยวกับ PDF: ในตัวอย่างง่ายๆนี้เรามักจะสุ่มตัวอย่างแบบสม่ำเสมอ ตัวอย่างแบบสุ่มที่สม่ำเสมอหมายถึงทุกตัวอย่างมีความน่าจะเป็นเหมือนกันทุกประการในการเลือก เราสุ่มตัวอย่างในช่วงดังนั้น,[0,2π]pdf(x)=1/(2π0)

การสุ่มตัวอย่างความสำคัญทำงานโดยไม่สุ่มตัวอย่างอย่างสม่ำเสมอ แต่เราพยายามเลือกตัวอย่างเพิ่มเติมที่มีส่วนร่วมกับผลลัพธ์ (สำคัญ) และตัวอย่างน้อยลงที่ให้ผลเพียงเล็กน้อย (สำคัญน้อยกว่า) ดังนั้นชื่อตัวอย่างความสำคัญ

หากคุณเลือกฟังก์ชั่นการสุ่มตัวอย่างที่มี pdf ตรงกับรูปร่างของมากคุณสามารถลดความแปรปรวนได้อย่างมากซึ่งหมายความว่าคุณสามารถสุ่มตัวอย่างได้น้อยลง อย่างไรก็ตามหากคุณเลือกฟังก์ชั่นการสุ่มตัวอย่างซึ่งมีค่าแตกต่างจากมากคุณสามารถเพิ่มความแปรปรวนได้ ดูรูปภาพด้านล่าง: รูปภาพจากวิทยานิพนธ์ของ Wojciech Jarosz ภาคผนวก Affเปรียบเทียบการสุ่มตัวอย่างที่ดีกับการสุ่มตัวอย่างที่ไม่ดี

ตัวอย่างหนึ่งของการสุ่มตัวอย่างที่สำคัญในการติดตามเส้นทางคือวิธีการเลือกทิศทางของรังสีหลังจากที่กระทบพื้นผิว หากพื้นผิวไม่ได้เป็นแบบ specular อย่างสมบูรณ์ (เช่นกระจกหรือกระจก) รังสีขาออกสามารถอยู่ที่ใดก็ได้ในซีกโลก

รังสีขาออกสามารถไปที่ใดก็ได้ในซีกโลก

เราสามารถสุ่มตัวอย่างซีกโลกเพื่อสร้างรังสีใหม่ อย่างไรก็ตามเราสามารถใช้ประโยชน์จากความจริงที่ว่าสมการการแสดงผลมีปัจจัยโคไซน์ใน:

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

โดยเฉพาะเรารู้ว่ารังสีใด ๆ ที่ขอบฟ้าจะถูกลดทอนอย่างหนัก (โดยเฉพาะ ) ดังนั้นรังสีที่เกิดขึ้นใกล้ขอบฟ้าจะไม่ส่งผลต่อค่าสุดท้ายมากนักcos(x)

เพื่อต่อสู้กับสิ่งนี้เราใช้การสุ่มตัวอย่างที่สำคัญ หากเราสร้างรังสีตามซีกน้ำหนักถ่วงโลกเรามั่นใจว่ามีการสร้างรังสีมากกว่าขอบฟ้าและใกล้กับขอบฟ้าน้อยกว่า สิ่งนี้จะลดความแปรปรวนและลดเสียงรบกวน

ในกรณีของคุณคุณระบุว่าคุณจะใช้ BRDF ที่ใช้ Cook-Torrance รูปแบบทั่วไปคือ:

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

ที่ไหน

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

บล็อก "A Graphic's Guy's Note" มีการเขียนที่ยอดเยี่ยมเกี่ยวกับวิธีการตัวอย่าง Cook-Torrance BRDF ผมจะนำคุณไปยังโพสต์บล็อกของเขา ที่กล่าวว่าฉันจะพยายามสร้างภาพรวมสั้น ๆ ด้านล่าง:

NDF โดยทั่วไปเป็นส่วนที่โดดเด่นของ Cook-Torrance BRDF ดังนั้นหากเราต้องการตัวอย่างที่สำคัญเราควรสุ่มตัวอย่างตาม NDF

Cook-Torrance ไม่ได้ระบุ NDF เฉพาะที่จะใช้ เรามีอิสระที่จะเลือกสิ่งที่เหมาะสมกับความแฟนซีของเรา ที่กล่าวว่ามี NDF ที่นิยมไม่กี่:

  • GGX
  • เบ็คมันน์
  • Blinn

แต่ละ NDF มีสูตรของตัวเองดังนั้นแต่ละตัวอย่างจะต้องแตกต่างกัน ฉันจะแสดงฟังก์ชันการสุ่มตัวอย่างสุดท้ายสำหรับแต่ละรายการเท่านั้น หากคุณต้องการดูว่าสูตรได้รับมาอย่างไรดูโพสต์บล็อก

GGXถูกกำหนดเป็น:

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

ในการเก็บตัวอย่างทรงกลมพิกัดมุมเราสามารถใช้สูตรได้:θ

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

โดยที่เป็นตัวแปรสุ่มแบบสม่ำเสมอξ

เราคิดว่า NDF isotropic ดังนั้นเราจึงสามารถสุ่มตัวอย่างอย่างสม่ำเสมอ:ϕ

ϕ=ξ2

Beckmannหมายถึง:

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

ซึ่งสามารถสุ่มตัวอย่างด้วย:

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

สุดท้ายBlinnถูกกำหนดเป็น:

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

ซึ่งสามารถสุ่มตัวอย่างด้วย:

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

วางไว้ในการปฏิบัติ

ลองดูที่ tracer path แบบพื้นฐานย้อนหลัง:

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 เราเด้งไปรอบ ๆ ฉากสะสมสีและการลดทอนแสงเมื่อเราไป ในการตีกลับแต่ละครั้งเราต้องเลือกทิศทางใหม่สำหรับรังสี ดังที่ได้กล่าวมาแล้วเราสามารถสุ่มตัวอย่างซีกโลกเพื่อสร้างรังสีใหม่ อย่างไรก็ตามรหัสนั้นฉลาดกว่า มันสำคัญตัวอย่างทิศทางใหม่บนพื้นฐานของ BRDF (หมายเหตุ: นี่คือทิศทางอินพุตเนื่องจากเราเป็นตัวติดตามเส้นทางย้อนหลัง)

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

ซึ่งสามารถนำมาใช้เป็น:

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

หลังจากที่เราสุ่มตัวอย่าง inputDirection ('wi' ในรหัส) เราจะใช้มันเพื่อคำนวณค่าของ BRDF จากนั้นเราหารด้วย pdf ตามสูตรมอนติคาร์โล:

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

โดยที่Eval ()เป็นเพียงฟังก์ชัน BRDF (Lambert, Blinn-Phong, Cook-Torrance, ฯลฯ ):

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

คำตอบที่ดี OP ยังถามถึงการสุ่มตัวอย่างความสำคัญของ Cook-Torrance ซึ่งคำตอบนี้ไม่ได้สัมผัส
PeteUK

6
ฉันอัปเดตคำตอบเพื่อเพิ่มหัวข้อเกี่ยวกับ Cook-Torrance
RichieSams

ตัวอย่างเช่น GGX เพื่อที่จะสุ่มตัวอย่างพิกัดมุมทรงกลม cos (θ) เราใช้สูตรตัวอย่างที่สำคัญในการคำนวณมุมและใช้มันใน GGX ตามปกติใช่ไหม? หรือสูตรแทนที่ GGX ทั้งหมดหรือไม่
Arjan Singh เมื่อ

3
ฉันได้เพิ่มหัวข้อเพื่อช่วยตอบคำถามของคุณ แต่ในระยะสั้นวิธีแรกของคุณถูกต้อง คุณใช้สูตรการสุ่มตัวอย่างเพื่อสร้างทิศทางจากนั้นคุณใช้ทิศทางใหม่นั้นในสูตร GGX ปกติและเพื่อรับ pdf สำหรับสูตร Monte Carlo
RichieSams

สำหรับ GGX ฉันจะคำนวณ / ตัวอย่างwiอย่างไร ฉันเข้าใจวิธีสุ่มตัวอย่างมุมทรงกลมพิกัดθ แต่สำหรับเวกเตอร์ทิศทางที่แท้จริงนั้นเป็นอย่างไร
Arjan Singh

11

f(x)f(x)

f(x)

f=FGπ(nωi)(nωo)
2π

สำหรับ NDF คุณต้องคำนวณฟังก์ชันการแจกแจงสะสมของ PDF เพื่อแปลงตำแหน่งตัวอย่างที่กระจายแบบสม่ำเสมอเป็นตำแหน่งตัวอย่างถ่วงน้ำหนัก PDF สำหรับ isotropic NDF จะทำให้ฟังก์ชั่น 1D นั้นง่ายขึ้นเนื่องจากความสมมาตรของฟังก์ชัน สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ CDF ที่มาคุณสามารถตรวจสอบบทความ GPU Gems เก่านี้ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.