holodepth

Three.js · Işık · Nokta kaynağı

PointLight: Evrenin küçük ampulü

Tek noktadan her yöne — uzaklaştıkça solar

Sahnenin ortasına asılmış bir ampul, yanan bir mum veya karanlıkta parlayan bir meşale hayal et. Işığın tek bir noktadan doğup her yöne — 360 derece — küresel biçimde yayıldığı o sıcak atmosferin mimarı PointLight’tır. Uzaklaştıkça zayıflayan doğasıyla sahneye en insani ve samimi derinlikten birini katar.

Bağlam: DirectionalLight (paralel güneş), AmbientLight, Materyal Giriş.

Zihin modeli: radyal enerji

Sonsuz bir duvar değil — merkezden dışarı pompalanan küre

  • Kaynak: Tek bir koordinat noktasıdır.
  • Yayılım: Radyal — merkezden her yöne eşit dağılır.
  • Zayıflama: Kaynaktan uzaklaştıkça şiddet doğal olarak azalır.
  • Amaç: Yerel aydınlatma, iç mekân lambaları, dinamik efektler (patlama, ateş vb.).

İzotrop küre: her yön “eşit hak”

PointLight ideal modelde tam simetrik bir kaynaktır: aynı mesafedeki tüm yüzeylere aynı enerji düşer; yön seçmez. Bu, güneş gibi uzaktaki paralel ışınlardan farklıdır ve SpotLight gibi koniyi kesen bir hacim değildir. Bu yüzden “nerede parlıyor?” sorusunun cevabı neredeyse yalnızca mesafe ve sönüm ile verilir — sahne ölçeğini birim başına tutarlı tutmak kritik hale gelir.

Ters kare yasası sezgisi

Fiziksel düşünceyle hizalı varsayılan decay = 2, parlaklığın mesafeyle kabaca mesafe karesinin tersiyle azalmasıdır. Pratikte bu, yakına koyduğun küçük bir ışığın bile hızla “ateş topu” gibi davranabileceği anlamına gelir; bu yüzden intensity ve distance’yi birlikte kalibre etmek, tek parametreyle “düzeltmeye” çalışmaktan daha az sinir bozucudur.

Mesafe ve sönümlenme (distance, decay)

PointLight’ı diğerlerinden ayıran kritik çift: distance ve decay. Işığın ne kadar uzağa “gideceğini” ve parlaklığın mesafeyle ne hızla düşeceğini bunlar belirler.

  • distance (mesafe): Etkinin sıfıra yaklaştığı sınır. 0 ise teorik olarak sınırsız menzil; yine de decay yüzünden pratikte hızla zayıflar.
  • decay (sönümlenme): Mesafeyle parlaklık kaybının hızı. Çoğu sahne için 2 (ters kare) uygundur. Interaktif demoda da 2 kullanılır: çok yüksek decay ile uzak nesneler anında kararır ve distance sürgüsü “çalışmıyor” sanılır — oysa baskın etki genelde ters-mesafe sönümüdür; sahne ölçeği ve decay birlikte düşünülmelidir.

Birim ölçeği ve “0 distance”

Three.js dünyasında bir birim neyi temsil ettiğini (metre mi, oyun kutusu mu) ekip içi sabitlediğinde, aynı distance değeri farklı projelerde tamamen farklı okunur. distance’yi 0 bırakmak teorik olarak sınırsız menzil verir; yine de decay yüzünden pratikte hızla zayıflarsın — GPU ve ışık toplama açısından çoğu ekip yine de makul bir üst sınır koymayı tercih eder. Bölüm 4’teki masa lambasında distance sürgüsü 12–48 iken sahneye düz 3.8–34 aralığına yeniden eşlenir — nesneler ışığa birkaç birim yakın olduğu için, aksi halde kesim hep “uzakta” kalır ve sürgü yalnızca decay düşüşü gibi görünür.

PhysicallyCorrectLights ve fiziksel ölçek

Eski Three.js sürümlerinde renderer.physicallyCorrectLights = true ile aynı fikir açılır: intensity daha çok candela benzeri bir ölçeğe yaklaşır ve decay = 2 fiziksel olarak beklenen taban olur. Güncel sürümlerde bu bayrak kaldırılmış / davranış varsayılanlarla birleşmiş olabilir — projende WebGLRenderer ve ışık birimleri için resmi migration notlarına bak.

Distance: yapay kesim

