광선 추적의 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.