การติดตามเส้นทางแบบก้าวหน้าด้วยการสุ่มตัวอย่างแสงอย่างชัดเจน


14

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

คำตอบ:


19

มีหลายพื้นที่ในการติดตามเส้นทางที่สามารถสุ่มตัวอย่างความสำคัญได้ นอกจากนี้ในแต่ละพื้นที่ดังกล่าวยังสามารถใช้การสุ่มตัวอย่างสำคัญหลายเสนอครั้งแรกใน Veach และ Guibas ของกระดาษ 1995 เพื่อที่จะอธิบายได้ดีขึ้นมาดูที่เส้นทางย้อนหลัง:

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);
    SurfaceInteraction interaction;

    // Bounce the ray around the scene
    const uint maxBounces = 15;
    for (uint bounces = 0; bounces < maxBounces; ++bounces) {
        m_scene->Intersect(ray);

        // The ray missed. Return the background color
        if (ray.GeomID == INVALID_GEOMETRY_ID) {
            color += throughput * m_scene->BackgroundColor;
            break;
        }

        // 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 emission
        if (light != nullptr) {
            color += throughput * light->Le();
        }

        interaction.Position = ray.Origin + ray.Direction * ray.TFar;
        interaction.Normal = normalize(m_scene->InterpolateNormal(ray.GeomID, ray.PrimID, ray.U, ray.V));
        interaction.OutputDirection = normalize(-ray.Direction);


        // Get the new ray direction
        // Choose the direction based on the bsdf        
        material->bsdf->Sample(interaction, sampler);
        float pdf = material->bsdf->Pdf(interaction);

        // Accumulate the weight
        throughput = throughput * material->bsdf->Eval(interaction) / pdf;

        // Shoot a new ray

        // Set the origin at the intersection point
        ray.Origin = interaction.Position;

        // Reset the other ray properties
        ray.Direction = interaction.InputDirection;
        ray.TNear = 0.001f;
        ray.TFar = infinity;


        // Russian Roulette
        if (bounces > 3) {
            float p = std::max(throughput.x, std::max(throughput.y, throughput.z));
            if (sampler->NextFloat() > p) {
                break;
            }

            throughput *= 1 / p;
        }
    }

    m_scene->Camera->FrameBufferData.SplatPixel(x, y, color);
}

เป็นภาษาอังกฤษ:

  1. ยิงรังสีผ่านฉาก
  2. ตรวจสอบว่าเราตีอะไร ถ้าไม่เราคืนสีสกายบ็อกซ์แล้วก็หัก
  3. ตรวจสอบว่าเราโดนแสงหรือไม่ ถ้าเป็นเช่นนั้นเราเพิ่มการปล่อยแสงเพื่อการสะสมสีของเรา
  4. เลือกทิศทางใหม่สำหรับรังสีต่อไป เราสามารถทำสิ่งนี้ได้อย่างสม่ำเสมอหรือมีความสำคัญตามตัวอย่างของ BRDF
  5. ประเมิน BRDF และสะสม ที่นี่เราต้องหารด้วย pdf ของทิศทางที่เราเลือกเพื่อทำตามอัลกอริทึม Monte Carlo
  6. สร้างรังสีใหม่ตามทิศทางที่เราเลือกและตำแหน่งที่เราเพิ่งมา
  7. [ไม่บังคับ] ใช้รูเล็ตรัสเซียเพื่อเลือกว่าเราควรยุติการฉายรังสี
  8. ไปที่ 1

ด้วยรหัสนี้เราจะได้สีถ้าในที่สุดแสงก็กระทบกับแสง นอกจากนี้ยังไม่รองรับแหล่งกำเนิดแสงที่ตรงต่อเวลาเนื่องจากไม่มีพื้นที่