Gerçek dünyada ışık “bir mesafede kesilmez”; yalnızca zayıflar. Three.js’teki distance üst sınırı ise performans ve kontrol için yapay kesimdir — ötesini tamamen sıfırlar; bu yüzden değeri “fiziksel menzil” sanmamak gerekir.

Sönüm (kısa çekirdek)

Mesafe kesimi dışında, sezgisel sönüm kabaca 1 / pow(d, decay) düşüncesine indirgenir (d yüzey–kaynak uzaklığı). Kısa yazım:
attenuation ≈ 1.0 / pow(distance, decay) — gerçek shader’da mesafe kesimi ve materyal terimleri bu çekirdeği çarpar.

Görsel gözlem: sahneyi nasıl etkiler?

  • Odaklı aydınlık: Bulunduğu bölgeyi öne çıkarır; geri kalanı karanlıkta bırakarak gizem verir.
  • Dinamik gölgeler: Gölge yönü ışığa göre değişir; DirectionalLight’taki gibi tek sabit yön yoktur; nesne ışığın etrafında döndükçe gölge de takip eder.
  • Hacimsel derinlik: Nesne ışığa yaklaştıkça parlar, uzaklaştıkça solar — mesafe sezgisel olarak okunur.

Speküler dilim ve materyal

MeshStandardMaterial ile hafif metalness, ışığın uzaydaki konumunu küçük bir parlak yansıma dilimi olarak gösterir; bu dilim hareketli kamera veya hareketli ampulde canlılık katar. Tam mat Lambert yüzeylerde nokta ışık hâlâ çalışır fakat “ışık nerede?” sorusu daha çok gölge ve ton farkıyla cevaplanır — bölüm 4’teki masa lambasında hafif metalness bu soruyu spekülerle de yanıtlar.

İnteraktif: masa lambası, speküler ve bloom

Bu sayfadaki tek canlı demo masa lambası sahnesidir: koyu oda zemininde masa üstü, üzerinde ayak + sıcak PointLight (decay 2), görünür ampul (yüksek segmentli küreler; çekirdek + sıcak orta + additive halo). Masada bardak (silindir), lamba yakınında küçük küre, uzak köşede kutu — böylece aynı ışıkta yakın parlak / uzak sönük ve farklı gölge yönleri okunur. Materyallerde metalness0.2, roughness0.3 bandı spekülerle ışığın yerini hissettirir. Zemin biraz daha açık ve daha düşük roughness ile sıcak havuz zeminde netleşir; AmbientLight0.052 tam siyah köşeleri yumuşatır. UnrealBloomPass (addons) ampul ve parlak yüzeylerde “enerji” verir — üretimde maliyet / artefakt için dikkatle ayarlanır; burada öğretim amaçlı orta güçlü bir profil kullanılır.

İsteğe bağlı ses: aynı bölümde, demo kutusundan ayrı sütun genişliğinde şerit — dosya adı Point-Light-Demo (uzantı sırasıyla denenir).

Masa lambası · PointLight + UnrealBloomPass

Sürgüler: menzil (12–48 → sahne 3.8–34) ve parlaklık. Küre lamba dibinde parlak; kutu uzakta daha soğuk. Ampul küreleri 48×48 segment; halo additive. Bloom parlak piksellere; mobilde ek GPU maliyeti olabilir. PCFSoftShadowMap.

Uygulama: dinamik bir ampul kurulumu

Aşağıdaki örnek, bölüm 4’teki masa lambası demosunun çekirdeğini özetler (diagram-point-light.jsinitPointLampDemo, attachBulbVisual, EffectComposer + UnrealBloomPass). Tablo ön harita olarak hangi sabitin nerede durduğunu gösterir; kod bloğu başlangıç noktası olarak kopyalanabilir.

Bloom gücü ve eşiği sahneye göre ayarlanır: eşik çok düşükse tüm sahne “yıkanır”; çok yüksekse ampul parlamaz. Önce distance / decay ile hacmi oturt, sonra bloom parametrelerini ince ayarla.

Ön harita tablosunu nasıl kullanırsın?

Satırlar sırasıyla PointLight, ampul görselleri, dolgu, zemin / nesne materyali özeti ve post-processing satırlarını listeler. Yönsel güneş karşılaştırması için DirectionalLight sayfasına bak.

