holodepth

Three.js · Gölge · Harita tabanlı

Shadow Map (Gölge Haritası) nedir?

Işığın gözünden derinlik — sonra kamera pikseli başına “arada engel var mı?”

Üç boyutlu bir sahnede gölge, yalnızca ışığın çarptığı yerin kararması kadar basit bir süreç değildir. Bilgisayar grafiklerinde gölge üretmenin en yaygın ve performanslı yollarından biri Shadow Mapping tekniğidir: sahne, ışığın bakış açısından bir kez “derinlik olarak” çizilir; ana kamera çizerken her nokta, bu haritayla karşılaştırılarak gölgede mi değil mi belirlenir.

Bağlam: Gölge girişi (fizik–harita köprüsü), DirectionalLight, SpotLight, PointLight, RectAreaLight (çekirdekte shadow map üretimi yok).

Temel mantık: ışığın bakış açısı

Shadow map’i anlamanın en kısa yolu şudur: Işığa bir göz takın ve sahneye onun gözünden bakın. Işık kaynağı kendi pozisyonundan (ve yönünden) sahneye baktığında hangi yüzeyleri “önde” görüyorsa, o bölgeler ışık altındadır. Işığın görüş hattında başka bir yüzeyin arkasında kalan noktalar ise gölge adayıdır: ışın o noktaya ulaşmadan önce engellenmiştir.

Harita uzayı: piksel = “ışık penceresi”

Shadow map, ışığın projeksiyonunda bir doku ızgarasıdır: her texel, dünya uzayında belirli bir ayak izi (footprint) temsil eder. Bu yüzden aynı geometri, harita çözünürlüğü düştükçe gölge kenarında merdiven (aliasing) oluşturur; çözünürlük arttıkça bellek ve doldurma maliyeti büyür — beşinci bölümde mapSize bu dengeyi doğrudan tutar.

Teknik süreç: iki aşamalı render

Shadow mapping, GPU üzerinde tipik olarak iki ana aşamada işler:

  1. Derinlik geçişi (depth pass): Sahne, ana kameranın değil, ışığın bakış açısından render edilir. Bu aşamada renklere odaklanılmaz; her hedef piksel için ışığa göre derinlik (z) değeri hesaplanır ve bir dokuya yazılır. Bu derinlik dokusuna shadow map (gölge haritası) denir.
  2. Gölge testi (shadow test): Ana sahne, kamera gözünden çizilirken her piksel için “bu nokta ışığa göre ne kadar uzakta?” sorusunun cevabı, shadow map’teki derinlikle kıyaslanır. Pikselin ışığa olan mesafesi, haritadaki engel mesafesinden büyükse, ışık ile nokta arasında daha yakında bir yüzey vardır ve piksel gölgelendirilir.

Derinlik geçişinde ne çizilir?

Tipik kurulumda derinlik geçişi gölge atan (castShadow) opak geometri üzerinden gider; şeffaf yüzeyler, çift taraflı ince geometri veya özel materyal yolları bu hikâyeyi karmaşıklaştırabilir. Ana fikir yine aynıdır: harita “ışığın gördüğü en yakın yüzey”i kaydeder; ana geçişte piksel bu yüzeyin gerisinde mi diye sorulur.

Işık türüne göre gölge üretimi

Three.js’te gölge, ışığın geometrisine göre farklı projeksiyon ve derinlik alanı ile üretilir:

  • DirectionalLight: Paralel ışınlar — güneş gibi uzak ve geniş alanlar için uygun ortografik gölge kamerası.
  • SpotLight: Perspektif koni içinde tek bir derinlik haritası — el feneri veya sahne spot’u.
  • PointLight: Her yöne yayılan ışık için çoğu zaman 6 yönlü derinlik (küp yüzleri / cube shadow) gerekir; maliyet genelde en yüksek seçeneklerdendir.

castShadow ve receiveShadow

Three.js’te gölge iki rolle ayrılır: mesh castShadow ile haritaya engel olur; receiveShadow ile zeminde veya başka yüzeyde haritayı okuyarak kararma alır. Işıkta castShadow = true olmadan harita üretilmez; alıcıda receiveShadow kapalıysa yüzey üzerinde harita testi yapılmaz — “ışık gölge üretiyor ama zeminde yok” çoğu zaman bu ikiliden biridir.

