holodepth

Three.js · Renderer

Renderer: piksel işleme merkezi

Three.js'te ekrana görüntü basan katman WebGLRenderer'dır (veya alternatif renderer sınıfları). Ağır iş CPU'da değil GPU'da yürür; her karede render(scene, camera) ile o anki sahne ve kamera durumu okunur, piksel tamponuna yazılır ve sonuç <canvas> üzerinden sayfaya taşınır. Renderer sahne grafiğini güncellemez; yalnızca çıktı üretir.

Holodepth üçlüsünde roller birbirini tamamlar: sahne yapıyı tutar, kamera hangi kesitin çizileceğini seçer, renderer bu kesiti GPU ile işler. Bu metinde üçlünün çıktı ve yürütme ayağını okuyorsunuz; sahne grafiği ve kamera matrisleri kardeş sayfalarda kalır.

Aşağıda render hattının özeti, requestAnimationFrame döngüsü, setSize / pixel ratio, shadowMap bayrakları, API tablosu ve tam kurulum örneği var; ışık türleri, materyal modelleri veya scene.traverse burada tekrar anlatılmaz.

WebGLRenderer ve Görüntüleme Hattı (Render Pipeline)

WebGLRenderer ve GPU Donanımı

Three.js ekosisteminde WebGLRenderer, matematiksel matrisleri (sahne, modeller, kamera açıları) ekranda görebileceğimiz piksellere dönüştüren motor (yürütücü katman) olarak görev yapar. Bu sınıf, grafik kartınızın (GPU) gücünü web tarayıcısına taşıyarak işlemleri gerçekleştirir ve üretilen nihai görüntüyü bir HTML <canvas> elemanı olan .domElement özelliği üzerinden sayfaya (DOM) enjekte eder.

Geleneksel web geliştirme süreçlerinden farklı olarak, 3D grafiklerin çizimi iki kritik donanım felsefesi üzerine inşa edilmiştir:

  • Paralel Hesaplama Performansı: Standart JavaScript (CPU üzerinde) işlemleri sırayla (tek şeritli bir yol gibi) yürütür. GPU ise donanımsal olarak binlerce küçük çekirdekten oluşan devasa çok şeritli bir otoyol gibidir. Ekranda gördüğünüz sahnenin her bir pikseli için (örneğin 1080p ekranda 2 milyondan fazla piksel) ışık, materyal ve derinlik hesabı yapılması gerekir. Bu devasa veri yükü JavaScript ile "tek tek" boyanamayacağı için WebGLRenderer bu işleri GPU çekirdeklerine dağıtır ve aynı anda (paralel olarak) hesaplatır.
  • Donanım Hızlandırması (Hardware Acceleration): WebGLRenderer, canvas üzerinde düşük seviyeli bir WebGL Bağlamı (Context) açar ve çizim komutlarını tarayıcı aradan çekilecek şekilde doğrudan GPU sürücülerine iletir. HTML Canvas 2D API'sinde olduğu gibi her karede fillRect() fonksiyonu çağırmazsınız. Akıcı bir performans (60 FPS), bu donanım hattının sürekli ve kesintisiz beslenmesiyle mümkündür.

Sistemin asgari düzeyde nasıl çalıştığını görmek için Sahne girişindeki tam örneği inceleyebilir; 2D çizim metodolojisi ile 3D WebGL mimarisi arasındaki yapısal farklar için Canvas vs WebGL dokümanına başvurabilirsiniz.

Salt Okunur (Read-Only) Mimari: Renderer Sahneyi Değiştirmez

Three.js mimarisini kavrarken yapılan en yaygın hata, işleyicinin (Renderer) dünyadaki fiziksel nesneleri hareket ettirdiğini düşünmektir. WebGLRenderer kesinlikle sahne grafiğini (Scene Graph) veya nesnelerin durumunu (State) değiştirmez.

Renderer, bir fotoğraf makinesinin deklanşörü gibi çalışır. Sadece o anki deklanşör anında; Scene nesnesinin içindeki koordinatları (kim nerede duruyor?) ve Camera nesnesinin bakış açısını (nereden bakılıyor?) salt okunur (read-only) olarak alır. Bu iki veri paketini birleştirerek bir 2D görüntü (fotoğraf) üretir. Animasyonlu bir nesneyi döndürmek istiyorsanız, bu işlemi Renderer'da değil, doğrudan nesnenin kendi özellikleri (örn: mesh.rotation.y) üzerinden yapmalısınız.

