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 dedecayyü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üksekdecayile uzak nesneler anında kararır vedistancesürgüsü “çalışmıyor” sanılır — oysa baskın etki genelde ters-mesafe sönümüdür; sahne ölçeği vedecaybirlikte 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 metalness ≈ 0.2,
roughness ≈ 0.3 bandı spekülerle ışığın yerini hissettirir.
Zemin biraz daha açık ve daha düşük roughness ile sıcak havuz zeminde netleşir;
AmbientLight ≈ 0.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).
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.js — initPointLampDemo,
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.
| 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 | RenderPass → UnrealBloomPass →
OutputPass; 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.