#include "fiko_test.hpp" #include "fiko/render/golge_kamera.hpp" // SAF matematik (Vulkan yok → test baglanmaz) #include using namespace fiko; // golgeKameraKur SAF matematik sozlesmesi (Vulkan yok). Undercut gorunurlugunun // dogrulugu buna dayanir: kamera CEKIM YONUNDE tepeye kurulur, merkeze bakar ve // frustum tum AABB'yi kapsar. // // ⭐ EN KRITIK SOZLESME — REVERSE-Z: proje geneli yakin→1, uzak→0 (mat4.hpp dikey). // Yani cekim yonunde DAHA YUKARIDAKI nokta DAHA BUYUK ndc.z verir. analiz.frag // bu yuzden `mapDepth > curDepth + esik` ile ORTULU der (web'in `<` karsilastirmasinin // TERSI). Bu testler o isaret yonunu kilitler — ters cevrilirse golge ters doner. namespace { // Dunya noktasini golge clip → ndc'ye tasi (w=1 ortografik). Vec3f ndc(const Mat4f& vp, Vec3f p) { const Vec4f c = vp * Vec4f{p, 1.0f}; return {c.x / c.w, c.y / c.w, c.z / c.w}; } Aabbf kutuSinir() { Aabbf a; a.genislet({-1, -1, -1}); a.genislet({1, 1, 1}); return a; } } // namespace FIKO_TEST(golge_kamera_gecersiz_girdi) { FIKO_KONTROL(!golgeKameraKur(Aabbf{}, {0, 0, 1}).gecerli); // gecersiz AABB FIKO_KONTROL(!golgeKameraKur(kutuSinir(), {0, 0, 0}).gecerli); // sifir yon FIKO_KONTROL(golgeKameraKur(kutuSinir(), {0, 0, 1}).gecerli); FIKO_KONTROL(golgeKameraKur(kutuSinir(), {0, 0, 7.5f}).gecerli); // birim olmayan yon } // ⭐ REVERSE-Z ISARET SOZLESMESI: cekim yonunde yukaridaki nokta BUYUK z verir. FIKO_TEST(golge_kamera_reverse_z_isareti) { const auto k = golgeKameraKur(kutuSinir(), {0, 0, 1}); FIKO_KONTROL(k.gecerli); const f32 ustZ = ndc(k.vp, {0, 0, 1}).z; // +Z ust yuz (kameraya YAKIN) const f32 altZ = ndc(k.vp, {0, 0, -1}).z; // -Z alt yuz (UZAK) FIKO_KONTROL(ustZ > altZ); // reverse-Z: yakin BUYUK // Tum AABB [0,1] derinlik araliginda kalmali (frustum disina tasma yok). FIKO_KONTROL(altZ >= 0.0f && altZ <= 1.0f); FIKO_KONTROL(ustZ >= 0.0f && ustZ <= 1.0f); // Monotonluk: ara yukseklikler sirali. FIKO_KONTROL(ndc(k.vp, {0, 0, 0.5f}).z > ndc(k.vp, {0, 0, 0}).z); FIKO_KONTROL(ndc(k.vp, {0, 0, 0}).z > ndc(k.vp, {0, 0, -0.5f}).z); } // Yon tersine → siralama tersine (kalibin diger yarisi). FIKO_TEST(golge_kamera_ters_yon) { const auto k = golgeKameraKur(kutuSinir(), {0, 0, -1}); FIKO_KONTROL(k.gecerli); FIKO_KONTROL(ndc(k.vp, {0, 0, -1}).z > ndc(k.vp, {0, 0, 1}).z); } // Ayni cekim isini uzerindeki iki nokta AYNI uv'ye dusmeli — occlusion'in temeli. // (Ortografik: yon boyunca kayma xy'yi degistirmez.) FIKO_TEST(golge_kamera_ayni_isin_ayni_uv) { const auto k = golgeKameraKur(kutuSinir(), {0, 0, 1}); const Vec3f a = ndc(k.vp, {0.3f, -0.4f, 1.0f}); const Vec3f b = ndc(k.vp, {0.3f, -0.4f, -1.0f}); FIKO_KONTROL(std::abs(a.x - b.x) < 1e-5f); FIKO_KONTROL(std::abs(a.y - b.y) < 1e-5f); FIKO_KONTROL(a.z > b.z); // ayni isin, ust olan yakin // Egik yonde de gecerli olmali. const auto k2 = golgeKameraKur(kutuSinir(), {1, 1, 1}); const Vec3f c = ndc(k2.vp, {0, 0, 0}); const Vec3f e = ndc(k2.vp, Vec3f{0, 0, 0} + Vec3f{1, 1, 1}.birim() * 0.5f); FIKO_KONTROL(std::abs(c.x - e.x) < 1e-4f); FIKO_KONTROL(std::abs(c.y - e.y) < 1e-4f); FIKO_KONTROL(e.z > c.z); } // Tum AABB kosleri NDC xy [-1,1] icinde (marj sayesinde kenarda tasma yok). FIKO_TEST(golge_kamera_aabb_kapsar) { const Aabbf s = kutuSinir(); const Vec3f yonler[4] = {{0, 0, 1}, {1, 0, 0}, {1, 1, 1}, {-2, 0.5f, 1}}; for (const Vec3f& y : yonler) { const auto k = golgeKameraKur(s, y); FIKO_KONTROL(k.gecerli); for (int i = 0; i < 8; ++i) { const Vec3f p{(i & 1) ? s.azami.x : s.asgari.x, (i & 2) ? s.azami.y : s.asgari.y, (i & 4) ? s.azami.z : s.asgari.z}; const Vec3f n = ndc(k.vp, p); FIKO_KONTROL(n.x >= -1.0f && n.x <= 1.0f); FIKO_KONTROL(n.y >= -1.0f && n.y <= 1.0f); FIKO_KONTROL(n.z >= 0.0f && n.z <= 1.0f); // Vulkan derinlik araligi } } } // Eksene hizali OLMAYAN yonde de "yukari" secimi cokmemeli (yon || global eksen // oldugunda capraz sifir olurdu → en-dik eksen secimi bunu onler). FIKO_TEST(golge_kamera_yukari_secimi_saglam) { const Aabbf s = kutuSinir(); const Vec3f yonler[6] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}}; for (const Vec3f& y : yonler) { const auto k = golgeKameraKur(s, y); FIKO_KONTROL(k.gecerli); const Vec3f ileri = ndc(k.vp, y.birim()); // yon tarafindaki nokta const Vec3f geri = ndc(k.vp, y.birim() * -1.f); FIKO_KONTROL(ileri.z > geri.z); // her global eksende isaret dogru } } // ⭐ IKI SINIR: XY hedefe daralir (texel yogunlugu), Z engelleri KAPSAR. // Engel Z'de kirpilirsa haritaya girmez → orttugu yer yanlislikla "acik" cikar. FIKO_TEST(golge_kamera_iki_sinir_xy_daralir_z_kapsar) { const Aabbf hedef = kutuSinir(); // [-1,1]^3 Aabbf engel; // hedefin COK ustunde+yaninda bir engel engel.genislet({-1, -1, -1}); engel.genislet({8, 8, 20}); const auto tek = golgeKameraKur(engel, {0, 0, 1}); // eski: her sey tek sinir const auto ikili = golgeKameraKur(hedef, engel, {0, 0, 1}); // yeni: XY hedef, Z engel FIKO_KONTROL(tek.gecerli && ikili.gecerli); // XY DARALDI: hedefin KOSELERI ikili kamerada NDC'nin cok daha buyuk kismini // kaplar (= ayni texel sayisi daha kucuk alana → daha yogun → az merdiven). // NOT: ndc.x dunya X'i DEGIL, kameranin `sag` eksenidir → eksen adiyla test etme, // kosleri tara. (Bu testin ilk hali tam bu yuzden yanlis kirmizi verdi.) // Olcu = hedefin NDC'de kapladigi GENISLIK (konumu degil!). NDC genisligi × // cozunurluk = hedefe dusen texel sayisi → merdivenin dogrudan olcusu. const auto genislik = [&](const Mat4f& vp) { f32 mnX = 1e30f, mxX = -1e30f, mnY = 1e30f, mxY = -1e30f; for (int i = 0; i < 8; ++i) { const Vec3f p{(i & 1) ? hedef.azami.x : hedef.asgari.x, (i & 2) ? hedef.azami.y : hedef.asgari.y, (i & 4) ? hedef.azami.z : hedef.asgari.z}; const Vec3f n = ndc(vp, p); mnX = std::min(mnX, n.x); mxX = std::max(mxX, n.x); mnY = std::min(mnY, n.y); mxY = std::max(mxY, n.y); } return std::min(mxX - mnX, mxY - mnY); // en dar eksen belirleyici }; const f32 tekG = genislik(tek.vp), ikiliG = genislik(ikili.vp); FIKO_KONTROL(ikiliG > tekG * 1.5f); // hedefe DUSEN texel sayisi arttı FIKO_KONTROL(ikiliG <= 2.0f); // ...frustum disina tasmadan ([-1,1] = 2 birim) FIKO_KONTROL(ikiliG > 1.7f); // ve NDC'nin neredeyse tamamini doldurarak // Z KAPSIYOR: engelin en ust noktasi (z=20) hala [0,1] derinlik araliginda. const f32 engelZ = ndc(ikili.vp, {0, 0, 20}).z; FIKO_KONTROL(engelZ >= 0.0f && engelZ <= 1.0f); // Engelin dibi de. const f32 dipZ = ndc(ikili.vp, {0, 0, -1}).z; FIKO_KONTROL(dipZ >= 0.0f && dipZ <= 1.0f); // Isaret hala dogru: yukaridaki engel hedefin ustunden BUYUK z verir → orter. FIKO_KONTROL(engelZ > ndc(ikili.vp, {0, 0, 1}).z); } // Tek-sinirli kisayol = iki-sinirli (hedef==engel) — geri uyum. FIKO_TEST(golge_kamera_kisayol_esdeger) { const Aabbf s = kutuSinir(); const auto a = golgeKameraKur(s, {1, 0, 1}); const auto b = golgeKameraKur(s, s, {1, 0, 1}); FIKO_KONTROL(a.gecerli && b.gecerli); for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) FIKO_KONTROL(std::abs(a.vp.e[i][j] - b.vp.e[i][j]) < 1e-6f); } // Gecersiz engel sinirı → hedefe dusmeli (cokme/NaN yok). FIKO_TEST(golge_kamera_gecersiz_engel_hedefe_duser) { const auto k = golgeKameraKur(kutuSinir(), Aabbf{}, {0, 0, 1}); FIKO_KONTROL(k.gecerli); const Vec3f n = ndc(k.vp, {0, 0, 1}); FIKO_KONTROL(n.z >= 0.0f && n.z <= 1.0f); } // Yassi (sifir kalinlikta) AABB → sifira bolme yok, gecerli matris. FIKO_TEST(golge_kamera_yassi_aabb) { Aabbf s; s.genislet({-1, -1, 0}); s.genislet({1, 1, 0}); // z kalinligi 0 const auto k = golgeKameraKur(s, {0, 0, 1}); FIKO_KONTROL(k.gecerli); const Vec3f n = ndc(k.vp, {0, 0, 0}); FIKO_KONTROL(n.z >= 0.0f && n.z <= 1.0f); FIKO_KONTROL(std::isfinite(n.x) && std::isfinite(n.y) && std::isfinite(n.z)); }