holodepth

Three.js · Gölge haritası · seri 2 / 4

PCFShadowMap: komşu örnekleme ile yumuşak kenar

PCFShadowMap (Percentage Closer Filtering), aynı derinlik haritasını okurken tek noktaya değil, komşu örneklere de bakıp sonucu oransal bir karışıma çevirir. Three.js tarafında WebGLRenderer.shadowMap.type için sık görülen varsayılanlardan biridir; yine de projede açıkça atamak sürüm farklarında netlik sağlar. Bu sayfadaki oynatıcı her zaman THREE.PCFShadowMap ile çalışır.

Önce gölge girişi, BasicShadowMap (sert eşik), ardından gölge haritası mantığı ve türler tablosu ile çerçeveyi tamamlayın; PCF’nin eklediği şeyin «fiziksel penumbra» değil, harita uzayında yumuşatma olduğunu böylece daha kolay ayırırsınız.

Aşağıdaki playground Technical · şehir ve Organic · eğriler arasında geçer: ilki kesişen gölgelerle gri tonları, ikincisi eğri yüzeylerle kenar okumasını öne çıkarır. light.shadow.radius ve mapSize birlikte oynatıldığında blokluluk ile bulanıklık arasındaki gerilim belirginleşir. Basic ile aynı sahneyi açıp kıyaslamak yerine her türe özel düzen kullanıldığından, iki sekmeyi yan yana tutarak «ikili test → yüzdesel karışım» farkını izlemeniz yeterlidir.

Teknik yapı: komşuya sor, ortalamayı al

Basic ile fark nerede başlar?

BasicShadowMap hedef noktada tek derinlik karşılaştırmasıyla «gölgede mi, değil mi?» ikilisine yakın sonuç üretirken; PCF aynı haritayı okurken çevreden birden fazla örnek alıp bunları birleştirir.

  • Örnekleme (sampling): Yalnızca tek texel değil, komşu bölgeler de derinlik testine dahil edilir.
  • Oransal sonuç: Örneğin komşu dört örnekten ikisi gölgede, ikisi aydınlıkta ise nokta tam siyah yerine ara gri tonlara yaklaşabilir.
  • Yumuşak kenar hissi: Bu yüzdesel yakınlık yaklaşımı, sert basamakları gradyana benzer bir geçişle yumuşatır.

Maliyet sezgisı: Basic’e göre ne eklenir?

PCF, aynı haritayı birden fazla kez örnekleyerek karar verir; bu, GPU’da ek dokunuş demektir — genelde hâlâ makul bir aralıktadır, fakat mobilde çok sayıda gölge veren ışık ve düşük mapSize bir aradayken fark hissedilir. Pratik kural: önce harita çözünürlüğünü ve frustum’u doğrula, sonra radius ile kenarı yumuşat; performans rehberi ile birlikte ölç.

Neden hâlâ “mükemmel” değil?

Harita kaba kaldığında

PCF kenarı yumuşatır; fakat gölge blokluluğu (düşük çözünürlükte karemsi yapı) tamamen kaybolmayabilir. shadow.mapSize çok düşükse, filtre yumuşatsa bile harita «kaba ızgara» hissi vermeye devam eder.

Gerçek dünyadaki penumbra (gölgenin mesafeye göre genişlemesi) ile karıştırmayın: PCF’deki yumuşaklık çoğu zaman ekran / harita uzayındaki bir yumuşatmadır; mesafe veya geometriye göre fiziksel olarak doğru dağılması gerekmez.

radius haritayı inceltmez

Yüksek shadow.radius kenarı bulanıklaştırır ama haritadaki texel sayısını artırmaz: bloklu bir 256 haritada radius’u şişirmek, “daha keskin silüet” yerine daha bulanık merdiven verir. İnce kenar istiyorsan önce mapSize ve gölge kutusu, sonra radius sırası daha az hayal kırıklığı yaratır.

Avantajlar: neden varsayılan tercih?