Bir Karenin (Frame) Anatomisi: İşlem Boru Hattı

Animasyon döngünüzün içindeki renderer.render(scene, camera) satırı her saniye 60 kez (60 FPS) çağrıldığında, donanım seviyesinde sırasıyla şu grafik boru hattı (Graphics Pipeline) işletilir:

  • 1. Ayıklama (Culling): Kameranın görüş açısından (Frustum) çıkan nesneler tespit edilir ve bu kare için işlem sırasından düşürülür.
  • 2. Matris Çözümleme (Transform): Ekranda kalan her bir nesnenin 3D uzaydaki X, Y, Z koordinatları, kameranın projeksiyon matrisi ile çarpılarak 2D ekran (piksel) koordinatlarına çevrilir.
  • 3. Vertex Shader & Işıklandırma: Geometrilerin köşe noktaları hesaplanır. Eğer sahnede ışıklar varsa (PBR), nesnelerin kameraya olan açılarına göre gölge ve aydınlanma değerleri GPU üzerinde saptanır.
  • 4. Rasterizasyon (Fragment Shader): Matematiksel alanlar, ekrandaki piksellere (kırmızı, yeşil, mavi renk değerlerine) dönüştürülür.
  • 5. Derinlik (Z-Buffer) ve Yazma: Hangi pikselin önde, hangisinin arkada kaldığı (Z-Index) hesaplanır, varsa saydamlık (blending) harmanlanır ve nihai sonuç ekrandaki <canvas> elementine fırlatılır.
Görüntü İşleme Boru Hattı (Render Pipeline) Özeti
Sahne + Kamera
(Matris Verisi)
Görünürlük
(Frustum Culling)
Işık & Materyal
(Vertex Shader)
Rasterizasyon
(Fragment Shader)
Z-Buffer & Blending
(Derinlik ve Saydamlık)
Canvas
(Nihai Çıktı)

Yukarıdaki şema, renderer.render() fonksiyonunun çağrıldığı o milisaniyelik zaman diliminde ekran kartının (GPU) arka planda yürüttüğü matematiksel sırayı temsil eder. Ham koordinat verisi önce filtrelenir (Görünürlük), ardından donanım seviyesinde ışıkla harmanlanır ve en son tarayıcının anlayabileceği 2D piksellere (Rasterizasyon) dönüştürülerek ekrana basılır.

Render Döngüsü (Animation Loop): Kalp Atışı

Sahneyi ve kamerayı kurup işleyiciyi (Renderer) hazırlamak, fotoğraf makinesini tripoda bağlamak gibidir; deklanşöre basılmadığı sürece ekranda hiçbir şey göremezsiniz. Eğer uygulamanız statik bir mimari görselleştirme değilse (yani dönen bir model, karakter animasyonu veya fare etkileşimi içeriyorsa), motorun sürekli olarak yeni kareler (frame) çizmesi gerekir.

Bunu JavaScript'te while veya setInterval gibi tehlikeli, işlemciyi kilitleyen döngülerle yapamayız. Bunun yerine, tarayıcının ekran yenileme hızına (genellikle saniyede 60 kez) doğrudan senkronize çalışan, performansı mükemmel şekilde optimize eden yerleşik requestAnimationFrame metodunu kullanırız. Bu, Three.js uygulamanızın kalp atışıdır.

function animate() {
    // Tarayıcıya "bir sonraki boş karede bu fonksiyonu tekrar çalıştır" emrini verir.
    // Sekme arkaplana atıldığında veya ekran kapandığında otomatik olarak duraklayarak pili korur.
    requestAnimationFrame(animate);

    // Varsa animasyon mantıkları buraya yazılır
    // (Örn: cube.rotation.y += 0.01)

    // O anki güncel durumun fotoğrafını çek ve canvas'a bas
    renderer.render(scene, camera);
}

// Kalp atışını ilk kez başlatır
animate();

