· Hasan Alp Güngör
Logomu kaydırınca parçalanan 3D bir cama nasıl çevirdim
Portfolyo sitemdeki cam logonun arkasındaki işler: logoyu çizgilere çevirmek, yedi parçaya bölmek, cam efektini çalıştırmak ve zayıf telefonlarda kasmamasını sağlamak.
- Three.js
- React Three Fiber
- GSAP
- GLSL

Portfolyomda ilk başta 3D bir oyun kolu vardı. Sonra kendi logomu kullanmaya karar verdim ve şunu istedim: sayfa açıldığında logo sağlam dursun, aşağı kaydırdıkça önce çatlasın, sonra parçalarına ayrılsın ve her parça bir yeteneğimi göstersin. Bu yazıda bunu nasıl yaptığımı anlatıyorum.
1. Resimden şekle
Elimde logonun sadece bir resmi vardı. 3D için ise köşe noktaları gerekiyor. Resmi OpenCV ile siyah beyaza çevirip konturlarını çıkardım, gereksiz noktaları sadeleştirdim ve her şeyi 2,6 birim yüksekliğe ölçekledim. Sonuçta bir dış çizgi ve logonun içindeki sekiz delik kaldı.
Parçalar için logoyu düz çizgilerle kesmek yapay duruyordu. Bunun yerine zikzaklı "çatlak" çizgileri tanımlayıp logoyu shapely ile bu çizgilerden böldüm. Böylece gerçekten kırılmış gibi görünen yedi parça çıktı: web, C# ve Java, 3D, Python, oyun geliştirme, donanım ve backend.
Her parçayı three.js'in ExtrudeGeometry'si ile kalınlaştırdım. Kenarlara bir pah (bevel) ekledim; cam efektinde ışığı yakalayan şey bu yuvarlatılmış kenarlar.
2. Arkasında hiçbir şey olmayan cam
Cam için drei'nin MeshTransmissionMaterial'ını kullandım. Bu materyal, arkasındaki sahneyi bükerek camı taklit ediyor. Ama bir sorun vardı: sitenin gümüş arka planı 3D sahnede değil, CSS ile çiziliyor. 3D sahnenin arkası boş olduğu için cam bükecek bir şey bulamıyor ve gri, mat bir plastik gibi görünüyordu.
Çözüm olarak arka planın bir benzerini bir shader ile yeniden çizdim. Her karede logoyu gizleyip bu arka planı ekranda görünmeyen bir tampona (FBO) çiziyorum, sonra bu tamponu materyale "arkanda bu var" diye veriyorum. Cam artık sayfanın arkasındaki gümüş ışığı büküyor. Üstüne kenarları beyaz parlatan ayrı bir fresnel shader'ı ekledim.
3. Kaydırma hikâyesi
Logonun duruşunu GSAP ScrollTrigger ile sayfanın kaydırma konumuna bağladım. Birkaç anahtar poz var (sağlam, çatlamış, dağılmış, dönmüş) ve kaydırdıkça bu pozlar arasında geçiş yapılıyor. Parçaların üzerindeki etiketler normal HTML; her karede parçanın 3D konumunu ekrana yansıtıp etiketi o noktaya taşıyorum.
En sinir bozucu hata da burada çıktı: sayfanın en üstüne geri dönünce logo kayboluyordu. Sebebi, ScrollTrigger'ın başlangıç noktasını "top top" yapmamdı. Kaydırma 0'dayken tetikleyici aktif sayılmıyordu, ben de aktif değilken çizimi durduruyordum. Başlangıcı "top bottom" yapınca düzeldi.
4. Zayıf telefonlar
Bu cam efekti ağır. Bu yüzden site açılırken cihaza bakıp üç kaliteden birini seçiyor:
- Yüksek: tam cam efekti, daha keskin görüntü
- Orta: telefonlar ve 4 GB veya daha az belleği olan cihazlar için, cam daha az örnekle çiziliyor
- Hafif: veri tasarrufu açıksa ya da cihaz çok zayıfsa; cam efekti yerine sade, yarı saydam bir malzeme
Seçim yanlış çıkarsa diye site birkaç saniye sonra kare hızını izlemeye başlıyor. Saniyede 30 karenin altında kalırsa kendiliğinden bir alt kaliteye iniyor.
tsfunction detect(): Quality {
const nav = navigator as Navigator & { deviceMemory?: number; connection?: { saveData?: boolean } };
const memory = nav.deviceMemory ?? 8;
const cores = nav.hardwareConcurrency ?? 8;
if (nav.connection?.saveData || memory <= 2 || cores <= 2) return 'lite';
if (matchMedia('(pointer: coarse)').matches || memory <= 4 || cores <= 4) return 'medium';
return 'high';
}5. Fareyle dalgalanan cam
Son olarak, fare logonun üzerine gelince camın dalgalanmasını istedim. Fareden bir ışın gönderip logoya çarpıp çarpmadığına bakıyorum. Çarpıyorsa arka plan shader'ında imlecin etrafındaki koordinatları bir sinüs dalgasıyla kaydırıyorum. Cam bu bükülmüş arka planı kırdığı için dalga camın içinde görünüyor.
glslvec2 toMouse = (uv - uMouse) * vec2(uAspect, 1.0);
float dist = length(toMouse);
float wave = sin(dist * 70.0 - uTime * 7.0) * exp(-dist * 7.0) * uHover;
uv += normalize(toMouse + 1e-5) * wave * 0.03;Sonucu görmek için sitenin ana sayfasını açıp aşağı kaydırman yeterli. Kodun hepsi GitHub'da açık.