Shadow frustum (gölge görüş alanı)

Işığın gölge üretebildiği alan sınırsız değildir. Her ışığın bir shadow camera frustum’ı (görüş hacmi) vardır: kutunun veya koninin dışında kalan geometri, ekranda görünse bile bu ışık için gölge haritasına düşmeyebilir. Frustum’u sahneye sıkı sarmalamak hem çözünürlük verimini artırır hem de gereksiz boş alanı haritada israf etmezsin.

Sızma ve “kutu dışı” gölge

Frustum’u gereğinden geniş tutmak, haritada devasa boş alan demektir: aynı mapSize ile dünya birimine düşen texel sayısı azalır ve gölge kenarları kabaşır. Tersine, kutuyu fazla dar tutmak ise sahnede görünen ama haritaya hiç düşmeyen geometriye yol açar — gölge ani kesilir veya tamamen kaybolur. Üretimde kutu, oyun alanını saracak kadar sıkı; clip düzlemleri ise z-fighting ve kesme arasında dengeye getirilir.

Çözünürlük ve kalite: mapSize

Shadow map bir dokudur; light.shadow.mapSize (ör. 512, 1024, 2048) haritanın piksel boyutunu belirler.

  • Düşük çözünürlük: Gölgeler bloklu ve tırtıklı görünür (aliasing).
  • Yüksek çözünürlük: Kenarlar daha keskin olur; GPU belleği ve doldurma maliyeti artar.

Bir texel kaç “metre”?

Harita çözünürlüğü tek başına anlamlı değildir; anlamlı olan gölge kamerası genişliği / harita pikseli oranıdır. Geniş alanı 1024 texele yaymak ince detayı öldürür; dar alanı 2048’e sıkıştırmak ise bellek ve band genişliğini harcar. Bu yüzden önce frustum’u (dördüncü bölüm), sonra mapSize’ı ayarlamak daha tutarlı bir sıradır.

Yaygın problemler: shadow artifacts

Shadow acne & Peter Panning

Shadow acne: Derinlik karşılaştırmasındaki hassasiyet yüzünden yüzeyin kendi üzerine “titreyen” veya noktalı gölge düşürmesi.

Peter Panning: Gölgenin yüzeyden kopuk görünmesi — nesnenin zeminde süzülüyormuş hissi.

Pratik çözüm: Three.js’te shadow.bias, shadow.normalBias ve gerektiğinde near/far ile frustum ayarı bu hataları yumuşatır; değerler sahneye göre ince ayar ister.

light.castShadow = true;
light.shadow.mapSize.set(1024, 1024);
light.shadow.bias = -0.0001;
light.shadow.normalBias = 0.02;

Sonraki okuma: bias ve filtre

Acne ve peter panning için sayısal örnekler ve teşhis akışı Bias & shadow acne sayfasında toplanır; filtre türü (shadowMap.type) ile birlikte düşünmek gerekir — özet tablo bu sayfanın 8. bölümünde.

İnteraktif: Shadow Playground

Dış model yok: geniş zemin, büyük küre (temas gölgesi), ince panel (zeminde keskin gölge kenarı) ve havada küçük küp (bias / acne için silüet). Tek ana ışık; OrbitControls yalnızca döndür + sınırlı zoom, pan kapalı. Aşağıdan mapSize, bias, ışık yönü ve kötü ayarlar ile acne / pikselleşmeyi güvenli biçimde gözlemle. Örnek sahne renderer.shadowMap.type için PCFSoft kullanır; diğer algoritmalar aşağıdaki 8. bölümdeki tabloda özetlenir.

Shadow Playground · tek ışık · yerel geometri
Işık

Harita: düşük = hızlı / gölge kaba · yüksek = net / GPU daha çok.

Sürükle: sadece dönüş + zoom (pan yok). Yönsel ışık seçiliyken Işık mesafesi sürgüsü devre dışıdır (paralel yön değişmez; anlamlı etki Spot modunda). Kötü ayar örneği açıkken harita 512², bias ve normal bias sıfıra zorlanır; harita ve bias sürgüleri kilitlenir — acne / z-fighting daha görünür olur. Kenarları vurgula ince panelin zemindeki gölge kenarına yaklaşır. Kapatınca kayıtlı harita ve bias değerlerin geri yüklenir. Spot kapsamı (zemin) açıkken zeminde mavi tonlu daireler Spot külahının izdüşümünü ve (yaklaşık) mesafe sönümünü gösterir; Yönsel modda bu kutu otomatik kapanır.

