BRDF และการประสานงานทรงกลมในการติดตามเรย์


9

ฉันพัฒนาเครื่องติดตามรังสีที่ใช้โมเดลไฟพงษ์ / บลินมาตรฐาน ตอนนี้ฉันกำลังแก้ไขเพื่อสนับสนุนการเรนเดอร์ตามร่างกายดังนั้นฉันจึงใช้โมเดล BRDF ต่างๆ ในขณะนี้ฉันมุ่งเน้นโมเดลของ Oren-Nayar และ Torrance-Sparrow แต่ละอันขึ้นอยู่กับพิกัดทรงกลมที่ใช้ในการแสดงเหตุการณ์ที่เกิดขึ้นและทิศทางแสง wo ขาออก

คำถามของฉันคือวิธีใดที่เหมาะสมที่จะแปลง wi และ wo จากพิกัดคาร์ทีเซียนไปเป็นพิกัดกลม

ฉันกำลังใช้สูตรมาตรฐานที่รายงานที่นี่https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversionsแต่ฉันไม่แน่ใจว่าฉันทำสิ่งที่ถูกต้องเพราะเวกเตอร์ของฉันไม่ได้อยู่ที่ต้นกำเนิดของ ระบบพิกัดคาร์ทีเซียน แต่มีศูนย์กลางอยู่ที่จุดตัดของรังสีกับวัตถุ

ที่นี่คุณสามารถค้นหาการใช้งานปัจจุบันของฉัน:

ใครสามารถช่วยฉันอธิบายวิธีที่ถูกต้องในการแปลงเวกเตอร์ wi และ wo จากคาร์ทีเซียนไปเป็นพิกัดกลม

UPDATE

ฉันคัดลอกส่วนที่เกี่ยวข้องของรหัสที่นี่:

การคำนวณพิกัดทรงกลม

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

Torrance-กระจอก

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นี้

ในการดำเนินการดังกล่าวข้างต้น theta สำหรับ wi และ wo นั้นได้รับเพียงแค่ทำ arccos (wo.dotProduct (ปกติ)) และ arccos (wi.dotProduct (ปกติ)) สิ่งนี้ดูสมเหตุสมผลสำหรับฉันเนื่องจากเราสามารถใช้จุดปกติของจุดตัดเป็นทิศทางที่ดีที่สุดสำหรับระบบพิกัดทรงกลมของเราและทำการคำนวณ การคำนวณแกมม่า = cos (phi_wi - phi_wo) ทำการประมาณการแบบ wi และ wo ในสิ่งที่เรียกว่า "พื้นที่สัมผัสกัน" สมมติว่าทุกอย่างถูกต้องในการนำไปใช้นี้ฉันสามารถใช้สูตร | ดู - ปกติ x (View.dotProduct (ปกติ)) | และ | แสง - ปกติ x (Light.dotProduct (ปกติ)) การรับพิกัด phi (แทนที่จะใช้ arctan ("อะไร"))?


ใครช่วยฉันได้บ้าง
Fabrizio Duroni

คุณสามารถแสดงข้อมูลโค้ดที่ถูกต้องไม่ใช่ข้อมูลซื้อคืนทั้งหมดได้หรือไม่
concept3d

ดูเหมือนว่านี่เป็นหนึ่งในคำถามที่ลึกลับที่สุดเกี่ยวกับ ray tracing al al: D
Fabrizio Duroni

ฉันแนะนำให้คุณถามที่นี่computergraphics.stackexchange.com
concept3d

เสร็จสิ้น @ concept3d คุณสามารถค้นหาได้ที่นี่computergraphics.stackexchange.com/questions/1799/…
Fabrizio Duroni

คำตอบ:


2

จริง ๆ แล้วมันจะดีกว่าที่จะไม่ใช้พิกัดทรงกลม (หรือมุมใด ๆ สำหรับเรื่องนั้น) เพื่อนำ BRDF ไปใช้ แต่ทำงานโดยตรงในระบบพิกัดคาร์ทีเซียนและใช้โคไซน์ของมุมระหว่างเวกเตอร์ซึ่งเป็นผลคูณจุดระหว่างเวกเตอร์หน่วย นี่คือทั้งแข็งแกร่งและมีประสิทธิภาพ

สำหรับ Oren-Nayar คุณอาจคิดว่าคุณต้องใช้มุม (เนื่องจาก min / max ของมุม) แต่คุณสามารถใช้ BRDF ในพื้นที่คาร์ทีเซียนได้โดยตรง: https://fgiesen.wordpress.com/2010/10/21 / เสร็จสิ้นของคุณ derivations กรุณา

สำหรับ microfacet Torrance-Sparrow หรือ Cook-Torrance BRDF คุณไม่จำเป็นต้องใช้พิกัดทรงกลมเช่นกัน ใน BRDF เหล่านี้มุมจะถูกส่งผ่านไปยังฟังก์ชันตรีโกณมิติ (โดยทั่วไปคือโคไซน์) ในเงื่อนไข D / F / G & ตัวส่วน BRDF ดังนั้นคุณสามารถใช้ผลิตภัณฑ์ dot หรือตัวบ่งชี้ตรีโกณมิติโดยไม่ต้องผ่านพิกัดทรงกลม


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) )

ซึ่งจริง ๆ แล้วราบราบของ wot เกี่ยวกับปัญญาเมื่อฉายบนระนาบแทนเจนต์


0

ถ้าคุณรู้จุดแยกและจุดกำเนิดมันจะไม่เป็นปัญหาในการลบอันใดอันหนึ่งจากอีกอันหนึ่งเพื่อที่คุณจะได้ผลลัพธ์เหมือนกับว่ามาจากต้นกำเนิดหรือไม่

หากคุณไม่เชื่อผลลัพธ์และต้องการไปให้ไกลคุณสามารถรับการแปลงแบบหมุนได้จากจุดหนึ่งไปอีกจุดหนึ่งผ่านเมทริกซ์ LookAt แล้วแยกย่อยมันเพื่อให้ได้องค์ประกอบการหมุน นอกจากนี้คุณยังสามารถรับ quaternion จากมันหากคุณต้องการ

ผลลัพธ์ที่เท่ากัน การพิสูจน์นั้นค่อนข้างยาว แต่ไม่ซับซ้อนและเหลือไว้ให้ผู้อ่าน


สวัสดี @Panda Pajama ขอบคุณสำหรับคำตอบของคุณ แต่ฉันไม่เข้าใจคำตอบของคุณ ฉันพยายามอธิบาย: ถ้าฉันมีจุดตัดและมุมมองฉันสามารถคำนวณ wi และ wo ได้ จากนั้นฉันสามารถใช้ค่าปกติเป็นทิศทางสูงสุดของฉันในการคำนวณ แต่ฉันไม่สามารถหาแกนอื่นที่จำเป็นในการค้นหามุมแอซิมัทในมุมฉากของระนาบถึงซีนิ ธ ใน snipped ด้านบนฉันใช้สูตรการแปลงสำหรับพิกัดกลมบน wi และ wo ที่กำหนดในระบบพิกัดโลก แต่ฉันไม่คิดว่านี่เป็นวิธีที่เหมาะสมในการคำนวณทีต้าและพี
Fabrizio Duroni
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.