holodepth

Three.js · İleri geometri

Terrain generation: dijital dünyanın temellerini atmak

Heightmap’ten sonsuz dünyaya — CPU/GPU işbölümü

Advanced Geometries serisinde prosedürel (yordamsal) sistemlere giriş yaptıktan sonra, bu gücü en somut ve etkileyici şekilde kullanabileceğimiz alana geliyoruz: terrain generation (arazi oluşturma). Modern 3D web deneyimlerinde devasa dünyaları yalnızca statik dosyalardan yüklemek yerine, matematiksel algoritmalar ve GPU ile çalışma anında inşa etmek, Holodepth vizyonunun merkezinde durur: okunabilir kod, öngörülebilir bellek ve tarayıcıda sürdürülebilir kare süresi. Canlı chunk + LOD ölçüleri demo sabitleri tablosunda doc-terrain-demo.js ile eşlenir.

Heightmaps: sanatsal kontrol katmanı

Arazi oluşturmanın en klasik yöntemi heightmap (yükseklik haritasıdır): 2D bir görüntüdeki gri tonlama (çoğu iş akışında 0–255 veya yüzen nokta aralığı) verisini, 3D uzayda bir düzlemin Y (veya seçtiğiniz “yukarı” ekseni) bileşenine aktarır. Three.js tarafında bu genelde paylaşılan bir PlaneGeometry veya özel BufferGeometry üzerinde vertex displacement ile somutlaşır.

Modern yaklaşım: Günümüzde heightmap çoğu zaman yalnızca başlangıç katmanı veya sanatçı kontrolü olarak kalır: ana silueti harita belirler; “premium” detay ise sonradan eklenen prosedürel katmanlarla — Perlin/Simplex gürültü, domain warp, erozyon benzeri geçişler — kazanılır. Böylece hem yönetilebilir bir omurga hem de tekrarlanabilir varyasyon elde edilir.

Sınırlandırma: Klasik heightfield her (x, z) için tek bir yükseklik taşır; bu yüzden mağara tavanları, içe doğru kıvrılan yüzeyler veya tek noktada birden fazla “katman” tek başına çözülmez. Bu tür hacimler için voxel, implicits veya mesh Boolean gibi başka temsil seçilir — terrain sayfasında odak yeryüzü kabuğudur.

GPU tabanlı deformasyon ve iş yükü dağılımı

Holodepth hedef kitlesinde ağır matematik genellikle GPU’ya emanet edilir; fakat burada kritik bir orkestrasyon vardır: CPU tamamen devreden çıkmaz.

  • Veri yönetimi (CPU): Parça (chunk) ağacı, dünya seed’i, hangi bölgenin yükleneceği, LOD eşikleri ve fizik için özet yükseklik (ör. basit height sample) CPU’da kalır. Bu katman “ne çizileceğine” karar verir.
  • Hesaplama gücü (GPU): Vertex displacement, gürültü örnekleme ve (varsa) tessellation benzeri yoğun işler GPU’da yürür. Böylece aynı draw çağrısı altında geniş alanlar akıcı kalır; vertex sayısı artsa bile CPU komut kuyruğu şişmez.

Pratikte interleaved buffer düzeni ve önbellek dostu vertex paketleme, yoğun arazi ağında bant genişliğini ciddi etkileyebilir — özellikle pozisyon + normal + UV birlikte güncelleniyorsa.

Normal ve tangent: ışığın gerçekliği

Köşeleri yukarı itmek (displacement) işin yarısıdır. Işığın yeni tepelere ve vadilere tutarlı düşmesi için normallerin yüzeyle uyumlu olması gerekir; aksi halde arazi doğru şekilde kıvrılmış olsa bile aydınlatma “düz karton” gibi görünür.

Normal recompute: Geometri CPU’da güncelleniyorsa geometry.computeVertexNormals() sık kullanılır. Shader’da anlık displacement yapıyorsanız, ya türetilmiş normaller kullanılır ya da ayrı bir pass ile yaklaşık normaller yazılır — seçim kalite ve maliyet dengesine bağlıdır.