Üstteki Shadow Playground (diagram-shadow-map.js, bu sayfada varsayılan GOLD_DEMO sahnesi) ile aynı sabitleri ön harita tabloda topladık; aşağıdaki kod demodaki GOLD_DEMO geometri özetini ve DirectionalLight / SpotLight kurulumunu hatırlatır. Sürgülerle değişenler (azimut, yükseklik, mesafe, harita, bias) tabloda “HUD” satırında; kalan satırlar dosyadaki sabitlerdir. Tür sütunu: HUD ile oynananlar ile dosyadan gelen sabitleri bir bakışta ayırır.

Sabit doğrulama: tablodaki GOLD_DEMO, ışık ctor’ları ve HUD başlangıçları, canlı sahne ile aynı kaynak dosyadan gelir (diagram-shadow-map.js). Harita ve bias sürgüleri oynanırken satırlar “Dinamik (HUD)” etiketiyle ayrılır; geometri ve hedef vektörleri kilitlidir.

Bu demo’da kontrol edilenler

  • mapSize → gölge haritası çözünürlüğü (kalite / GPU maliyeti)
  • biasshadow acne / z-fighting (ince ayar)
  • normalBias → yüzey yönüne göre gölge kayması (silüet / yüzey hataları)
  • Azimut / yükseklik → ışığın geliş yönü (setLightPose)
  • Mesafe → yalnızca Spot modunda anlamlı (yönselde sürgü devre dışı; paralel ışıkta mesafe yönü değiştirmez)
diagram-shadow-map.js — GOLD Demo Sabitleri (shadow-map.html playground)
Sahne / rol Parametre Değer Tür
Ortak · hedef LIGHT_TARGET (yönsel ışık hedefi) (0, 0.58, 0) 🔒 Sabit
SpotLight.target (spot dalı) (0, 0.52, 0)setLightPose içinde sabit 🔒 Sabit
GOLD_DEMO · geometri Zemin PlaneGeometry planeSize 16 · materyal 0x2a3348, roughness 0.72, metalness 0.12 🔒 Sabit
Küre sphereR 0.5 · konum (−0.42, 0.5, 0.38) 🔒 Sabit
İnce panel (BoxGeometry) tx, ty, tz0.048 × 1.12 × 0.72 · konum (0.48, 0.56, −0.58) · rotation.y −0.34 🔒 Sabit
Havada küp cubeS 0.17 · konum (0.2, 0.69, 0.52) · rotation.y 0.22 🔒 Sabit
GOLD_DEMO · odak focusRing (Kenarları vurgula, zemin) Merkez yaklaşık (0.02, −0.05), yarıçap 0.42 🔒 Sabit
focusOrbit Kamera (0.45, 0.88, 1.52) → hedef (0.02, 0.028, −0.06), FOV 31, min/max mesafe 0.76 / 4.35 🔒 Sabit
DirectionalLight (ctor) color / intensity 0xfff4ec · 1.42 🔒 Sabit
Gölge kamera (ortho) left/right/top/bottom ±5.2 · near 0.8 · far 26 🔒 Sabit
SpotLight (ctor) color / intensity / distance 0xfff4ec · 88 · 30 🔒 Sabit
angle / penumbra / decay Math.PI / 5.2 · 0.22 · 1.2 🔒 Sabit
Spot gölge kamera near 0.25 · far 38 · shadow.focus 1 🔒 Sabit
GOOD_DEFAULT (gölge tabanı) mapSize / bias / normalBias 1024 · −0.0001 · 0.02 (ctor + HUD başlangıcı ile uyumlu) 🔄 Dinamik (HUD)
shadow.radius (shadow-map sayfası) Işık oluşturulurken ctor’da 2.8; bu sayfada applyShadowParams ile 1 (PCF dışı varyant) 🔒 Sabit (kod)
Dolgu ışıklar AmbientLight 0xd8e8ff, yoğunluk 0.06 🔒 Sabit
HemisphereLight Gökyüzü 0x7a9cbd · zemin 0x1a1418 · yoğunluk 0.22 🔒 Sabit
Renderer / kamera Ton / gölge ACESFilmicToneMapping, exposure ≈1.08 · PCFSoftShadowMap · arka plan 0x070a12 🔒 Sabit
PerspectiveCamera + OrbitControls FOV 48, clip 0.0880; başlangıç poz (4.6, 3.35, 5.9), hedef (0, 0.52, 0); zoom 3.813.5; pan kapalı 🔒 Sabit
Yardımcı çizimler Tel / çizgi Yönsel: sarı Line kaynak→hedef; Spot: koni LineSegments (DirectionalLightHelper / SpotLightHelper yok) 🔒 Sabit + HUD (görünürlük)
HUD başlangıç (shadow-map.html) Sürgü / seçim Harita 1024; bias adım −10−0.0001; normal bias adım 200.02; azimut 118°, yükseklik 38°, mesafe 14.5 (yönselde mesafe sürgüsü kapalı) 🔄 Dinamik (HUD)
Yönsel başlangıç konumu setLightPose (° + mesafe) Yaklaşık (10.08, 9.51, −5.36) = LIGHT_TARGET + küresel offset (118°, 38°, 14.5) 🔄 Dinamik (HUD)
// diagram-shadow-map.js — okuma sırası (GOLD_DEMO geometri tabloda)