Pratikte ne kazanırsınız?

  • Daha okunaklı kenarlar: Basic’deki rahatsız edici aliasing büyük ölçüde azalır.
  • Geniş donanım uyumu: Çoğu mobil ve masaüstü GPU ile sorunsuz çalışır.
  • Dengeli maliyet: Gözle görülür kalite kazanımı sunarken PCFSoft / VSM gibi seçenekler kadar agresif olmayabilir (sürüm ve sahneye bağlıdır).

Üretimde “varsayılan PCF” ne zaman yeter?

Çoğu iç mekân ve ürün vitrininde, tek ana ışık + makul mapSize ile PCF, acne ayarı yapıldığında uzun süre yeterli kalır. Daha geniş yarı gölge veya hareket halinde bile stabil kenar istiyorsan sırada PCFSoftShadowMap veya VSM denemeleri vardır — maliyet ve artefakt profili farklıdır.

Kritik parametre: light.shadow.radius

Radius neyi genişletir, neyi genişletmez?

mapSize dışında PCF tarafında en çok işinize yarayan ayarlardan biridir: radius — komşu örneklerin ne kadar geniş bir alandan toplanacağını etkiler. Değeri artırdıkça kenar daha bulanık görünür; bu, gerçek bir yarı gölge değil, matematiksel yumuşatmadır — sınır çizgileri kabaca aynı ölçüde yumuşar; nesne–ışık mesafesine göre doğal penumbra beklemeyin.

Tuning: radius ile bias birlikte

Radius arttıkça yüzey acne’si bazen maske gibi görünür; bu durumda yalnız radius’u kesmek yerine bias / normalBias ile birlikte küçük adımlar ver. Kötü kombinasyonları bu sayfadaki Kötü ayar örneği kutusuyla güvenli biçimde gözlemleyebilirsin; Bias & shadow acne rehberi teşhis dili için kullan.

// Bu sayfadaki demo açıkça PCF seçer (yazmazsanız sürümünüzün varsayılanına bağlı kalırsınız)
renderer.shadowMap.type = THREE.PCFShadowMap;

// Yumuşaklık (örnek; sahneye göre ayarlayın — varsayılan genelde küçük değerdedir)
directionalLight.shadow.radius = 4;

Oynatıcı: iki sahne, aynı PCF çekirdeği

Hangi ipuçlarına odaklanmalısınız?

Technical · şehir kule yüksekliği ve ayak izini oynatarak kesişen gölgelerde gri ton birikimini gösterir; başlangıç ışığı daha yatay bir gökten uzun gölge okur. Organic · eğriler TorusKnot ve heliks ile kenar okumasını farklı bir yüzeyde dener. shadow.radius tüm sınırlara benzer bulanıklık ekler; haritayı 256 veya 512’ye indirip blokluluk ile PCF yumuşamasını üst üste izleyin. Spot ve ışık mesafesi, gerçek penumbra beklentisi ile ekran uzayı filtresini ayırt etmeye yarar. Kenarları vurgula Technical’de cadde kenarına, Organic’de knot yakınına yaklaşır. Amaç: «yumuşar ama fiziksel dağılmayı taklit etmek zorunda değildir» sınırını elle hissetmek.

PCFShadowMap · iki sahne
Sahne
Işık

Harita: 256 = blok + blur birlikte · 1024 = denge · 2048 = daha temiz kenar.

Sürükle: dönüş ve zoom (pan kapalı). Technical · şehir: kule örtüşmeleri gri geçiş üretir; zemin tonu gölgeleri ayırt etmek için biraz açıktır. Organic: knot ve heliks ayrık; zemin daha açık, formlar hafif parlak. Radius kenarı eşit bulanıklaştırır (fiziksel penumbra değildir). Işık yüksekliği / dönüşü yan ışık denemesi için. Kenarları vurgula Technical’de cadde, Organic’de düğüm yakını. Spot kapsamı (zemin) yalnız Spot’ta. Yönsel modda mesafe sürgüsü kapalıdır.

