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.
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.
Demo sabitleri tablosu (Chunk + LOD playground)
initTerrainPlayground · dış heightmap / ağ yok; yükseklik
sampleHeight ile üretilir.
| 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.