ในการแก้ไขปัญหานี้เราจะสุ่มตัวอย่างแสงโดยตรงทุกการเด้ง เราต้องทำการเปลี่ยนแปลงเล็กน้อย:

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);
    SurfaceInteraction interaction;

    // Bounce the ray around the scene
    const uint maxBounces = 15;
    for (uint bounces = 0; bounces < maxBounces; ++bounces) {
        m_scene->Intersect(ray);

        // The ray missed. Return the background color
        if (ray.GeomID == INVALID_GEOMETRY_ID) {
            color += throughput * m_scene->BackgroundColor;
            break;
        }

        // 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 this is the first bounce or if we just had a specular bounce,
        // we need to add the emmisive light
        if ((bounces == 0 || (interaction.SampledLobe & BSDFLobe::Specular) != 0) && light != nullptr) {
            color += throughput * light->Le();
        }

        interaction.Position = ray.Origin + ray.Direction * ray.TFar;
        interaction.Normal = normalize(m_scene->InterpolateNormal(ray.GeomID, ray.PrimID, ray.U, ray.V));
        interaction.OutputDirection = normalize(-ray.Direction);


        // Calculate the direct lighting
        color += throughput * SampleLights(sampler, interaction, material->bsdf, light);


        // Get the new ray direction
        // Choose the direction based on the bsdf        
        material->bsdf->Sample(interaction, sampler);
        float pdf = material->bsdf->Pdf(interaction);

        // Accumulate the weight
        throughput = throughput * material->bsdf->Eval(interaction) / pdf;

        // Shoot a new ray

        // Set the origin at the intersection point
        ray.Origin = interaction.Position;

        // Reset the other ray properties
        ray.Direction = interaction.InputDirection;
        ray.TNear = 0.001f;
        ray.TFar = infinity;


        // Russian Roulette
        if (bounces > 3) {
            float p = std::max(throughput.x, std::max(throughput.y, throughput.z));
            if (sampler->NextFloat() > p) {
                break;
            }

            throughput *= 1 / p;
        }
    }

    m_scene->Camera->FrameBufferData.SplatPixel(x, y, color);
}

ก่อนอื่นเราเพิ่ม "color + = throughput * SampleLights (... )" ฉันจะพูดถึงรายละเอียดเกี่ยวกับ SampleLights () นิดหน่อย แต่โดยพื้นฐานแล้วมันจะวนลูปผ่านแสงทั้งหมดและคืนการมีส่วนร่วมของพวกเขาให้กับสีที่ได้รับการลดทอนโดย BSDF

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

อย่างไรก็ตามมีสองกรณีมุม:

  1. รังสีแรก
  2. การตีกลับที่สมบูรณ์แบบ (กระจกเงา aka)

หากรังสีแรกกระทบกับแสงคุณควรเห็นการเปล่งแสงโดยตรง ดังนั้นหากเราข้ามไปไฟทั้งหมดจะปรากฏเป็นสีดำแม้ว่าพื้นผิวรอบ ๆ พวกเขาจะติด

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

ตอนนี้เรามาเจาะ SampleLights ():

float3 SampleLights(UniformSampler *sampler, SurfaceInteraction interaction, BSDF *bsdf, Light *hitLight) const {
    std::size_t numLights = m_scene->NumLights();

    float3 L(0.0f);
    for (uint i = 0; i < numLights; ++i) {
        Light *light = &m_scene->Lights[i];

        // Don't let a light contribute light to itself
        if (light == hitLight) {
            continue;
        }

        L = L + EstimateDirect(light, sampler, interaction, bsdf);
    }

    return L;
}

เป็นภาษาอังกฤษ:

  1. วนผ่านแสงทั้งหมด
  2. ข้ามแสงถ้าเรากดมัน
    • อย่าจุ่มสองครั้ง
  3. สะสมแสงโดยตรงจากแสงทั้งหมด
  4. คืนแสงโดยตรง

ในที่สุด EstimateDirect () กำลังประเมินBSDF(p,ωi,ωo)Li(p,ωi)

