A Roleta Russa é realmente a resposta?


21

Vi que em algumas implementações do Path Tracing, uma abordagem chamada Roleta Russa é usada para selecionar alguns dos caminhos e compartilhar sua contribuição entre os outros caminhos.

Entendo que, em vez de seguir um caminho até que ele caia abaixo de um certo valor limite de contribuição e, em seguida, abandoná-lo, um limite diferente é usado e os caminhos cuja contribuição está abaixo desse limite são finalizados apenas com uma pequena probabilidade. Os outros caminhos têm sua contribuição aumentada em uma quantidade correspondente ao compartilhamento da energia perdida do caminho terminado. Não está claro para mim se isso é para corrigir um viés introduzido pela técnica ou se toda a técnica é necessária para evitar o viés.

  • A Roleta Russa oferece um resultado imparcial?
  • A Roleta Russa é necessária para obter um resultado imparcial?

Ou seja, o uso de um limiar minúsculo e o término de um caminho no momento em que ele cai abaixo desse limiar proporcionariam um resultado mais ou menos parcial?

Dado um número arbitrariamente grande de amostras, ambas as abordagens convergiriam para uma imagem resultante imparcial?

Estou procurando entender o motivo subjacente ao uso da abordagem da Roleta Russa. Existe uma diferença significativa em velocidade ou qualidade?


Entendo que a energia é redistribuída entre outros raios, a fim de preservar a energia total. No entanto, essa redistribuição ainda não poderia ser feita se o raio terminasse caindo abaixo de um limite fixo, em vez de ter uma vida útil determinada aleatoriamente após atingir esse limite?

Por outro lado, se a energia que seria perdida ao terminar um raio sem redistribuir sua energia é eventualmente perdida (como os raios aos quais é redistribuído também acabam), como isso melhora a situação?

Respostas:


26

Para entender a roleta russa, vejamos um rastreador de caminho muito básico para trás:

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. pulamos pela cena, acumulando cores e atenuação de luz à medida que avançamos. Para ser completamente matematicamente imparcial, os saltos devem ir para o infinito. Mas isso é irreal e, como você observou, não é visualmente necessário; para a maioria das cenas, após um certo número de rejeições, digamos 10, a quantidade de contribuição para a cor final é muito, muito mínima.

Portanto, para economizar recursos de computação, muitos rastreadores de caminho têm um limite rígido para o número de rejeições. Isso adiciona viés.

Dito isto, é difícil escolher qual deve ser esse limite. Algumas cenas ficam ótimas após 2 saltos; outros (digamos, com transmissão ou SSS) podem levar até 10 ou 20. 2 Rejeições do Grande Herói da Disney 6 9 Rejeições do Grande Herói da Disney 6

Se escolhermos muito baixo, a imagem será visivelmente distorcida. Mas se escolhermos muito alto, estamos desperdiçando energia e tempo de computação.

Uma maneira de resolver isso, como você observou, é finalizar o caminho depois de atingirmos algum limiar de atenuação. Isso também adiciona viés.

A fixação de um limite funcionará , mas novamente, como escolhemos o limite? Se escolhermos muito grande, a imagem será visivelmente tendenciosa, muito pequena e estamos desperdiçando recursos.

A Roleta Russa tenta resolver esses problemas de maneira imparcial. Primeiro, aqui está o código:

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;


        // Russian Roulette
        // Randomly terminate a path with a probability inversely equal to the throughput
        float p = std::max(throughput.x, std::max(throughput.y, throughput.z));
        if (sampler->NextFloat() > p) {
            break;
        }

        // Add the energy we 'lose' by randomly terminating paths
        throughput *= 1 / p;


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

A Roleta Russa termina aleatoriamente um caminho com uma probabilidade inversamente igual à taxa de transferência. Portanto, caminhos com baixo rendimento que não contribuem muito para a cena têm mais probabilidade de serem finalizados.

Se pararmos por aí, ainda somos tendenciosos. Nós "perdemos" a energia do caminho que terminamos aleatoriamente. Para torná-lo imparcial, aumentamos a energia dos caminhos não terminados pela probabilidade de serem terminados. Isso, além de aleatório, torna a Roleta Russa imparcial.

