중요도 샘플링이란 무엇입니까?


33

중요도 샘플링이란 무엇입니까? 내가 읽은 모든 기사에는 'PDF'가 언급되어 있습니까?

내가 수집 한 것으로부터 중요도 샘플링은 다른 것보다 중요한 반구의 영역 만 샘플링하는 기술입니다. 따라서 이상적으로는 노이즈를 줄이고 속도를 높이기 위해 광원을 향한 광선을 샘플링해야합니다. 또한, 방목 각도에있는 일부 BRDF는 계산에 거의 차이가 없으므로 중요도 샘플링을 사용하여이를 피하는 것이 좋습니다?

Cook-Torrance BRDF에 대한 중요도 샘플링을 구현하려면 어떻게해야합니까?


이것은 PDF가 무엇인지 설명하는 잘 읽은 링크입니다. TL; DR a PDF는 (연속 부동 소수점) 난수의 확률을 설명하는 함수입니다. 특정 PDF에서 난수를 생성하는 것은 어려울 수 있으며 그렇게하는 몇 가지 기술이 있습니다. 이것은 그들 중 하나에 대해 이야기합니다. 이 기사 이후의 기사는 다른 방법에 대해 이야기합니다. blog.demofox.org/2017/08/05/…
Alan Wolfe

답변:


51

짧은 답변:

중요도 샘플링 은 실제 함수의 모양에 가까운 추정기를 선택하여 Monte Carlo Integration의 분산을 줄이는 방법입니다.

PDF확률 밀도 함수 (Probability Density Function )의 약자입니다 . 인 랜덤 샘플 생성 확률 제공 .pdf(x)x

긴 답변 :

우선, Monte Carlo Integration의 정의와 수학적으로 어떤 것이 있는지 살펴 보겠습니다.

Monte Carlo Integration은 적분 값을 추정하는 기술입니다. 일반적으로 적분에 대한 닫힌 양식 솔루션이 없을 때 사용됩니다. 다음과 같이 보입니다 :

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 Solution이 정확하지 않다는 것을 알 수 있습니다. 추정치이기 때문입니다. 즉, 이 무한대에 가까울수록 추정값이 정답에 가까워지고 가까워 져야합니다. 이미 일부 런은 정답과 거의 동일합니다.NN=2000

PDF에 대한 참고 사항 :이 간단한 예에서는 항상 균일 한 임의 샘플을 가져옵니다. 균일 한 랜덤 샘플은 모든 샘플이 정확히 같은 확률로 선택 될 수 있음을 의미합니다. 범위에서 샘플링 하므로[0,2π]pdf(x)=1/(2π0)

중요도 샘플링은 균일 하지 않은 샘플링으로 작동합니다 . 대신 결과에 많은 기여를하는 더 많은 샘플 (중요)을 선택하고 결과에 약간만 기여하는 적은 샘플을 선택합니다 (중요하지 않음). 따라서 이름, 중요도 샘플링.

pdf가 의 모양과 매우 일치하는 샘플링 함수를 선택 하면 분산을 크게 줄일 수 있으므로 샘플 수를 줄일 수 있습니다. 그러나 값이 와 매우 다른 샘플링 함수를 선택 하면 분산을 증가시킬 수 있습니다 . 아래 그림을보십시오 : Wojciech Jarosz의 논문 부록 Aff좋은 샘플링과 나쁜 샘플링의 비교

경로 추적에서 중요도 샘플링의 한 예는 광선이 표면에 닿은 후 광선의 방향을 선택하는 방법입니다. 표면이 완벽하게 반사되지 않는 경우 (예 : 거울 또는 유리), 나가는 광선은 반구의 어느 곳에 나있을 수 있습니다.

나가는 광선은 반구의 어느 곳으로나 갈 수 있습니다

우리는 할 수 균일 새로운 광선을 생성하는 반구 샘플. 그러나 렌더링 방정식에 코사인 요소가 있다는 사실을 활용할 수 있습니다.

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

특히, 수평선의 광선은 크게 감쇠됩니다 (특히 ). 따라서 수평선 근처에서 생성 된 광선은 최종 값에 크게 기여하지 않습니다.cos(x)