// 1. Renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// 2. Işık kurulumu (yönsel örnek; Spot ctor farklı — tabloya bak)
const LIGHT_TARGET = new THREE.Vector3(0, 0.58, 0);
const light = new THREE.DirectionalLight(0xfff4ec, 1.42);
light.castShadow = true;
light.target.position.copy(LIGHT_TARGET);
scene.add(light);
scene.add(light.target);

// Spot dalı (özet): new THREE.SpotLight(0xfff4ec, 88, 30, Math.PI / 5.2, 0.22, 1.2)
// spot.target.position.set(0, 0.52, 0); scene.add(spot); scene.add(spot.target);

// 3. Gölge ayarı (önemli kısım)
light.shadow.mapSize.set(1024, 1024);
light.shadow.bias = -0.0001;
light.shadow.normalBias = 0.02;
// Bu sayfada Directional: applyShadowParams sonrası shadow.radius ≈ 1 (PCF dışı varyant).

Shadow map türleri (algoritmalar)

renderer.shadowMap.type seçimi, aynı haritada kenar yumuşatma ve artefakt dengesini değiştirir; sahne ölçeği ve hareket hızına göre farklı türler “kabul edilebilir” olur. Aşağıdaki tablo hızlı seçim içindir; her satırın ayrı sayfası ve demoları gölge alt klasöründe derinleşir.

Renderer shadowMap.type seçenekleri (özet)
Teknik Açıklama
BasicShadowMap En hızlısı; kenarlar keskin ve pikselli.
PCFShadowMap Çevre örneklemesiyle yumuşak kenar; çoğu projede güvenilir klasik seçenek. PCF sayfası ve demo.
PCFSoftShadowMap Daha geniş filtre hissi; düşük mapSize’ta bile genelde daha okunaklı.
VSMShadowMap Variance Shadow Map — çok yumuşak gölgeler; sahneye ve ayarlara duyarlı, artefakt riski farklıdır.

Önemli not ve Holodepth ipucu

Shadow map bir “hile”dir

Bu yöntem gerçek dünyadaki gibi fotonların sekerek dolanmasını (global illumination) simüle etmez; yalnızca “ışığa en yakın yüzey hangisi?” sorusunu tek ışın hattında derinlik kıyasıyla yaklaşıklar.

Holodepth pro ipucu

Sahnede on ışık varsa ve hepsinde castShadow açmak performansı hızla düşürür. Üretimde çoğu zaman yalnızca ana ışığın (güneş benzeri DirectionalLight veya ana SpotLight) gölgesini açmak, kare süresini korumanın en sağlam yollarından biridir.

Çoklu harita, dinamik çözünürlük ve bütçe tablosu için Gölge performansı optimizasyonu sayfasına geç; nokta ışığında geçiş sayısı planı için PointLight gölge maliyeti notuna bak.