diagram-point-light.js — Masa lambası + bloom
Sahne / rol Parametre Değer
Üst sabitler POINT_COLOR / POINT_DECAY 0xffb078 · 2
Menzil eşlemesi DIST_SLIDER_LO/HI = 12 / 48; LAMP_DISTANCE_MIN/MAX = 3.8 / 34; sliderToLampDistance = bu uçlar arası lineer lerp
BULB_SPHERE_SEG 48 — ampul küreleri widthSegments / heightSegments
glossyMat varsayılan roughness 0.3, metalness 0.2 (parametre ile ezilir)
PointLight (lampLight) color · decay POINT_COLOR · POINT_DECAY
position (-0.88, 1.35, -0.2)lampX/lampZ + tableTopY + ayak yüksekliği
Kurucu (ilk atama) new THREE.PointLight(…, 3.65, sliderToLampDistance(28), 2) (~ 17.2) — ilk setSize’ta applyControls yine yazar
distance (HUD) sliderToLampDistance(clamp(sürgü, DIST_SLIDER_LO, DIST_SLIDER_HI))3.8–34.0
intensity (HUD) t = intensity% / 100, t ∈ [0.08, 1] → 2.05 + t * 2.65 → ≈2.26–4.70
Gölge mapSize 1024; bias −0.00005; normalBias 0.035; kamera near 0.12 / far 42
Ampul mesh attachBulbVisual — tüm parçalar castShadow / receiveShadow kapalı
Ampul geometri Halo Yarıçap 0.34, opacity 0.26, AdditiveBlending, toneMapped: false, scale 1.3
Orta küre Yarıçap 0.16; renk 0xffdcb8; emissive 0xff9a4a, emissiveIntensity 3.2; toneMapped: false
Çekirdek Yarıçap 0.07; emissive 0xfff4e6, emissiveIntensity 8; toneMapped: false
Kamera PerspectiveCamera FOV 40, near 0.1 far 80; pozisyon (2.62, 1.48, 3.62); lookAt(-0.02, 0.84, -0.02)
Sahne background 0x070b14
Renderer Ton / temiz renk / gölge setClearColor(0x060910); ACESFilmicToneMapping, exposure 1.02; SRGBColorSpace; PCFSoftShadowMap
Zemin Düzlem + materyal PlaneGeometry(24, 24); renk 0x1e2a38, roughness 0.62, metalness 0.06
Masa + ayak tableTopY / kutular tableTopY = 0.72; masa Box(2.9, 0.12, 1.72)glossyMat(0x4a433a, 0.55, 0.12); taban 0x2a2218 (0.5, 0.18); direk 0x342a20 (0.48, 0.16)
Nesneler Bardak · küre · kutu Silindir radialSegments 28; küre (40, 32); kutu 0.24×0.2×0.22; speküler bant roughness 0.28–0.32, metalness 0.2
Dolgu AmbientLight + HemisphereLight 0xa8b8e8 · 0.052; gök 0x203048 / yer 0x080a10 · 0.09
Post UnrealBloomPass strength 0.48, radius 0.38, threshold 0.12; çözünürlük Vector2(floor(w·pr), floor(h·pr)), pr = getPixelRatio()
Zincir + boyut RenderPassUnrealBloomPassOutputPass; composer.setSize(w, h, false) + setPixelRatio(pr); yeniden boyutta composer.dispose()
// diagram-point-light.js ile uyumlu özet (three + addons)
import * as THREE from "three";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";

const POINT_COLOR = 0xffb078;
const POINT_DECAY = 2;
const DIST_SLIDER_LO = 12;
const DIST_SLIDER_HI = 48;
const LAMP_DISTANCE_MIN = 3.8;
const LAMP_DISTANCE_MAX = 34;
const BULB_SPHERE_SEG = 48;

function sliderToLampDistance(s) {
  const t = THREE.MathUtils.clamp(s, DIST_SLIDER_LO, DIST_SLIDER_HI);
  const u = (t - DIST_SLIDER_LO) / (DIST_SLIDER_HI - DIST_SLIDER_LO);
  return LAMP_DISTANCE_MIN + u * (LAMP_DISTANCE_MAX - LAMP_DISTANCE_MIN);
}

const lampLight = new THREE.PointLight(POINT_COLOR, 3.65, sliderToLampDistance(28), POINT_DECAY);
lampLight.position.set(-0.88, 1.35, -0.2);
lampLight.castShadow = true;
lampLight.shadow.mapSize.set(1024, 1024);
lampLight.shadow.bias = -0.00005;
lampLight.shadow.normalBias = 0.035;
lampLight.shadow.camera.near = 0.12;
lampLight.shadow.camera.far = 42;
scene.add(lampLight);
// attachBulbVisual(lampLight) — demoda: halo (48×48, AdditiveBlending, scale 1.3) + orta + çekirdek