สำหรับแหล่งกำเนิดแสงที่ตรงต่อเวลาสิ่งนี้ง่ายเหมือน:

float3 EstimateDirect(Light *light, UniformSampler *sampler, SurfaceInteraction &interaction, BSDF *bsdf) const {
    // Only sample if the BRDF is non-specular 
    if ((bsdf->SupportedLobes & ~BSDFLobe::Specular) != 0) {
        return float3(0.0f);
    }

    interaction.InputDirection = normalize(light->Origin - interaction.Position);
    return bsdf->Eval(interaction) * light->Li;
}

อย่างไรก็ตามหากเราต้องการให้แสงมีพื้นที่เราต้องสุ่มตัวอย่างจุดบนแสงก่อน ดังนั้นคำจำกัดความเต็มคือ:

float3 EstimateDirect(Light *light, UniformSampler *sampler, SurfaceInteraction &interaction, BSDF *bsdf) const {
    float3 directLighting = float3(0.0f);

    // Only sample if the BRDF is non-specular 
    if ((bsdf->SupportedLobes & ~BSDFLobe::Specular) != 0) {
        float pdf;
        float3 Li = light->SampleLi(sampler, m_scene, interaction, &pdf);

        // Make sure the pdf isn't zero and the radiance isn't black
        if (pdf != 0.0f && !all(Li)) {
            directLighting += bsdf->Eval(interaction) * Li / pdf;
        }
    }

    return directLighting;
}

เราสามารถติดตั้ง light-> SampleLi ได้ตามต้องการ เราสามารถเลือกจุดอย่างสม่ำเสมอหรือตัวอย่างที่สำคัญ ไม่ว่าในกรณีใดเราแบ่งความเป็นวิทยุด้วย pdf ของการเลือกจุด อีกครั้งเพื่อตอบสนองความต้องการของ Monte Carlo

หาก BRDF นั้นขึ้นอยู่กับมุมมองที่สูงอาจเป็นการดีกว่าที่จะเลือกจุดที่ขึ้นกับ BRDF แทนที่จะเป็นจุดสุ่มบนแสง แต่เราจะเลือกได้อย่างไร ตัวอย่างขึ้นอยู่กับแสงหรือตาม BRDF?

ทำไมไม่ทั้งสองล่ะ ป้อนการสุ่มตัวอย่างสำคัญหลายรายการ ในระยะสั้นเราประเมินหลาย ๆ ครั้งโดยใช้เทคนิคการสุ่มตัวอย่างที่แตกต่างกันแล้วทำการเฉลี่ยด้วยกันโดยใช้น้ำหนักตาม PDF ในรหัสนี่คือ:BSDF(p,ωi,ωo)Li(p,ωi)

float3 EstimateDirect(Light *light, UniformSampler *sampler, SurfaceInteraction &interaction, BSDF *bsdf) const {
    float3 directLighting = float3(0.0f);
    float3 f;
    float lightPdf, scatteringPdf;


    // Sample lighting with multiple importance sampling
    // Only sample if the BRDF is non-specular 
    if ((bsdf->SupportedLobes & ~BSDFLobe::Specular) != 0) {
        float3 Li = light->SampleLi(sampler, m_scene, interaction, &lightPdf);

        // Make sure the pdf isn't zero and the radiance isn't black
        if (lightPdf != 0.0f && !all(Li)) {
            // Calculate the brdf value
            f = bsdf->Eval(interaction);
            scatteringPdf = bsdf->Pdf(interaction);

            if (scatteringPdf != 0.0f && !all(f)) {
                float weight = PowerHeuristic(1, lightPdf, 1, scatteringPdf);
                directLighting += f * Li * weight / lightPdf;
            }
        }
    }


    // Sample brdf with multiple importance sampling
    bsdf->Sample(interaction, sampler);
    f = bsdf->Eval(interaction);
    scatteringPdf = bsdf->Pdf(interaction);
    if (scatteringPdf != 0.0f && !all(f)) {
        lightPdf = light->PdfLi(m_scene, interaction);
        if (lightPdf == 0.0f) {
            // We didn't hit anything, so ignore the brdf sample
            return directLighting;
        }

        float weight = PowerHeuristic(1, scatteringPdf, 1, lightPdf);
        float3 Li = light->Le();
        directLighting += f * Li * weight / scatteringPdf;
    }

    return directLighting;
}