이를 방지하기 위해 중요도 샘플링을 사용합니다. 코사인 가중치 반구에 따라 광선을 생성하면 수평선 위, 수평선 근처에서는 광선이 더 많이 생성됩니다. 이렇게하면 분산이 줄어들고 노이즈가 줄어 듭니다.

귀하의 경우, Cook-Torrance, 마이크로 패싯 기반 BRDF를 사용하도록 지정했습니다. 일반적인 형태는 다음과 같습니다.

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
  • 베크만
  • 블린

각 NDF에는 고유 한 공식이 있으므로 각각 다르게 샘플링해야합니다. 각각에 대한 최종 샘플링 기능 만 보여줄 것입니다. 공식이 어떻게 파생되는지 보려면 블로그 게시물을 참조하십시오.

GGX 는 다음과 같이 정의됩니다.

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

구형 좌표 각도 를 샘플링하기 위해 다음 공식을 사용할 수 있습니다.θ

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

여기서 는 균일 한 랜덤 변수입니다.ξ

NDF가 등방성이라고 가정하여 균일하게 샘플링 할 수 있습니다 .ϕ

ϕ=ξ2

Beckmann 은 다음과 같이 정의됩니다.

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

다음으로 샘플링 할 수 있습니다 :

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

마지막으로 Blinn 은 다음과 같이 정의됩니다.

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

다음으로 샘플링 할 수 있습니다 :

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

실용화

기본 역방향 경로 추적기를 살펴 보겠습니다.

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 값을 계산합니다. 그리고 우리는 Monte Carlo 공식에 따라 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를 얻을 수 있습니다.
RichieSams

GGX의 경우 어떻게 계산 / 샘플 wi합니까? 구형 좌표 각도 θ를 샘플링하는 방법을 이해하지만 실제 방향 벡터의 경우 어떻게 수행됩니까?
Arjan Singh

11

1D 함수 가 있고이 함수를 0에서 1까지 통합하려는 경우이 통합을 수행하는 한 가지 방법은 [0, 1] 범위의 N 개의 랜덤 샘플을 취하여 각각에 대해 를 평가 하는 것입니다 표본을 추출하고 표본의 평균을 계산합니다. 그러나이 "순진한"Monte Carlo 통합은 "천천히 수렴"한다고합니다. 즉, 특히 함수의 주파수가 높은 경우, 지상 사실에 가까워 지려면 많은 수의 샘플이 필요합니다.f(x)f(x)

중요도 샘플링을 사용하면 [0, 1] 범위에서 N 개의 랜덤 샘플을 얻는 대신 최종 결과에 가장 많이 기여하는 의 "중요한"영역에서 더 많은 샘플을 가져 옵니다. 그러나 함수의 중요한 영역을 향해 샘플링을 바이어스하기 때문에 이러한 샘플은 PDF (확률 밀도 함수)가 나타나는 바이어스에 대응하기 위해 가중치를 적게 두어야합니다. PDF는 주어진 위치에서 샘플의 확률을 알려주고 각 샘플을 각 샘플 위치에서 PDF 값으로 나누어 샘플의 가중 평균을 계산하는 데 사용됩니다.f(x)

Cook-Torrance 중요도 샘플링을 사용하면 일반적인 분포 함수 NDF를 기반으로 샘플을 분배하는 것이 일반적입니다. NDF가 이미 정규화 된 경우 PDF로 직접 사용할 수 있으며 이는 BRDF 평가에서 용어를 취소하므로 편리합니다. 당신이 다음해야 할 유일한 것은, 즉 NDF 기간없이 PDF를 기반으로 샘플의 위치를 배포하고 BRDF를 평가하는 그리고 적분 한 영역의 솔리드 각도 (예 : 반구의 경우 를 곱한 샘플 결과의 평균을 계산 합니다.

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

NDF 의 경우 균일하게 분포 된 샘플 위치를 PDF 가중 샘플 위치로 변환하려면 PDF의 누적 분포 함수 를 계산해야합니다 . 등방성 NDF의 경우 이는 기능의 대칭으로 인해 1D 기능으로 단순화됩니다. CDF 파생에 대한 자세한 내용은 이 오래된 GPU Gems 기사를 확인 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.