Kod yolu: diagram-shadow-map.js içinde data-shadow-variant="pcf" dalı; sahne radyosu Technical · şehir (PCF_EDGE_LAB) veya Organic · eğriler (PCF_ORGANIC) arasında seçilir. Aşağıdaki tablo bu varyantın sayısal haritasıdır — Tür sütunu HUD ile değişenleri dosyada kilitli kalanlardan ayırır; PCF sayfasına özgü ek sürgü shadow.radius satırlarında görünür.

Sabit doğrulama: tablodaki PCF_EDGE_LAB ve PCF_ORGANIC blokları, pcf varyantında buildPcfEdgeLabInto / buildPcfOrganicInto ile birebir; renderer (PCFShadowMap, exposure 1.02, arka plan 0x030305), dolgu ışıkları ve kamera başlangıcı da aynı dosyadaki pcf dalından gelir. HUD satırları «Dinamik» ile işaretlenir; Technical / Organic seçimi radyo ile sahneyi yeniden kurar.

HUD’dan hangi parametreler akıyor?

  • mapSize — çözünürlük; düşük değerde blokluluk ile PCF yumuşaması üst üste binir
  • shadow.radius — komşu örnekleme yarıçapı; kenar bulanıklığı (fiziksel penumbra değil). Artınca yayılım mesafesi değil, filtre genişliği hissi büyür
  • bias / normalBiasshadow acne ve yüzey kaydırması
  • Azimut ve yükseklik — ışın yönü; setLightPose ile güncellenir
  • Mesafe — yalnızca SpotLight için anlamlıdır; DirectionalLight paralel ışın kullandığından yönsel modda sürgü gizlenir