Yukarıdaki blokta scene ve camera değişkenlerinin önceden tanımlanmış olduğu varsayılır. Bu kısacık kod parçası, donuk bir 3D modeli alıp yaşayan, etkileşimli bir dünyaya dönüştüren temel mühendisliktir. Bu döngünün, WebGL motorunun piksel (pixel ratio) ve boyut ayarlamalarıyla birlikte nasıl entegre edildiğini görmek için sayfanın sonundaki Tam Kurulum ve Render Döngüsü bloğunu inceleyebilirsiniz. Zaman (Delta Time) bazlı gelişmiş animasyon hesaplamaları ise "Zaman ve Hareket" bölümünde ele alınacaktır.

Kritik kurulum parametreleri

new THREE.WebGLRenderer({ … }) satırındaki seçenekler, canvas ve WebGL bağlamının ilk an nasıl açılacağını belirler; çoğu seçenek sonradan değiştirilemez; gerekirse renderer yeniden oluşturulur. Görsel kalite, şeffaflık ve GPU tercihi burada; piksel oranı ve boyut bir sonraki alt başlıkta, gölge ve renk uzayı tam örnekte tamamlanır.

  • Antialias: Kenar ve eğimli yüzeylerde görülen tırtıklanmayı (aliasing) azaltır; çokgen kenarları daha yumuşak görünür. { antialias: true } ek GPU maliyeti getirir — mobilde pil ve ısınma için bazen false bırakılır; masaüstü ve vitrin sahnelerinde sık açık tutulur. Kapalıyken sonradan post-processing ile AA mümkün olabilir; burada yalnızca oluşturucu bayrağı anlatılır.
  • Alpha: alpha: true iken WebGL tamponu şeffaf olabilir; çizilmeyen veya yarı saydam piksellerde altta sayfanın CSS arka planı görünür. Tam opak sahne için genelde setClearColor (ve gerekirse alpha kanalı) ile birlikte düşünülür — bu, sahne background yaklaşımından farklı bir katmandır. Şeffaf canvas, 3B içeriği HTML/CSS katmanlarıyla kompozisyon için kullanılır (ör. sayfa düzeni üzerinde canvas, HUD).
  • powerPreference: Tarayıcıya hangi GPU profilini tercih ettiğinizi bildirir; garanti değildir — son söz tarayıcı ve işletim sistemindedir. Çift GPU'lu dizüstülerde "high-performance" bazen ayrık (dedicated) kartı seçtirir; "low-power" pil ömrünü korur. Yoğun sahnelerde high-performance yaygındır; ölçüm yapmadan her projede açmak şart değildir.
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true,
  powerPreference: 'high-performance',
});

Ekran Netliği: Piksel Yoğunluğu (Pixel Ratio) ve Boyutlandırma

WebGLRenderer'ın ürettiği görüntünün donanımsal olarak ne kadar keskin olacağını ve ekranda ne kadar fiziksel alan kaplayacağını belirleyen iki kritik yapılandırma aşaması vardır:

  • Piksel Yoğunluğu (setPixelRatio): Modern mobil cihazlar ve Retina (Yüksek DPI) monitörler, standart ekranlara kıyasla aynı alana çok daha fazla piksel sığdırır. Motorun işleme çözünürlüğünü ekranla eşleştirmezseniz 3D görüntünüz bulanık (çamurlu) çıkar. Ancak, çözünürlüğü kontrolsüzce artırmak GPU'nun hesaplama yükünü eksponansiyel olarak artırarak FPS düşüşüne yol açar. Bu darboğazı önlemek için endüstri standardı olan setPixelRatio(Math.min(window.devicePixelRatio, 2)) formülü kullanılır. Bu akıllı limit, cihazın doğal keskinliğini yakalarken performansı korumak adına çizim çarpanını maksimum 2 ile sınırlar.
  • Çizim Alanı (setSize): setSize() metodu, HTML <canvas> elementinin tarayıcı içindeki genişlik ve yükseklik değerlerini ayarlar. Tam ekran (fullscreen) bir deneyim yaratırken genellikle tarayıcı penceresinin güncel ölçüleri (window.innerWidth ve window.innerHeight) referans alınır. Kullanıcı pencere boyutunu değiştirdiğinde görüntünün sünmemesi veya pikselleşmemesi için, bu boyutların tarayıcının yerleşik resize olayında (event listener) sürekli güncellenmesi gerekir.

