광선 추적의 BRDF 및 구면 좌표


9

표준 phong / blinn phong 조명 모델을 사용하는 광선 추적기를 개발했습니다. 이제 물리 기반 렌더링을 지원하도록 수정하고 있으므로 다양한 BRDF 모델을 구현하고 있습니다. 현재 저는 Oren-Nayar 및 Torrance-Sparrow 모델에 중점을두고 있습니다. 이들 각각은 입사 wi 및 나가는 광 방향을 표현하는 데 사용되는 구형 좌표를 기반으로합니다.

내 질문은 : 올바른 방법으로 직교 좌표에서 구형 좌표로 wi와 wo를 변환하는 방법은 무엇입니까?

https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions 에보 고 된 표준 수식을 적용하고 있지만 내 벡터가 원점에 꼬리가 없으므로 올바른 일을하고 있는지 확실 하지 않습니다. 직교 좌표계이지만 객체와 광선의 교차점을 중심으로합니다.

여기 내 현재 구현을 찾을 수 있습니다.

누구든지 wi 및 wo 벡터를 직교 좌표에서 구형 좌표로 변환하는 올바른 방법에 대한 설명을 도와 줄 수 있습니까?

최신 정보

코드의 관련 부분을 여기에 복사합니다.

구면 좌표 계산

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

오렌 나야 르

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

토 런스 참새

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

업데이트 2

일부 검색 후 Oren-Nayar BRDF 구현을 발견 했습니다 .

위의 구현에서 wi 및 wo에 대한 세타는 단순히 arccos (wo.dotProduct (Normal)) 및 arccos (wi.dotProduct (Normal))를 수행하여 얻습니다. 우리는 구형 좌표계의 천정 방향으로 교차점의 법선을 사용하고 계산을 수행 할 수 있기 때문에 나에게 합리적인 것처럼 보입니다. 감마 = cos (phi_wi-phi_wo)의 계산은 "탄젠트 스페이스"라고 불리는 것에 대해 일종의 wi 및 wo 투영을 수행합니다. 이 구현에서 모든 것이 정확하다고 가정하면 수식을 사용할 수 있습니다. | View-Normal x (View.dotProduct (Normal)) | 및 | Light-표준 x (Light.dotProduct (Normal)) | arc 좌표 ( "something") 대신 phi 좌표를 얻으려면?


누구든지 나를 도울 수 있습니까?
Fabrizio Duroni

전체 저장소가 아닌 정확한 코드 스 니펫을 보여줄 수 있습니까?
concept3d

이것은 시대의 광선 추적에 대한 가장 신비한 질문 중 하나 인 것 같습니다 : D
Fabrizio Duroni


@ concept3d를 완료했습니다. 당신은 여기에서 찾을 수 있습니다 computergraphics.stackexchange.com/questions/1799/...
파브리 지오 Duroni에게

답변:


2

실제로 더 나은 하지 BRDF의를 구현하기 위해 구형 좌표 (또는 그 문제에 대한 모든 각도)를 사용하는 것이 아니라 바로 데카르트의 일을 당신이 알고있는 단위 벡터 사이의 일반 내적은 벡터 사이의 각도의 시스템 사용 코사인을 조정. 이것은 더욱 강력하고 효율적입니다.

Oren-Nayar의 경우 각도를 사용해야한다고 생각할 수도 있지만 (각도의 최소 / 최대로 인해) 직교 공간에서 BRDF를 간단하게 구현할 수 있습니다. https://fgiesen.wordpress.com/2010/10/21 / 완료-귀하의-제발-제발

Torrance-Sparrow 또는 Cook-Torrance microfacet BRDF의 경우 구형 좌표를 사용할 필요가 없습니다. 이 BRDF에서 각도는 D / F / G 항과 BRDF 분모의 삼각 함수 (보통 코사인) 기능으로 전달되므로 구면 좌표를 거치지 않고 내적을 직선 또는 삼각 항등식으로 사용할 수 있습니다 .


1

법선 N과 다른 벡터가 주어지면 좌표계를 지정할 수 있습니다. 우리는 wi를 선택할 것입니다. 따라서 접평면에 투영 될 때 wi와 같은 방향을 가진 벡터는 방위각이 0입니다.

먼저 탄젠트 평면에 wi를 투영합니다. (wi가 이미 정규화되었다고 가정)

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

이제 wo와 똑같이 할 수 있습니다.

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

이제, 위트와 워트는 N과 직교하고 교차점에 접하는 평면에 놓여 있습니다.

이제 둘 사이의 각도를 계산할 수 있습니다.

azimuth = arcos ( dot(wit, wot) )

탄젠트 평면에 투영 될 때 재치와 관련하여 실제로는 방위각입니다.


0

교차점과 원점을 알고 있다면 그 점을 다른 점에서 빼는 것이 문제가 아니기 때문에 원점에서 온 것처럼 결과를 얻을 수 있습니까?

결과를 믿지 않고 먼 길을 가고 싶다면 LookAt 행렬을 통해 한 지점에서 다른 지점으로 회전 변환을 얻은 다음 분해하여 회전 구성 요소를 얻을 수 있습니다. 원하는 경우 쿼터니언을 얻을 수도 있습니다.

결과는 같습니다. 증거는 약간 길지만 복잡하지는 않으며 독자에게 달려 있습니다.


안녕하세요 @Panda Pajama 님의 답변에 감사드립니다. 그러나 귀하의 답변을 이해할 수 없습니다. 나는 명확히하려고합니다 : 교차점과 관점이 있으면 wi와 wo를 계산할 수 있습니다. 그런 다음 법선을 천정 방향으로 계산하여 계산할 수 있지만 천정에 직교하는 평면에서 방위각을 찾는 데 필요한 다른 축을 찾을 수 없습니다. 위의 그림에서 나는 세계 좌표계에서 주어진 wi와 wo에 구형 좌표에 대한 변환 공식을 간단히 적용했지만 이것이 세타와 phi를 계산하는 올바른 방법이라고 생각하지 않습니다.
Fabrizio Duroni
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.