BRDF e coordenada esférica no traçado de raios

9

Desenvolvi um traçador de raios que usa o modelo padrão de iluminação phong / blinn phong. Agora estou modificando-o para oferecer suporte à renderização com base física, por isso estou implementando vários modelos de BRDF. No momento, estou focado no modelo de Oren-Nayar e Torrance-Sparrow. Cada uma delas é baseada em coordenadas esféricas usadas para expressar o incidente com o w e a saída com a direção da luz.

Minha pergunta é: qual o caminho certo para converter wi e wo de coordenada cartesiana em coordenada esférica?

Estou aplicando a fórmula padrão relatada aqui https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions, mas não tenho certeza se estou fazendo a coisa certa, porque meu vetor não está com cauda na origem do sistema de coordenadas cartesianas, mas estão centralizadas no ponto de interseção do raio com o objeto.

Aqui você pode encontrar minha implementação atual:

Alguém pode me ajudar a dar uma explicação da maneira correta de converter o vetor wi e wo da coordenada cartesiana em esférica?

ATUALIZAR

Copio aqui a parte relevante do código:

cálculo de coordenadas esféricas

float Vector3D::sphericalTheta() const {

    float sphericalTheta = acosf(Utils::clamp(y, -1.f, 1.f));

    return sphericalTheta;
}

float Vector3D::sphericalPhi() const {

    float phi = atan2f(z, x);

    return (phi < 0.f) ? phi + 2.f * M_PI : phi;
}

Oren Nayar

OrenNayar::OrenNayar(Spectrum<constant::spectrumSamples> reflectanceSpectrum, float degree) : reflectanceSpectrum{reflectanceSpectrum} {

    float sigma = Utils::degreeToRadian(degree);
    float sigmaPowerTwo = sigma * sigma;

    A = 1.0f - (sigmaPowerTwo / 2.0f * (sigmaPowerTwo + 0.33f));
    B = 0.45f * sigmaPowerTwo / (sigmaPowerTwo + 0.09f);
};

Spectrum<constant::spectrumSamples> OrenNayar::f(const Vector3D& wi, const Vector3D& wo, const Intersection* intersection) const {

    float thetaI = wi.sphericalTheta();
    float phiI = wi.sphericalPhi();

    float thetaO = wo.sphericalTheta();
    float phiO = wo.sphericalPhi();

    float alpha = std::fmaxf(thetaI, thetaO);
    float beta = std::fminf(thetaI, thetaO);

    Spectrum<constant::spectrumSamples> orenNayar = reflectanceSpectrum * constant::inversePi * (A + B * std::fmaxf(0, cosf(phiI - phiO) * sinf(alpha) * tanf(beta)));

    return orenNayar;
}

Torrance-Sparrow

float TorranceSparrow::G(const Vector3D& wi, const Vector3D& wo, const Vector3D& wh, const Intersection* intersection) const {

    Vector3D normal = intersection->normal;
    normal.normalize();

    float normalDotWh = fabsf(normal.dot(wh));
    float normalDotWo = fabsf(normal.dot(wo));
    float normalDotWi = fabsf(normal.dot(wi));
    float woDotWh = fabsf(wo.dot(wh));

    float G = fminf(1.0f, std::fminf((2.0f * normalDotWh * normalDotWo)/woDotWh, (2.0f * normalDotWh * normalDotWi)/woDotWh));

    return G;
}

float TorranceSparrow::D(const Vector3D& wh, const Intersection* intersection) const {

    Vector3D normal = intersection->normal;
    normal.normalize();

    float cosThetaH = fabsf(wh.dot(normal));

    float Dd = (exponent + 2) * constant::inverseTwoPi * powf(cosThetaH, exponent);

    return Dd;
}

Spectrum<constant::spectrumSamples> TorranceSparrow::f(const Vector3D& wi, const Vector3D& wo, const Intersection* intersection) const {

    Vector3D normal = intersection->normal;
    normal.normalize();

    float thetaI = wi.sphericalTheta();
    float thetaO = wo.sphericalTheta();

    float cosThetaO = fabsf(cosf(thetaO));
    float cosThetaI = fabsf(cosf(thetaI));

    if(cosThetaI == 0 || cosThetaO == 0) {

        return reflectanceSpectrum * 0.0f;
    }

    Vector3D wh = (wi + wo);
    wh.normalize();

    float cosThetaH = wi.dot(wh);

    float F = Fresnel::dieletricFresnel(cosThetaH, refractiveIndex);
    float g = G(wi, wo, wh, intersection);
    float d = D(wh, intersection);

    printf("f %f g %f d %f \n", F, g, d);
    printf("result %f \n", ((d * g * F) / (4.0f * cosThetaI * cosThetaO)));

    Spectrum<constant::spectrumSamples> torranceSparrow = reflectanceSpectrum * ((d * g * F) / (4.0f * cosThetaI * cosThetaO));

    return torranceSparrow;
}