diagram-shadow-map.js: PCF varyantı, Technical / Organic sahne sabitleri — bu HTML oynatıcısı ile eşleşen değerler
Sahne / rol Parametre Değer Tür
Ortak · hedef LIGHT_TARGET (yönsel hedef) (0, 0.58, 0) 🔒 Sabit
SpotLight.target (spot dalı) (0, 0.52, 0)setLightPose 🔒 Sabit
PCF_EDGE_LAB · Technical · şehir Zemin groundSize 20 · 0x1a2330, roughness 0.9, metalness 0.035 🔒 Sabit
Kule ızgarası InstancedMesh birim BoxGeometry(1,1,1) · kule mat. 0xd8e6f4 · hücre 0.44 · yaklaşık 11×11 (merkez şerit / aralık atlamalı) · yükseklik 0.31.26 arası pseudo-rastgele 🔒 Sabit
Plaza BoxGeometry(2.35, 0.038, 2.35) · konum (0.04, 0.019, −0.08) · 0x8f9eb2 🔒 Sabit
focusRing Merkez (−0.92, 0.48), yarıçap 0.62 🔒 Sabit
focusOrbit Kamera (1.88, 1.02, 2.28) → hedef (−0.58, 0.035, 0.38), FOV 28, min/max 0.64 / 5.95 🔒 Sabit
PCF_ORGANIC · Organic · eğriler Zemin groundSize 20 · 0x242e3e, roughness 0.93, metalness 0.025 🔒 Sabit
TorusKnotGeometry + heliks TubeGeometry Knot: TorusKnotGeometry(0.38, 0.032, 118, 14, 2, 3), konum (−0.58, 0.5, −0.72) · tüp: 136 kesit, boru yarıçapı 0.024, 8 radial; heliks eğrisi 100 örnek, 4.4 tur 🔒 Sabit
focusRing Merkez (−0.35, −0.55), yarıçap 0.72 🔒 Sabit
focusOrbit Kamera (2.65, 1.32, 2.88) → hedef (−0.15, 0.045, −0.35), FOV 27, min/max 0.72 / 6.2 🔒 Sabit
Ana ışık (pcf yükleme) color + yoğunluk 0xffffff · yönsel 1.92 · Spot 108 🔒 Sabit (varyant)
DirectionalLight gölge kamerası (ortho) ±5.2 · near 0.8 · far 26 🔒 Sabit
SpotLight (makeSpot + pcf) Ctor + pcf renk / yoğunluk distance 30 · angle Math.PI / 5.2 · penumbra 0.22 · decay 1.2 · pcf’de color 0xffffff, intensity 108 🔒 Sabit
Spot gölge kamera near 0.25 · far 38 · shadow.focus 1 🔒 Sabit
Gölge tabanı + HUD mapSize / bias / normalBias Başlangıç 1024 · −0.0001 · 0.02 · “Kötü ayar”da BAD (512, 0, 0) 🔄 Dinamik (HUD)
shadow.radius PCF dalında HUD sürgüsü 010 (adım 0.25); başlangıç 2 · applyShadowParams ile clamp(0, 12) 🔄 Dinamik (HUD)
Sahne modeli data-shadow-pcf-model · varsayılan rods (Technical) 🔄 Dinamik (HUD)
Dolgu (pcf sahne) AmbientLight 0xffffff, yoğunluk 0.028 🔒 Sabit
HemisphereLight 0x8899aa / 0x06080c, yoğunluk 0.1 🔒 Sabit
Renderer / kamera (pcf) Ton / gölge / arka plan ACESFilmicToneMapping, exposure 1.02 · PCFShadowMap · 0x030305 🔒 Sabit
PerspectiveCamera + OrbitControls Başlangıç poz (5.35, 3.95, 6.55), bakış / hedef (0.08, 0.2, −0.18); zoom 3.813.5; pan kapalı 🔒 Sabit
Yardımcı çizimler Tel / grid / eksen Yönsel: sarı Line; Spot: koni LineSegments. Grid açıkken GridHelper(20, 40, 0x2a3848, 0x121820) 🔒 Sabit + HUD (görünürlük)
HUD başlangıç (pcf-shadow-map.html) Sürgü / seçim Harita 1024; radius 2; bias −10−0.0001; normal bias 200.02; azimut 104°, yükseklik 26°, mesafe 20 🔄 Dinamik (HUD)
Yönsel başlangıç konumu setLightPose Yaklaşık (17.44, 9.35, −4.35) = LIGHT_TARGET + offset (104°, 26°, 20) 🔄 Dinamik (HUD)
// diagram-shadow-map.js — PCF varyant okuma sırası (Technical / Organic geometri tabloda)
// 🔑 PCFShadowMap’te gölge okuması: shadowMap.type + light.shadow.mapSize + light.shadow.radius (+ bias / normalBias)

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

// 2. Işık kurulumu (pcf: beyaz + yoğunluk yükseltilir)
const LIGHT_TARGET = new THREE.Vector3(0, 0.58, 0);
const light = new THREE.DirectionalLight(0xffffff, 1.92);
light.castShadow = true;
light.target.position.copy(LIGHT_TARGET);
scene.add(light);
scene.add(light.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;
light.shadow.radius = 2; // PCF: komşu örnekleme — sahneye göre 0–12 aralığında ayarla

İleri seviye not

Yüzdesel filtre neyi sorar?

PCF, kabaca konuşmak için GPU’ya şu soruyu çoğaltır: «Bu noktanın çevresinde, derinlik testine göre ışık almayan örneklerin oranı nedir?» İsimdeki percentage (yüzdesel) buradan gelir: sonuç saf ikili değil, istatistiksel bir karışımdır. Bu sayfa ve tablo, bu fikrin sahne sabitleriyle somutlaşmış halini verir.

Aynı fikir, genel tür tablosundaki PCF satırı ile de köprülenebilir — bu sayfa o satırın çift sahne laboratuvarıdır.

Sırada ne var?

Bir sonraki basamakta PCFSoftShadowMap ve VSMShadowMap farklı çekirdek ve maliyet profilleri sunar. Hepsini tek bakışta Shadow map türleri tablosunda kıyaslayabilirsiniz.