เป็นภาษาอังกฤษ:

  1. อันดับแรกเราสุ่มตัวอย่างแสง
    • การอัปเดตนี้จะช่วยโต้ตอบ InterputDirection
    • ให้เราลี่เป็นผู้ให้แสงสว่าง
    • และ pdf ของการเลือกจุดนั้นบนแสง
  2. ตรวจสอบว่าไฟล์ pdf นั้นถูกต้องและรัศมีไม่เป็นศูนย์
  3. ประเมิน BSDF โดยใช้ InputDirection ตัวอย่าง
  4. คำนวณ pdf สำหรับ BSDF ที่กำหนด InputDirection ตัวอย่าง
    • โดยพื้นฐานแล้วตัวอย่างนี้มีความเป็นไปได้มากน้อยเพียงใดถ้าเราใช้ตัวอย่าง BSDF แทนแสง
  5. คำนวณน้ำหนักโดยใช้ pdf แบบเบาและแบบ BSDF
    • Veach และ Guibas กำหนดวิธีที่ต่างกันในการคำนวณน้ำหนัก จากการทดลองพวกเขาค้นพบพลังฮิวริสติกที่มีกำลัง 2 เพื่อให้ได้ผลดีที่สุดสำหรับกรณีส่วนใหญ่ ฉันแนะนำคุณไปที่กระดาษเพื่อดูรายละเอียดเพิ่มเติม การดำเนินการอยู่ด้านล่าง
  6. คูณน้ำหนักด้วยการคำนวณแสงโดยตรงและหารด้วย pdf แบบแสง (สำหรับ Monte Carlo) และเพิ่มการสะสมแสงโดยตรง
  7. จากนั้นเราสุ่มตัวอย่าง BRDF
    • การอัปเดตนี้จะช่วยโต้ตอบ InterputDirection
  8. ประเมิน BRDF
  9. รับ pdf สำหรับการเลือกทิศทางนี้ตาม BRDF
  10. คำนวณแสงแบบ pdf กำหนดให้ InputDirection ตัวอย่าง
    • นี่คือกระจกแห่งก่อน ทิศทางนี้เป็นไปได้มากน้อยเพียงใดถ้าเราจะลองแสง
  11. หาก lightPdf == 0.0f แสดงว่ารังสีพลาดแสงดังนั้นเพียงแค่คืนแสงโดยตรงจากตัวอย่างแสง
  12. มิฉะนั้นคำนวณน้ำหนักและเพิ่มแสงโดยตรงของ BSDF เพื่อการสะสม
  13. ในที่สุดก็คืนแสงสะสมโดยตรง

.

inline float PowerHeuristic(uint numf, float fPdf, uint numg, float gPdf) {
    float f = numf * fPdf;
    float g = numg * gPdf;

    return (f * f) / (f * f + g * g);
}

มีการปรับให้เหมาะสม / การปรับปรุงจำนวนมากที่คุณสามารถทำได้ในฟังก์ชั่นเหล่านี้ แต่ฉันได้ตัดมันลงเพื่อพยายามทำให้เข้าใจง่ายขึ้น หากคุณต้องการฉันสามารถแบ่งปันการปรับปรุงเหล่านี้บางอย่าง

Sampling One Light เท่านั้น

ใน SampleLights () เราวนแสงทั้งหมดและรับความช่วยเหลือ สำหรับไฟจำนวนเล็กน้อยนี่เป็นเรื่องปกติ แต่สำหรับหลายร้อยหรือหลายพันไฟนี่จะมีราคาแพง โชคดีที่เราสามารถใช้ประโยชน์จากความจริงที่ว่า Monte Carlo Integration เป็นค่าเฉลี่ยขนาดใหญ่ ตัวอย่าง:

มานิยามกัน

h(x)=f(x)+g(x)

ขณะนี้เรากำลังประมาณโดย:h(x)

h(x)=1Ni=1Nf(xi)+g(xi)

แต่การคำนวณทั้งและนั้นมีราคาแพงดังนั้นเราจึง:f(x)g(x)

h(x)=1Ni=1Nr(ζ,x)pdf

โดยที่เป็นตัวแปรสุ่มแบบสม่ำเสมอและถูกกำหนดเป็น:ζr(ζ,x)

r(ζ,x)={f(x),0.0ζ<0.5g(x),0.5ζ<1.0

ในกรณีนี้เนื่องจาก pdf ต้องรวมกับ 1 และมี 2 ฟังก์ชันให้เลือกpdf=12

เป็นภาษาอังกฤษ:

  1. สุ่มเลือกหรือเพื่อประเมินg ( x )f(x)g(x)
  2. หารผลลัพธ์ด้วย (เนื่องจากมีสองรายการ)12
  3. เฉลี่ย

เมื่อ N มีขนาดใหญ่การประมาณจะมาบรรจบกับวิธีการแก้ไขที่ถูกต้อง

เราสามารถใช้หลักการเดียวกันนี้กับการสุ่มตัวอย่างแบบอ่อน แทนที่จะสุ่มทุกแสงเราสุ่มเลือกหนึ่งตัวและคูณผลลัพธ์ด้วยจำนวนแสง (ซึ่งเหมือนกับการหารด้วย pdf เศษส่วน):

float3 SampleOneLight(UniformSampler *sampler, SurfaceInteraction interaction, BSDF *bsdf, Light *hitLight) const {
    std::size_t numLights = m_scene->NumLights();

    // Return black if there are no lights
    // And don't let a light contribute light to itself
    // Aka, if we hit a light
    // This is the special case where there is only 1 light
    if (numLights == 0 || numLights == 1 && hitLight != nullptr) {
        return float3(0.0f);
    }

    // Don't let a light contribute light to itself
    // Choose another one
    Light *light;
    do {
        light = m_scene->RandomOneLight(sampler);
    } while (light == hitLight);

    return numLights * EstimateDirect(light, sampler, interaction, bsdf);
}

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

หลายความสำคัญสุ่มตัวอย่างทิศทาง "เรย์ใหม่"

รหัสปัจจุบันมีความสำคัญเพียงตัวอย่างทิศทาง "นิวเรย์" ตาม BSDF ถ้าเราต้องการตัวอย่างที่สำคัญเช่นกันตามตำแหน่งของแสง

จากสิ่งที่เราเรียนรู้ข้างต้นวิธีหนึ่งคือยิงแสงและน้ำหนัก "ใหม่" สองอันโดยยึดตามไฟล์ PDF อย่างไรก็ตามนี่เป็นทั้งการคำนวณที่มีราคาแพงและยากที่จะดำเนินการโดยไม่ต้องเรียกซ้ำ

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

// Get the new ray direction

// Randomly (uniform) choose whether to sample based on the BSDF or the Lights
float p = sampler->NextFloat();

Light *light = m_scene->RandomLight();

if (p < 0.5f) {
    // Choose the direction based on the bsdf 
    material->bsdf->Sample(interaction, sampler);
    float bsdfPdf = material->bsdf->Pdf(interaction);

    float lightPdf = light->PdfLi(m_scene, interaction);
    float weight = PowerHeuristic(1, bsdfPdf, 1, lightPdf);

    // Accumulate the throughput
    throughput = throughput * weight * material->bsdf->Eval(interaction) / bsdfPdf;

} else {
    // Choose the direction based on a light
    float lightPdf;
    light->SampleLi(sampler, m_scene, interaction, &lightPdf);

    float bsdfPdf = material->bsdf->Pdf(interaction);
    float weight = PowerHeuristic(1, lightPdf, 1, bsdfPdf);

    // Accumulate the throughput
    throughput = throughput * weight * material->bsdf->Eval(interaction) / lightPdf;
}

อย่างที่ทุกคนพูดว่าเราต้องการให้ความสำคัญกับตัวอย่างทิศทาง "เรย์ใหม่" ตามแสงไหม? สำหรับการให้แสงโดยตรงความเรดิโอได้รับผลกระทบจากทั้ง BSDF ของพื้นผิวและทิศทางของแสง แต่สำหรับแสงทางอ้อมวิทยุนั้นเกือบจะถูกกำหนดโดย BSDF ของพื้นผิวที่กระทบมาก่อน ดังนั้นการเพิ่มการสุ่มตัวอย่างที่มีความสำคัญน้อยไม่ได้ให้อะไรเราเลย

ดังนั้นจึงเป็นเรื่องธรรมดาที่จะให้ความสำคัญเฉพาะตัวอย่าง "ทิศทางใหม่" กับ BSDF แต่ใช้การสุ่มตัวอย่างความสำคัญหลายครั้งกับแสงโดยตรง


ขอบคุณสำหรับคำตอบที่ชัดเจน! ฉันเข้าใจว่าหากเราต้องใช้ตัวติดตามเส้นทางโดยไม่มีการสุ่มตัวอย่างแสงอย่างชัดเจนเราจะไม่ชนกับแหล่งกำเนิดแสงจุด ดังนั้นโดยพื้นฐานแล้วเราสามารถเพิ่มการสนับสนุนได้ ในทางกลับกันถ้าเราสุ่มตัวอย่างแหล่งกำเนิดแสงในพื้นที่เราต้องตรวจสอบให้แน่ใจว่าเราไม่ควรชนกับแสงทางอ้อมอีกครั้งเพื่อหลีกเลี่ยงการจุ่มสองครั้ง
Mustafa Işık

แน่นอน! มีส่วนใดบ้างที่คุณต้องการให้ชี้แจง หรือมีรายละเอียดไม่เพียงพอ
RichieSams

นอกจากนี้การสุ่มตัวอย่างที่สำคัญหลายครั้งใช้สำหรับการคำนวณแสงโดยตรงหรือไม่ บางทีฉันอาจพลาด แต่ฉันไม่เห็นตัวอย่างอีก ถ้าฉันยิงรังสีลำแสงเดียวต่อการสะท้อนกลับในเส้นทางของฉันดูเหมือนว่าฉันไม่สามารถทำได้สำหรับการคำนวณแสงทางอ้อม
Mustafa Işık

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

1
ดูเหมือนว่าเราแยกแหล่งที่มาของความกระจ่างใสออกเป็นสองส่วนเป็นทางตรงและทางอ้อม เราสุ่มตัวอย่างแสงอย่างชัดเจนสำหรับชิ้นส่วนโดยตรงและในขณะที่สุ่มตัวอย่างชิ้นส่วนนี้มันมีเหตุผลที่จะให้ความสำคัญกับตัวอย่างของแสงเช่นเดียวกับ BSDF อย่างไรก็ตามในส่วนทางอ้อมเราไม่มีความคิดเกี่ยวกับทิศทางที่อาจให้ค่าความกระจ่างใสที่สูงขึ้นเนื่องจากเป็นปัญหาที่เราต้องการแก้ไข อย่างไรก็ตามเราสามารถพูดได้ว่าทิศทางใดที่สามารถมีส่วนร่วมมากขึ้นตามเงื่อนไขโคไซน์และ BSDF นี่คือสิ่งที่ฉันเข้าใจ แก้ไขฉันถ้าฉันผิดและขอบคุณสำหรับคำตอบที่ยอดเยี่ยมของคุณ
Mustafa Işık
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.