Para responder às suas últimas perguntas:

  1. A Roleta Russa oferece um resultado imparcial?
    • sim
  2. A Roleta Russa é necessária para obter um resultado imparcial?
    • Depende do que você quer dizer com imparcial. Se você quer dizer matematicamente, então sim. No entanto, se você quer dizer visualmente, então não. Você só precisa escolher a profundidade máxima do caminho e o limite de corte com muito cuidado. Isso pode ser muito entediante, pois pode mudar de cena para cena.
  3. Você pode usar uma probabilidade fixa (corte) e depois redistribuir a energia 'perdida'. Isso é imparcial?
    • Se você usa uma probabilidade fixa, está adicionando viés. Ao redistribuir a energia 'perdida', você reduz o viés, mas ainda é matematicamente tendencioso. Para ser completamente imparcial, deve ser aleatório.
  4. Se a energia que seria perdida ao terminar um raio sem redistribuí-lo é eventualmente perdida (como os raios aos quais é redistribuído também acabam), como isso melhora a situação?
    • A Roleta Russa só para de saltar. Não remove a amostra completamente. Além disso, a energia 'perdida' é contabilizada nos saltos até o término. Portanto, a única maneira de a energia ser "eventualmente perdida de qualquer maneira" seria ter um quarto completamente preto.

No final, a Roleta Russa é um algoritmo muito simples que utiliza uma quantidade muito pequena de recursos computacionais extras. Em troca, ele pode economizar uma grande quantidade de recursos computacionais. Portanto, não vejo realmente um motivo para não usá-lo.


Sinceramente, não tenho muita certeza to be completely unbiased it must be random. Acho que você ainda pode obter resultados matemáticos, usando pesos fracionados de amostras, em vez da passagem / queda binária que a roleta russa impõe, é só que a roleta convergirá mais rapidamente porque está operando uma amostragem de importância perfeita.
v.oddou

9

A técnica da roleta russa em si é uma maneira de terminar caminhos sem introduzir viés sistêmico. O princípio é bastante direto: se em um vértice específico você tiver 10% de chance de substituir arbitrariamente a energia por 0, e se fizer isso um número infinito de vezes, verá 10% menos energia. O aumento de energia compensa isso. Se você não compensasse a energia perdida devido ao término do caminho, a roleta russa seria distorcida, mas toda a técnica é um método útil para evitar distorções.

Se eu fosse um adversário procurando provar que a técnica "encerra caminhos cuja contribuição é menor que um pequeno valor fixo" é tendenciosa, eu construí uma cena com luzes tão escuras que os caminhos contribuintes são sempre inferiores a esse valor. Talvez eu esteja simulando uma câmera com pouca luz.

Mas é claro que você sempre pode expor o valor fixo como um parâmetro ajustável para o usuário, para que ele possa cair ainda mais se a cena estiver com pouca luz. Então, vamos desconsiderar esse exemplo por um minuto.

O que acontece se eu considerar um objeto iluminado por muitos caminhos de energia muito baixa coletados por um refletor parabólico ? Os caminhos de baixa energia não necessariamente oscilam indiscriminadamente de uma maneira que você pode negligenciar completamente. Da mesma forma, o raciocínio se aplica a, por exemplo, cortar caminhos após um número fixo de saltos: você pode construir uma cena com um caminho que salta uma série de 20 espelhos antes de atingir um objeto.

Outra maneira de ver: se você define a contribuição de um caminho para 0 depois que ele cai abaixo de algum epsilon fixo, como você corrige essa perda de energia? Você não está simplesmente reduzindo a energia total em alguma fração. Você não sabe nada sobre quanta energia está negligenciando, porque está cortando em algum limiar de contribuição antes de conhecer o outro fator: a energia incidente.


8

Apenas para expandir algumas das outras respostas, a prova de que a Roleta Russa não fornece um resultado tendencioso é muito simples.

Suponha que você tenha alguma variável aleatória que é a soma de vários termos:F

F=F1++FN

Substitua cada termo por:

Fi={1piFiwith probability pi0otherwise

Então:

E[Fi]=pi×1piE[Fi]+(1pi)×0=E[Fi]

Observe que não importa quais probabilidades você escolhe para . O valor esperado dos termos e, portanto, o valor esperado de , é o mesmo.piF

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.