// HUD (demodaki applyControls):
// lampLight.distance = sliderToLampDistance(Number(distanceSlider.value));
// const t = Math.max(0.08, Math.min(1, Number(intensitySlider.value) / 100));
// lampLight.intensity = 2.05 + t * 2.65;

const pr = renderer.getPixelRatio();
const composer = new EffectComposer(renderer);
composer.setPixelRatio(pr);
composer.setSize(width, height, false);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(
  new UnrealBloomPass(
    new THREE.Vector2(Math.max(1, Math.floor(width * pr)), Math.max(1, Math.floor(height * pr))),
    0.48,
    0.38,
    0.12
  )
);
composer.addPass(new OutputPass());
// composer.render();

API tarafında hatırlatma

new THREE.PointLight(color, intensity, distance, decay) kurucusu dört parametreyi bir arada alır; çalışma anında intensity ve distance’yi animasyonla oynamak titreyen lamba veya patlama efekti için yaygındır. Gölge açıkken shadow.mapSize ve bias değerleri, küre harita maliyetiyle birlikte düşünülmelidir — her ekstra gölgeli nokta ışığı, mobilde hissedilir biçimde büyür.

Yaygın hata: objeyi ışığın içine hapsetmek

Işığı mesh’in tam merkezine koymak

Hata: PointLight tek noktadan yayılır; kaynağı bir modelin (ampul camı vb.) içine gömersen ışınlar yüzeylerin içinde kalır, sahne beklediğin gibi aydınlanmaz — ışık “kaybolmuş” gibi görünür.

Çözüm: Işığı fiziksel modelin birkaç birim önünde veya dış yüzeyine yakın, boş alanda konumlandır.

Aynı tuzak, “ışığı karakterin göğsünden yürütelim” denildiğinde de çıkar: kamera içinden geçen görünmez bir nokta ışığı sahneyi yıkar, yüzey normalleri ışığa göre terslenebilir. Çözüm çoğu zaman ışığı kol hizası dışına, tavana yakın bir asılı noktaya veya lamba geometrisinin hemen üstüne taşımaktır.

Holodepth ilkesi

Yakın dramatik, uzak etkisiz

PointLight yakın mesafede dramatik, uzakta etkisiz kalır. Onu genel sahne aydınlatması değil, yerel efekt ve vurgu aracı olarak düşün.

Holodepth çizgisinde “bir ampul ekledim, tüm harita aydınlandı” beklentisi genelde hatalıdır; doğru tablo, DirectionalLight veya geniş dolgu ile tabanı kurup nokta ışığı sıcaklık ve hikâye için kullanmaktır. Bir sonraki adımda ışığın hacmini konik olarak kesen SpotLight ile kontrol artar.

Birden fazla PointLight üst üste: katkılar toplandığı için sahne hızlıca overbright (yıkanmış / pastelleşmiş) görünür — her yeni ampulü “biraz daha parlatalım” diye eklemek yerine, önce mevcutların intensity ve menzilini gözden geçirmek daha güvenlidir.

Production notu: gölge maliyeti

PointLight gölgeli kullanımda GPU açısından pahalıdır: DirectionalLight tek yöne gölge haritası üretirken, nokta ışığı her yöne yayılır; gölge için genelde küp harita (altı yön) gerekir. Kabaca sayıyla: tipik nokta gölgesi 6 yüz (küp) için ayrı derinlik geçişi anlamına gelir; yönsel güneş ise aynı bağlamda tek ortografik harita yönüne indirgenir (maliyet algısı: “6 vs 1”).

Aynı anda çok sayıda gölgeli PointLight performansı ciddi düşürür. Yalnız kritik ışıklarda castShadow = true kullan; diğerlerini dolgu / ışık rengi için gölgesiz bırakmayı düşün.

Özet kutusundan sıradaki dosyaya geçerek konik ışık ve penumbra düşüncesine devam edebilirsin; burada kritik mesafe, nokta ışığın her yöne maliyet taşımasıdır — aynı dramayı daha ucuz taşımak için bazen tek yönsel + zayıf dolgu daha iyi bir başlangıç olur.