Derivative normals: Parçacık yüzey türevleri (dFdx, dFdy) ile eğimden normale yaklaşım, ek normal haritası olmadan bile PBR hissi verebilir; özellikle yüksek çözünürlüklü gürültü ile birleşince verimlidir. Normal haritası kullanıyorsanız tangent uzayı (Mikktspace uyumu) kritik hale gelir; bu konuda custom attribute ve özel vertex verisi sık devreye girer.

Chunking ve seam (kenar) problemleri

Devasa dünyalar tek mesh olarak taşınmaz; arazi chunk’lara bölünür. Parçalar birleştiğinde iki tip görsel risk doğar: LOD crack (farklı çözünürlükte komşu kenarların birbirinden kopması) ve derinlik/z-fighting kaynaklı ince çizgiler.

  • Skirt geometry: Chunk kenarından aşağı inen kısa “etek” yüzeyleri, milimetrik boşlukları gizlemek için yaygın bir tampondur; maliyeti birkaç ekstra üçgen ile ödenir.
  • Stitching (dikişleme): Düşük ve yüksek LOD komşu olduğunda, sınır vertex’lerinin dünya uzayında eşleştirilmesi veya geçiş şeridi ile topoloji uyumu sağlanır; aksi halde ışık ve gölge “yırtık” gösterir.

Streaming düşünüyorsanız, chunk sınırları aynı zamanda bellek ve GC sınırlarıdır: geometry nesnelerini havuzlamak ve BufferGeometry.dispose() disiplinini korumak uzun oturumlarda stabilite sağlar.

Canlı: chunk + LOD oyun alanı

Aşağıdaki sahne tamamen tarayıcıda üretilir: harici heightmap veya API çağrısı yoktur (veri sızması / beklenmeyen ağ trafiği riski yok). Parça başına LOD açıldığında komşu chunk’larda farklı segment yoğunluğu oluşur; bu, metinde anlatılan LOD crack / T-junction riskini gözlemlemeniz için kasıtlıdır. Varsayılan LOD renk vurgusu ve yavaş otomatik dönüş, çözünürlük değişimini kare sayısına bağlı yorumlamayı azaltır. Düz normaller açıkken ışığın “facet” kırılımını, kapalıyken computeVertexNormals ile yumuşak gölgelendirmeyi karşılaştırabilirsiniz. Teknik ölçüler demo sabitleri tablosunda özetlenir.

Chunk + LOD playground
Parça: — LOD: — FPS: —

Güvenlik: dış kaynak yok, girdiler yalnızca sayısal slider/checkbox. Düşük parça sayısı ve orta segment hedefi, FPS’in LOD fikrini gölgelemesini azaltır; yine de yoğun sahnede LOD asıl kazanımı draw / fill tarafında verir.

LOD renk vurgusu açıkken L0/L1/L2 farklı tonda görünür; hareketle (orbit veya yavaş otomatik dönüş) hangi parçanın ne zaman sadeleştiğini izlemek kolaylaşır. Wireframe üçgen yoğunluğunu gösterir. Parça başına LOD açıkken komşu renk/segment farkı kenar riskini tartışmaya açar — üretimde stitch / skirt ile dengelenir.

Demo sabitleri tablosu (Chunk + LOD playground)

initTerrainPlayground · dış heightmap / ağ yok; yükseklik sampleHeight ile üretilir.