Gölgeler ve Performans Maliyeti

Gerçek zamanlı gölge hesaplaması (Shadow Mapping), WebGL hattındaki en pahalı işlemlerden biridir. Motor, sahneyi önce kameranın gözünden, ardından sahnede gölge atan her bir ışığın gözünden tekrar çizer. Bu nedenle renderer.shadowMap.enabled = true komutu projeye ciddi bir ek maliyet getirir. Optimizasyon kuralı şudur: Yalnızca gerçekten gölgeye ihtiyacı olan ana ışıkları (castShadow) ve gölgeyi üzerine alması gereken büyük yüzeyleri (zemin, duvar gibi receiveShadow) manuel olarak belirleyin.

Teknik Tablo: Renderer API Özeti

Metot / Özellik Kavramsal Açıklama Mimari Sorumluluk (Neden Önemli?)
.render(scene, camera) O anki sahneyi, belirtilen kameranın perspektifinden hedefe çizer. Sistemin kalbidir; requestAnimationFrame döngüsü içinde sürekli çağrılarak animasyonu var eder.
.domElement Renderer'ın arka planda oluşturduğu fiziksel <canvas> HTML öğesidir. 3D dünyayı normal web sayfasına (DOM'a) bağlamak (append) için köprü görevi görür.
.shadowMap Gölge haritası (Shadow Map) algoritmalarının yönetim objesidir. Gerçekçi sahne aydınlatması için .enabled = true yapılmalı ve uygun filtreleme (PCFSoft vb.) seçilmelidir.
.setClearColor() Her yeni kareden önce tuvalin (tamponun) hangi renkle temizleneceğini belirler. Eğer scene.background kullanılmıyorsa, evrenin temel arkaplan rengini oluşturmak için bir alternatiftir.
.outputColorSpace Çıktı renk uzayını (Color Space) tanımlar (ör. THREE.SRGBColorSpace). Fiziksel materyallerin (PBR) ve kaplamaların (Texture) monitörde soluk görünmemesi, doğru gamma değerleriyle ekrana basılması için kritiktir.

Tam Kurulum ve Render Döngüsü

Şimdiye kadar anlatılan WebGLRenderer ayarlarının (renk uzayı, piksel yoğunluğu, donanım seçimi ve gölge hesaplamaları) tek bir modern, üretime (production) hazır konfigürasyon bloğunda nasıl birleştiğini aşağıda görebilirsiniz.

// 1. Renderer Sınıfını Başlat ve Donanım Parametrelerini Gir
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true,
  powerPreference: 'high-performance'
});

// 2. Çözünürlük ve Ekran Boyutunu Ayarla
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

// 3. Renk Uzayı ve Fiziksel Işık/Gölge Ayarları
renderer.outputColorSpace = THREE.SRGBColorSpace; // Işıkların soluk görünmesini engeller
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Gölgelerin kenarlarını yumuşatır

// 4. Üretilen WebGL Canvas'ını HTML Sayfasına (DOM) Yerleştir
document.body.appendChild(renderer.domElement);

// 5. Animasyon Döngüsünü Kur (Scene ve Camera değişkenleri tanımlı kabul edilir)
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

Holodepth Mimari Notu: Kesin 16.6ms Bütçesi

Akıcı bir 3D deneyimi, saniyede 60 kare (60 FPS) hızında çalışmalıdır. Bu matematiği tersten kurduğumuzda, GPU ve CPU'nun ekranı silip, yeni ışıkları hesaplayıp, matrisleri çarpıp resmi çizmek için sahip olduğu süre tam olarak 16,6 milisaniyedir. Eğer sahnenize milyonlarca poligon (vertex) veya onlarca dinamik ışık eklerseniz, motor bu 16.6ms barajını aşar ve uygulama "kasmaya" (frame drop) başlar. Three.js'te ustalığın %80'i kod yazmak değil, görsel kaliteden ödün vermeden bu matematiksel işlem bütçesini (Draw Call ve Vertex limitlerini) kontrol altında tutabilmektir.