ATUALIZAÇÃO 2

Após algumas pesquisas, encontrei esta implementação do Oren-Nayar BRDF .

Na implementação acima, theta para wi e wo é obtido simplesmente fazendo arccos (wo.dotProduct (Normal)) e arccos (wi.dotProduct (Normal)). Isso me parece razoável, pois podemos usar o normal do ponto de interseção como a direção do zênite para o nosso sistema de coordenadas esféricas e fazer o cálculo. O cálculo de gama = cos (phi_wi - phi_wo) faz algum tipo de projeção de wi e wo no que chama de "espaço tangente". Supondo que tudo esteja correto nesta implementação, posso apenas usar as fórmulas | View - Normal x (View.dotProduct (Normal)) | e | Luz - Normal x (Light.dotProduct (Normal)) | obter a coordenada phi (em vez de usar arctan ("alguma coisa"))?

Fabrizio Duroni
fonte
Alguém poderia me ajudar?
Fabrizio Duroni 30/11/2015
Você pode mostrar o snippet de código exato, não o repositório inteiro?
precisa
Parece que esta é uma das perguntas mais misteriosas sobre o traçado de raios de todos os tempos: D
Fabrizio Duroni
Convido você a perguntar aqui computergraphics.stackexchange.com
concept3d
Feito @ concept3d. Você pode encontrá-lo aqui computergraphics.stackexchange.com/questions/1799/…
Fabrizio Duroni

Respostas:

2

Na verdade, é melhor não usar coordenadas esféricas (ou quaisquer ângulos) para implementar os BRDFs, mas trabalhar diretamente no sistema de coordenadas cartesianas e usar cosseno do ângulo entre os vetores, que é um produto de ponto simples entre os vetores unitários, como você conhece. Isso é mais robusto e eficiente.

Para Oren-Nayar, você pode pensar em usar ângulos (devido a mín / máx), mas pode simplesmente implementar o BRDF diretamente no espaço cartesiano: https://fgiesen.wordpress.com/2010/10/21 / termine suas derivações-por favor

Para os BRDFs de microfacetes Torrance-Sparrow ou Cook-Torrance, você também não precisa usar coordenadas esféricas. Nesses BRDFs, o ângulo é passado para uma função trigonométrica (geralmente cosseno) em termos de D / F / G e o denominador BRDF, para que você possa usar identidades retas ou trigonométricas de produtos pontuais sem passar por coordenadas esféricas.

JarkkoL
fonte
1

Você pode especificar um sistema de coordenadas considerando o N normal e outro vetor. Nós vamos escolher wi. Portanto, qualquer vetor que tenha a mesma direção que wi quando projetado no plano tangente terá um azimute de 0

Primeiro, projetamos wi no plano tangente: (supondo que wi já esteja normalizado)

wit = normalize(wi - N * dot(wi, N))

agora, podemos fazer o mesmo com wo:

wot = normalize(wo - N * dot(wo, N))

Agora, wit e wot estão ambos em um plano ortogonal a N e tangente ao ponto de interseção.

Agora podemos calcular o ângulo entre os dois:

azimuth = arcos ( dot(wit, wot) )

Qual é realmente o azimute do wot em relação à sagacidade quando projetada no plano tangente.

Gato
fonte
0

Se você conhece o ponto de interseção e o ponto de origem, não seria apenas uma questão de subtrair um do outro para obter o resultado como se fosse da origem?

Se você não acredita no resultado e deseja chegar lá por um longo caminho, também é possível obter a transformação de rotação de um ponto para outro por meio de uma matriz LookAt e decompô-la para obter o componente rotacional. Você também pode obter um quaternion, se quiser.

Os resultados são iguais. A prova é um pouco longa, mas não complicada, e é deixada para o leitor.

Panda Pajama
fonte
Olá, @Panda Pyjama, obrigado pela sua resposta, mas não consigo entender sua resposta. Tento esclarecer: se eu tivesse o ponto de interseção e o ponto de vista, posso calcular wi e wo. Então eu posso usar o normal como minha direção do zênite para calcular, mas não consigo encontrar o outro eixo necessário para encontrar o ângulo do azimute em um plano ortogonal ao zênite. No exemplo acima, eu simplesmente apliquei as fórmulas de conversão para coordenadas esféricas em wi e wo fornecidas no sistema de coordenadas do mundo, mas não acho que esse seja o caminho certo para calcular teta e phi.
Fabrizio Duroni