initTerrainPlayground · chunk + LOD
Sahne / rol Parametre Değer Tür
Parça boyutu CHUNK 6 (dünya birimi kenar) 🔒 Sabit
Izgara GRID_R 1(2R+1)² = 9 parça 🔒 Sabit
LOD segment LOD_SEGS [16, 8, 4] → L0 / L1 / L2 🔒 Sabit
İlk mesh buildChunkGeometry segment başlangıçta sabit 10×10 (sonra LOD’a göre yenilenir) 🔒 Sabit
LOD eşik lodFromDistance (parça başına) d < 9 → L0 · d < 22 → L1 · aksi L2 🔒 Sabit
LOD eşik lodFromDistance (birlik) d < 14 → L0 · d < 32 → L1 · aksi L2 🔒 Sabit
Yükseklik sampleHeight 4 oktav · f×1.95 · m×0.48 · a×0.62 · sin/cos terimleri 🔒 + ↔ UI
Girdi clamp readParams frekans 0.04…0.55 · genlik 0.35…4.5 🔒 + ↔ UI
Materyal taban matBase roughness 0.88 · metalness 0.04 · FrontSide 🔒 Sabit
Gövde rengi matNeutral 0x3d6b58 🔒 Sabit
LOD vurgu matLod[0…2] 0x2d9d6a · 0x3d7da8 · 0x7a6aac (açıkken) ↔ UI
Chunk ızgarası LineBasicMaterial 0x5ec8ff · opaklık 0.35 · çizgi y = 0.04 🔒 Sabit
Sis Fog 0x070910 · yakın/uzak 18 / 95 🔒 Sabit
Işık HemisphereLight 0xb8e0ff / 0x1a1e28 · yoğunluk 0.9 🔒 Sabit
Işık DirectionalLight beyaz · yoğunluk 1.15 · konum (12, 22, 8) 🔒 Sabit
Renderer WebGLRenderer antialias: false · arka plan 0x070910 · setPixelRatio(min(dpr, 1.35)) · SRGBColorSpace 🔒 Sabit
Kamera PerspectiveCamera FOV 50 · yakın/uzak 0.1 / 200 · konum (22, 16, 28) 🔒 Sabit
OrbitControls hedef / mesafe / dönüş (0, 1.2, 0) · 8…85 · damping 0.06 · maxPolarAngle = π/2 − 0.08 · autoRotateSpeed 0.42 🔒 + ↔ UI
HTML başlangıç checkbox / slider chunk ızgarası + LOD renk + yavaş dönüş açık; parça başına LOD kapalı · frekans/genlik HTML varsayılanları 🔒 + ↔ UI

Önemli kod kesiti

Yükseklik tamamen deterministik sinüs katmanlarından üretilir; dış texture veya ağ çağrısı yoktur.

function sampleHeight(wx, wz, freq, amp) {
  let f = freq;
  let h = 0;
  let m = 1;
  let a = amp;
  for (let o = 0; o < 4; o += 1) {
    h += a * m * Math.sin(wx * f + o * 0.7) * Math.cos(wz * f * 1.07 + o * 0.3);
    f *= 1.95;
    m *= 0.48;
    a *= 0.62;
  }
  return h;
}

Sonsuz dünya ve floating origin

“Sonsuz” hissi veren dünyada oyuncu merkezden uzaklaştıkça float32 hassasiyeti zayıflar; uzak koordinatlarda jitter (titreme) ve gölge/depth hataları artar. Çözüm, dünyayı büyütmek değil, periyodik olarak koordinat sistemini yeniden merkezlemektir.

Floating origin: Kamera (veya oyuncu) çok uzaklaştığında, tüm chunk ve dinamik nesneleri ters yönde kaydırıp sayıları küçük tutarsınız; böylece GPU ve CPU transform zinciri tekrar “temiz” aralıkta kalır.

Coordinate shifting: Fizik motoru ve ağ senkronu varsa kaydırma olayı tüm alt sistemlere tutarlı yansıtılmalıdır; aksi halde tek karelik sıçramalar (pop-in) görülebilir. Bu teknik, büyük açık dünya web deneyimlerinde “elite level” sayılır çünkü hem görsel hem simülasyon borcunu aynı anda kapatır.

Three.js notu

Object3D.position büyüdükçe hassasiyet sorunları yaşanır; büyük dünya için ya floating origin ya da kamera-merkezli streaming stratejisi şarttır. Yoğun örnekli detay için instancing ile chunk başına tek draw çağrısı hedefi sık birleştirilir.

Özet: Holodepth arazi mimarisi

Terrain generation, matematiğin sanata dönüştüğü katmandır. Buffer yönetimi ve prosedürel teknikleri burada birleştirdiğinizde amaç yalnızca görsel şölen değil; optimize edilmiş, ışıkla tutarlı ve mümkün olduğunca sonsuz ölçekte stabil kalan bir dijital dünya kurmaktır.

Sonraki adımlar: gerçek zamanlı erozyon veya compute tabanlı üretim (WebGPU ile) gibi konular bu omurgayı genişletir; fakat heightmap + GPU displacement + doğru normal + chunk dikişleri + origin kaydırma, tarayıcıda sağlam bir arazi hattının çekirdeğini oluşturur.