holodepth

3D Model Formatları · glTF / GLB · Geometri Verileri

Normal Verileri — glTF Yapısında Yüzey Yönü Nedir?

Index Buffer «bu noktalar nasıl bağlanıyor?» sorusunu yanıtladı. Bu sayfa bir sonraki soruya geçer: bu yüzey hangi yöne bakıyor? Vertex konumu şekli oluşturur; normal verisi ışığın o şekli nasıl okuyacağını belirler; ama normal, ışık değildir.

Holodepth geometri hattında üç soru art arda gelir: nerede? (POSITION) · nasıl bağlanıyor? (indices) · hangi yöne bakıyor? (NORMAL). Normal Data bu üçüncü katmandır: birim uzunluktaki yön vektörü (nx, ny, nz); koordinat taşımaz, mesafe ölçmez.

Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «Aynı POSITION ve aynı index ile iki farklı NORMAL tablosu, aynı geometrinin farklı okunmasına yol açar. Normal = yön; ışık sonradan bu yöne bakar.»

Bu sayfanın sınırı · komşu konularla ayrım

Bu sayfa yalnızca glTF NORMAL semantic attribute'unu ve format okuma hattını anlatır:

  • Normal nedir (yön vektörü, pozisyon değil)
  • Face normal vs vertex normal
  • Flat vs smooth shading — aynı geometri, farklı normal tablosu
  • glTF accessor zinciri, eksik normal, hard edge kavramı

Bilinçli olarak dışarıda bırakılan konular (başka sayfalarda):

Kısa ayrım: Normal Data (Format) «yüzey hangi yöne bakıyor, dosyada nasıl saklanır?» · Normal Map (Runtime) «doku ile mikro yön nasıl taklit edilir?» · Lighting «bu yöne ışık nasıl çarpıyor?»

Normal Nedir?

Çoğu eğitim normal'i doğrudan «ışık hesabı» ile tanıtır; sanki parlaklık üreten ikinci bir ışık kaynağıymış gibi. Holodepth'te sıra farklıdır: önce geometri hattında üçüncü soruya gelirsiniz; şekil zaten var, bağlantı zaten kurulmuş; şimdi bu yüzey hangi yöne bakıyor? Bu bölümün giriş cevabı kısadır: normal, uzayda yön gösteren birim vektördür; ışık değildir.

Yeni başlayanların en sık yaptığı karışıklık şudur: «Normal = ışık». Hayır. Normal parlaklık üretmez, gölge atmaz, renk taşımaz. Işık modeli (ambient, directional, PBR…) çalışma anında bu yöne bakar; «ışın bu yüzeye ne kadar dik geliyor?» diye sorar; cevabı parlaklığa çevirir. Ama o soru, normal tanımlandıktan sonra gelir. Pipeline sırası format okuyucusu için şöyledir:

1. POSITION   → köşe nerede?
2. indices     → köşeler nasıl üçgene bağlandı?
3. NORMAL      → yüzey hangi yöne bakıyor?     ← bu sayfa
4. (sonra)     → ışık / materyal bu yöne bakar  ← Lighting sayfaları

Index Buffer «nasıl bağlanıyor?» sorusunu kapattı: zemin dörtgeni iki üçgene derlendi, paylaşımlı köşeler okundu. Bu sayfa aynı mesh üzerinde devam eder; koordinatları veya index listesini yeniden tanımlamaz. Soru değişir: derlenmiş yüzey dışa hangi yöne bakıyor?

Üç soru · üç semantic

Geometri Verileri ağacında her katman tek bir soruya cevap verir; karışıklık çoğu zaman bu soruları birbirine katmaktan doğar:

Holodepth geometri hattı — soru zinciri
Sayfa Semantic Soru
Vertex Data POSITION Bu nokta nerede?
Index Buffer indices Bu noktalar nasıl bağlanıyor?
Normal Data (bu sayfa) NORMAL Bu yüzey hangi yöne bakıyor?

Vertex konumu şekli oluşturur. Normal verisi, ışığın o şekli nasıl okuyacağını belirleyen yön tablosudur; ışığın kendisi değil.

Normal ne değildir?

§1 kapsamında netleştirmek, sonraki bölümlerde tekrarı önler. Normal şunların hiçbiri değildir:

  • Koordinat değil — vertex'i uzayda taşımaz, ölçeklemez, döndürmez (POSITION işidir)
  • Bağlantı tablosu değil — hangi köşelerin üçgende birleşeceğini söylemez (indices işidir)
  • Işık kaynağı değil — parlaklık, renk veya gölge üretmez (Lighting / materyal katmanı)
  • Normal map dokusu değil — fragment'ta mikro pürüz taklidi ayrı kanaldır (Three.js · Normal Map)

Normal yalnızca yüzey yönü taşır: «bu noktada / bu yüzde dış dünya hangi tarafa bakıyor?» Format dosyasında bu, vertex başına (veya yüz başına türetilerek) yazılan (nx, ny, nz) float üçlüsüdür.

Okuma katmanı — aynı şekil, farklı his

Geometri hattında normal, şeklin okunma biçimini kodlar. İki farklı durumu ayırın:

  • Farklı poligon yoğunluğu — düşük poligonlu küre ile yüksek poligonlu küre farklı POSITION dağılımlarıdır; bu Vertex Data + index konusudur.
  • Aynı üçgen ağı, farklı normal tablosu — vertex sayısı ve index aynı kalır; yalnızca NORMAL accessor değişir; yüzey daha düz (facet'li) veya daha yumuşak okunur (§4).

Bu sayfanın odak noktası ikincisidir. İlk kez burada «geometri aynı kalsa bile görünüm değişebilir» fikri ortaya çıkar; sebep konum veya bağlantı değil, yön verisidir.

Pozisyon değil · yön

Vertex Data §2 POSITION'ın (x, y, z) koordinat taşıdığını öğretti. NORMAL aynı accessor zincirinde (Buffer → BufferView → Accessor) yaşar; byte düzeni çoğu zaman yine VEC3 + FLOAT görünür. Tehlike burada gizlidir: binary okuyucu «üç float arka arkaya» görür; anlamı attribute adı verir:

POSITION vs NORMAL (semantic ayrımı)
Semantic Ne taşır? Birim Soru
POSITION Konum (x, y, z) Uzunluk (metre, birim) Bu nokta nerede?
NORMAL Yön (nx, ny, nz) Birim vektör (uzunluk ≈ 1) Bu yüzey hangi yöne bakıyor?

Aynı primitive içinde POSITION[i] ile NORMAL[i] aynı vertex indeksine karşılık gelir; biri «i. köşe nerede», diğeri «i. köşede yüzey hangi yöne bakıyor» der. Index buffer bu eşleşmeyi bozmaz; üçgen derlerken hem POSITION hem NORMAL aynı indeksle okunur (§5).

Birim vektör — uzunluk neden ≈ 1?

Normal pratikte normalize edilmiş yön vektörüdür: uzunluğu 1'e yakındır (glTF'de (nx, ny, nz) ile saklanır). Neden? Işık ve materyal hesapları yönün açısını kullanır; «ne kadar uzun» olduğu anlamsızdır; yalnızca hangi tarafa baktığı önemlidir. Dosyada normalize olmayan üçlü bulunursa loader veya motor düzeltir; format okuyucusu için niyet: yön bilgisi, mesafe bilgisi değil.

Örnek (yukarı bakan yüzey):
  n = (0, 1, 0)     → uzunluk = 1, +Y yönü

Yanlış yorum:
  n = (0, 5, 0)     → hâlâ «yukarı» ama birim değil; normalize → (0, 1, 0)

Üçgenden normal — cross product

Teorik olarak her düz yüzeyin bir «dik yönü» vardır. Üç köşesi bilinen bir üçgende bu yön, iki kenar vektörünün çapraz çarpımı (cross product) ile üretilir. Öğretim için zemin örneğindeki ilk üçgeni kullanalım; köşeler Vertex Field / Mesh Compiler ile aynı:

Köşeler (POSITION):
  V0 = (−1, 0,  1)
  V1 = ( 1, 0,  1)
  V2 = ( 1, 0, −1)

Kenar vektörleri (V0'dan):
  e1 = V1 − V0
  e2 = V2 − V0

Face normal (normalize edilmiş):
  n = normalize( e1 × e2 )

Sayısal olarak (zemin üçgeni, sağ el kuralı):

e1 = V1 − V0 = (2, 0, 0)
e2 = V2 − V0 = (2, 0, −2)

e1 × e2 = (0, 4, 0)   → normalize → (0, 1, 0)

Sonuç vektör üçgen düzlemine diktir; bu örnekte model uzayında «yukarı» (+y) yönünü gösterir. Index buffer bu üçgeni [0, 1, 2] ile derler (Index Buffer §3). Winding sırası değişirse (0, 2, 1) çapraz çarpımın işareti tersine döner; aynı düzlem, karşı «dış yüz» yönü (Index Buffer §4). Winding bu sayfada tekrar anlatılmaz; normal için çıkarım: yön, üçgen geometrisinden türetilir; index sırası işareti etkiler.

Face normal (yüz başına tek yön) ve vertex normal (köşe başına yön) bu türetmenin dosyaya nasıl yazıldığıdır; §2 ve §3. §1'de yalnızca ortak tanım kalır: normal, düzleme dik birim vektördür.

        ↑ n (face normal)
        │
        │   V1 ─────── V2
        │    ╲         ╱
        │     ╲       ╱
        │      ╲     ╱
        │       ╲   ╱
        │        V0
        └────────────────── y=0 düzlemi (zemin)

Normal = pozisyon değil. Normal = yön. Üç köşe konumu şekli tanımlar; index buffer şeklin hangi parçalarının birleştiğini söyler; normal o şeklin «hangi tarafının dışa baktığını» kodlar. Işık, bu yön tanımlandıktan sonra devreye girer.

§1 — durma noktası

Bu bölümde normal'in tanımını ve geometri hattındaki yerini netleştirdik: birim yön vektörü, üçüncü semantic, ışık değil. Sonraki bölümler «bu yön dosyada ve GPU'da nasıl yaşar?» sorusuna geçer; face vs vertex (§2–§3), flat vs smooth (§4), accessor zinciri (§5).

Face Normal — Yüz Başına Tek Yön

Face normal (yüz normali), bir üçgenin tamamı için tek bir yön vektörü demektir: düzlemdeki tüm noktalar «aynı yöne» bakıyormuş gibi davranır. Düşük poligonlu modellerde facetler (yüzey parçaları) net görünür; her üçgenin ayrı bir «kart» gibi aydınlanması face normal mantığına yakındır.

Tek üçgenli bir zemin parçasında face normal hesabı basittir: üç köşe sabitken e1 × e2 tek bir n üretir. Mesh'i kameranın etrafında döndürseniz bile (world matrix değişse bile) model uzayında aynı üçgen için face normal aynı kalır; çünkü köşe konumları değişmedi.

Flat face — düz yüzey okuması

«Flat» (düz) okuma: bir üçgenin üç köşesine de aynı normal yazılır veya shader yüzeyi tek normal ile aydınlatır. Görsel sonuç: o yüzey parçası homojen, keskin bir facet gibi görünür. Bu, §4'teki flat shading ile birleşir; fark şu: face normal kavramı geometrik tanım, flat shading ise GPU'nun bu tanımı nasıl kullandığıdır.

Tek üçgen · face normal n

V0  →  n
V1  →  n    (üç köşe de aynı yönü taşır — flat okuma)
V2  →  n

glTF dosyasında flat bir üçgen için NORMAL accessor'da üç satır da aynı (nx, ny, nz) değerini taşıyabilir; veya motor face normal'i çalışma anında türetir. Format okuyucusu için önemli olan: bir primitive'in düz yüzeyi tek yönle temsil edilebilir.

Face normal ≠ ışık hesabı

Face normal tanımlandıktan sonra ışık modeli «ışın bu yöne ne kadar dik geliyor?» diye sorar; cevap parlaklığı belirler. Bu sayfa ışık formülüne girmez; yalnızca ışığın okuyacağı verinin formatını anlatır. Işık pipeline'ı Three.js · Işık Nedir? sayfasında ayrı katmandır.

Vertex Normal — Köşe Başına Yön

Gerçek zamanlı motorların çoğu yalnızca face normal ile çalışmaz; her vertex satırına bir vertex normal yazar. Aynı uzaysal köşede birleşen yüzeylerin yönleri «ortalanır» veya blend edilir; sonuç, keskin facet yerine yumuşak geçiş hissi verir.

Kritik eğitim noktası: vertex sayısı değişmez. Küp hâlâ sekiz köşe (veya paylaşımlı indexed modelde daha az benzersiz vertex) taşır; yalnızca NORMAL accessor'daki float üçlüleri değişir. Şekil aynı, okuma biçimi farklı.

Küp örneği — flat mod

Bir küpte her yüz düzlemseldir. Flat modda her yüz kendi face normal'ini taşır; köşe vertex'i birden fazla yüzeye aitse pratikte normal split gerekir; aynı POSITION, farklı NORMAL satırları (§7). Basit sayımla: küpün 6 yüzü → 6 farklı yön; köşe başına 3 yüz birleştiğinden «tek ortalama» alınmazsa keskin kutu silueti korunur.

Flat küp (kavramsal):
  6 yüz  →  6 ayrı normal yönü (+X, −X, +Y, −Y, +Z, −Z)
  Köşe V →  komşu yüzlerin normal'i paylaşılmaz (split)

Küp örneği — smooth mod

Smooth modda komşu yüzlerin face normal'leri aynı vertex indeksinde ortalama alınır (vertex normal averaging). Küp köşesinde üç dik yüzeyin normal'leri birleşince köşe «yuvarlatılmış» gibi aydınlanır; geometri hâlâ küp, poligon sayısı aynı.

Smooth küp (kavramsal):
  Köşe V  →  normalize( n_yüz1 + n_yüz2 + n_yüz3 )
  Aynı POSITION · farklı NORMAL tablosu · daha yumuşak okuma

Export araçları (Blender «Shade Smooth», glTF exporter) bu tabloyu dosyaya yazar veya runtime'da türetir. glTF okuyucusu için mesaj: NORMAL accessor, POSITION ile aynı vertex count ve aynı index eşlemesine sahip olmalıdır; her POSITION satırının bir NORMAL karşılığı vardır (eksik durum §6).

Aynı geometri · farklı normal. Vertex ve index sabitken NORMAL değişirse ekrandaki silüet aynı kalabilir; ışık altındaki okuma değişir.

Flat vs Smooth Shading

Bu bölüm sayfanın ana fikrini somutlaştırır. Geometri hattında ilk iki sayfa «görünümü» şöyle değiştirir: Vertex Data köşe koordinatlarını değiştirince silüet değişir; Index Buffer bağlantı sırasını değiştirince hangi yüzeylerin derlendiği değişir. Normal Data'da ise çoğu zaman şunu görürsünüz: silüet aynı, üçgen sayısı aynı, ekrandaki okuma farklı; sebep POSITION veya indices değil, NORMAL accessor'ıdır.

Bu, Holodepth geometri hattının üçüncü «aha» anıdır: mesh yalnızca şekil değildir; aynı şekil farklı yön tablosuyla farklı «hissedilir». Işık formülüne girmeden bile (tek directional ışık yeter) flat ve smooth yan yana konduğunda fark belirgindir; çünkü değişen veri ışık değil, ışığın okuyacağı yön tablosudur.

Silüet aynı · okuma farklı

Yeni başlayanlar «daha yumuşak görünüm = daha fazla poligon» sanır. Smooth shading bu varsayımı kırar: düşük poligonlu bir kristal veya asteroidde vertex ve triangle sayısı sabitken yalnızca NORMAL değişince form yuvarlakmış gibi okunabilir. Dış kontur (wireframe, orthographic silüet) çoğu zaman özdeş kalır; değişen yüzey üzerindeki aydınlık geçişidir; konum katmanına dokunulmadığı için.

Referans Model · Facet Crystal Vein

Three.js · glTF · NORMAL

Geometri hattının üçüncü kiti: düşük poligonlu kristal damar kümesi. Vertex Field (zemin V0–V3), Mesh Compiler (aynı dört düğüm) ve Space Station gibi önceki referans sahnelerle paylaşılmaz. Aynı POSITION ve indices ile yalnızca NORMAL accessor içeriği değişince okuma değişir; §4'ün görsel kanıtı.

Mesh 6 facet shard · tek primitive
POSITION 33 vertex · sabit
indices 162 · 54 üçgen
NORMAL count = POSITION · flat / smooth
Metafor Geometry Orientation
Kullanım Light Response Lab

Aynı geometri · aynı index · farklı normal

Flat vs smooth karşılaştırması «dosyada ne değişir?» sorusuna cevap verir. §2–§3'teki face / vertex normal tanımları burada yan yana sonuç olarak okunur; formüller tekrarlanmaz.

Flat vs smooth — ne değişir, ne değişmez?
Katman Flat shading Smooth shading
POSITION accessor Byte-byte aynı
indices Aynı üçgen listesi
Vertex / üçgen sayısı Aynı
Bounding box / silüet Pratikte aynı
NORMAL accessor Yüz normali; köşede split veya üçlüde aynı n (§2) Komşu yüzlerin ortalaması (§3)
GPU'da interpolate edilen yön Yüz içi sabit (gradyan yok) Köşeler arası geçiş (gradyan var)
Görsel okuma Facet'li, keskin, «low-poly» Yumuşak, yuvarlakmış his

Dosyada yan yana — aynı primitive

Aynı glTF primitive düşünün: attributes.POSITION ve indices sabit; yalnızca NORMAL accessor içeriği veya export öncesi shading modu değişir. Kavramsal fark (8 köşeli kristal köşesi V0, iki komşu yüz):

// Ortak (değişmez):
attributes.POSITION  → 8 vertex koordinatı
indices              → 12 üçgen × 3 (örnek)

// Flat — V0 her yüz için ayrı normal (split) veya üçgende tek n:
NORMAL[flat][0]   ≈  yüz A'nın face normal'i
NORMAL[flat][0']  ≈  yüz B'nin face normal'i   (aynı uzay noktası, farklı satır)

// Smooth — V0 tek satır, ortalama:
NORMAL[smooth][0] ≈  normalize( n_A + n_B )

Smooth modda vertex sayısı hâlâ 8'dir; flat + split senaryosunda POSITION tekrarı gerekebilir (§7); bu, «yumuşak görünüm için vertex çoğaltmak» değil, kenar keskinliği tasarımıdır. §4'ün çekirdek dersi daha basittir: split olmadan da aynı indexed mesh'te NORMAL tablosunu smooth'a çevirmek yeterli olabilir.

DCC export — Shade Flat / Shade Smooth

Blender ve benzeri araçlarda «Shade Flat» ve «Shade Smooth» doğrudan NORMAL accessor içeriğini etkiler; mesh'e yeni köşe eklemez (smooth için mevcut vertex'lerde normal yeniden hesaplanır). glTF exporter çoğu zaman bu tabloyu dosyaya yazar; sonuç:

  • Shade Flat → facet'li NORMAL; düşük poly estetik, keskin kenarlar
  • Shade Smooth → ortalanmış vertex NORMAL; organik / yuvarlak okuma

Format okuyucusu dosyayı açtığında «neden bu model keskin, bu model yumuşak?» sorusunun cevabı çoğu zaman NORMAL byte'larındadır; poligon bütçesi aynı olsa bile.

Runtime bayrağı — kısa köprü

Three.js'te material.flatShading çalışma anında shading modunu değiştirir; bu, dosyadaki NORMAL ile birlikte veya ona alternatif olarak düşünülebilir. Format sayfası için mesaj: glTF'deki NORMAL tablosu «smooth export» varsayımına yakındır; flat görünüm bazen materyal bayrağı ile runtime'da zorlanır. Debug görselleştirme: MeshNormalMaterial + flatShading; algoritma detayı Three.js sayfasında, burada yalnızca «aynı geometri, farklı shading bayrağı» fikrinin köprüsü.

material.flatShading = true;
material.needsUpdate = true;

Shader'da ne olur? (özet)

Her iki modda da vertex shader NORMAL attribute'unu okur; fragment'a interpolate edilmiş yön gider. Fark, köşelere yazılan başlangıç değerlerindedir:

Flat:
  Üç köşede aynı n  →  interpolate sonrası hâlâ n  →  yüz homojen

Smooth:
  Köşelerde farklı n  →  interpolate  →  yüz içi gradyan

Interpolation matematiği Shader · Interpolation sayfasındadır. Işık formülü (dot product, specular…) bu sayfada yok; Işık Nedir? katmanı yalnızca «interpolate edilmiş normal'e bakar» cümlesiyle sınırlı kalır.

Normal map değil · attribute NORMAL

Flat / smooth ayrımı geometri dosyasındaki NORMAL semantic'i ile ilgilidir. Normal map fragment shader'da ek mikro yön ekler; silüeti değiştirmez, attribute tablosunu değiştirmez. İkisi üst üste kullanılabilir; bu sayfa yalnızca attribute katmanını anlatır. Three.js · Normal Map ayrı konudur.

Sık yanlışlar

  • «Smooth = daha fazla vertex» — Hayır; çoğu smooth export aynı vertex count ile gelir. Vertex çoğaltma hard edge / UV split senaryosudur (§7), smooth'ın tanımı değil.
  • «Flat = normal yok» — Flat'ta da NORMAL vardır; çoğu köşede yüz normali tekrarlanır veya split edilir (§6 eksik veri ayrı konu).
  • «Işığı değiştirdim, shading modu değişti» — Işık gücü parlaklığı değiştirir; facet / smooth ayrımı NORMAL tablosunun yapısıdır.

Geometri Verileri hattının üçüncü dersi: Vertex Data «nerede?» · Index Buffer «nasıl bağlanıyor?» · Normal Data «hangi yöne bakıyor?»; flat vs smooth, aynı ilk iki cevapla üçüncü cevabın görünümü nasıl değiştirdiğini gösterir.

§4 — durma noktası

Bu bölümde ana fikir somutlaştı: POSITION + indices sabitken NORMAL değişince okuma değişir. Sonraki adımlar dosya ve edge case'lerdir: glTF accessor zinciri (§5), eksik NORMAL (§6), hard edge split (§7). Aşağıdaki Light Response Lab demosu bu bölümün görsel karşılığıdır; Facet Crystal Vein, flat / smooth modları yan yana.

GPU Açısından NORMAL

Dosya yapısı hattında buffers, bufferViews ve accessors kurallarını Accessor Mantığı sayfasında öğrendiniz. Vertex Data §5 bu zinciri POSITION semantic'i ile geometri hattına bağladı. Bu bölüm aynı omurgayı NORMAL için tarif eder: byte düzeni çoğu zaman POSITION ile özdeş görünür (VEC3 float); fark, okunan üçlünün koordinat değil yön olmasıdır (§1).

Zinciri iki yönden okuyabilirsiniz; POSITION için geçerli olan mantık NORMAL'de de geçerlidir:

  • Yukarıdan aşağı primitive → "NORMAL": 1 → accessor[1] → bufferView → buffer
  • Aşağıdan yukarı buffer byte → accessor kuralı → «bu N satır VEC3 float yön» → mesh attribute haritasında NORMAL

Debug sırasında «neden bu model düz aydınlanıyor?» sorusu çoğu zaman accessor[1] içeriğine veya attribute'un eksikliğine iner (§6); POSITION byte'ları sağlam olsa bile.

glTF primitive — attribute eşlemesi

{
  "meshes": [{
    "primitives": [{
      "attributes": {
        "POSITION": 0,
        "NORMAL": 1
      },
      "indices": 2,
      "mode": 4
    }]
  }],
  "accessors": [
    {
      "bufferView": 0,
      "componentType": 5126,
      "count": 24,
      "type": "VEC3"
    },
    {
      "bufferView": 1,
      "componentType": 5126,
      "count": 24,
      "type": "VEC3"
    },
    {
      "bufferView": 2,
      "componentType": 5123,
      "count": 36,
      "type": "SCALAR"
    }
  ]
}

"NORMAL": 1 satırı zincirin giriş kapısıdır: mesh «yön verisi için 1 numaralı accessor'ı kullan» der. POSITION ile aynı primitive içinde yaşar; ikisi paralel tablolardır; satır i için POSITION konumu, NORMAL yönü. Index buffer (indices: 2) hangi satırların üçgende birleşeceğini söyler; her index değeri k için draw anında tipik okuma:

index değeri k  →  POSITION[k]  +  NORMAL[k]  (+ diğer attribute'lar)

Örnek üçlü 0, 1, 2:
  Köşe A: POSITION[0], NORMAL[0]
  Köşe B: POSITION[1], NORMAL[1]
  Köşe C: POSITION[2], NORMAL[2]

Assembly mantığı Index Buffer · attribute'lar assembly ile çekilir bölümünde POSITION odaklı anlatıldı; NORMAL aynı indeksle gelir. Index, NORMAL'i hesaplamaz; yalnızca hangi satırın okunacağını seçer. Flat / smooth farkı NORMAL tablosunun içeriğindedir (§4), index listesinde değil.

NORMAL accessor alanları

{
  "accessors": [
    {
      "bufferView": 0,
      "componentType": 5126,
      "count": 24,
      "type": "VEC3"
    },
    {
      "bufferView": 1,
      "componentType": 5126,
      "count": 24,
      "type": "VEC3"
    },
    {
      "bufferView": 2,
      "componentType": 5123,
      "count": 36,
      "type": "SCALAR"
    }
  ]
}

Tipik NORMAL accessor: type: "VEC3", componentType: 5126 (FLOAT). count, POSITION accessor ile eşit olmalıdır; her konum satırının bir yön karşılığı vardır. Index accessor'ın count'u farklıdır (üçgen × 3); NORMAL ile karıştırılmaz (Index Buffer · doğrulama POSITION count ile index değerlerini; burada kural POSITION count = NORMAL count).

Aynı dosyada yan yana iki accessor
Alan POSITION (ör. 0) NORMAL (ör. 1)
type VEC3 VEC3
componentType 5126 FLOAT 5126 FLOAT
count N vertex N (aynı)
Semantic anlam Konum (x,y,z) Yön (nx,ny,nz)
min / max Sık kullanılır (bounds) Nadiren anlamlı (yön birim vektör)

Binary okuyucu aynı byte düzenini görür; semantic mesh tablosundan gelir. NORMAL float üçlüsünü POSITION koordinatı sanmak yaygın hata; Accessor Lens demosu yanlış yorumun geometriyi nasıl bozduğunu gösterir.

Format → GPU zinciri (ışık hariç)

glTF JSON (mesh.primitives.attributes.NORMAL → 1)
        ↓
Accessor  (type: VEC3, componentType: FLOAT, count: N)
        ↓
BufferView (byte aralığı — POSITION'dan ayrı veya interleaved)
        ↓
Buffer    (GLB gömülü / harici .bin)
        ↓
Loader decode  (byte → Float32Array, semantic: NORMAL)
        ↓
GPU vertex attribute slot  (NORMAL)
        ↓
Vertex Shader  (yön girdisi — transform detayı shader kanalı)
        ↓
Interpolation  →  fragment'a varyan normal
        ↓
Lighting / materyal  ← bu sayfa burada durur

Sıkıştırma (Draco, Meshopt) decode sonrası yine float üçlü yön üretir; niceleme Vertex Quantization konusudur. Varsayılan yol: açık VEC3 float NORMAL.

Her katman ne bilir, ne bilmez?

Vertex Data §5 tablosu POSITION odaklıdır. NORMAL için sorumluluk sınırı aynıdır; yalnızca semantic ve shader yorumu değişir:

NORMAL yolculuğunda katman sorumlulukları
Katman NORMAL için bilir Bilmez
buffers Ham byte Bu byte konum mu yön mü?
bufferViews Offset, length, stride Semantic (POSITION / NORMAL ayrımı)
accessors VEC3 FLOAT, count = N Flat mu smooth mu, ışık gücü
attributes.NORMAL «Yön verisi accessor 1» Byte stride, interleave düzeni
Loader NORMAL slot'una float dizisi Normal map, tangent uzayı
Vertex shader «Bu slot yüzey yönü» glTF accessor index numarası

Buffer düzeni — ayrı veya interleaved

POSITION ve NORMAL aynı buffer içinde bitişik iki bufferView olabilir veya tek interleaved bufferView'da stride ile ardışık durabilir; glTF her ikisine de izin verir. Format okuyucusu için kritik olan byte fizik değil, accessor count eşleşmesi ve semantic haritasıdır:

Ayrı bufferView (yaygın export):
  bufferView 0  →  yalnızca POSITION float'ları
  bufferView 1  →  yalnızca NORMAL float'ları

Interleaved (tek bufferView, stride = 24 byte):
  [x,y,z, nx,ny,nz,  x,y,z, nx,ny,nz, ...]
   └─ POSITION ─┘ └─ NORMAL ─┘   (vertex başına)

Interleave detayı Three.js · Interleaved Buffers sayfasındadır; bu sayfa «iki semantic, iki accessor, aynı vertex count» kuralında kalır.

Loader köprüsü

glTF spesifikasyonu loader tanımlamaz; uygulama (ör. GLTFLoader) accessor[1] kurallarına göre NORMAL float dizisini çözer ve GPU attribute buffer'ına yazar. Runtime karşılığı Attributes & Buffers sayfasında geometry.attributes.normal olarak yaşar; WebGL bind ve draw call detayı Three.js kanalındadır.

Loader NORMAL'i üretmez (§6 eksikse motor sonradan computeVertexNormals() ile tamamlayabilir); dosyada varsa olduğu gibi taşır. Flat / smooth içerik farkı accessor byte'larındadır, loader semantic adından anlaşılmaz.

Sahne transformu — kısa köprü

NORMAL, tıpkı POSITION gibi mesh yerel uzayında saklanır. Node matrix uygulandığında konum world uzayına taşınır; normal için ayrı «normal matrix» dönüşümü gerekir (ölçekli transformlarda yön bozulmasın diye). Bu matematik Shader · Coordinate Spaces sayfasındadır. Format sayfası durma noktası:

NORMAL accessor = yüzey yönünün yerel tanımı · Node transform = şekli sahneye taşıyan çerçeve. İkisi birleşmeden world-space yön tamamlanmaz; bu birleşim shader sorumluluğudur.

Shader ve ışık derinliği bu sayfada yok

Vertex shader'da normal transform, varyan interpolation, fragment'ta dot product ve PBR ByteOmi / Three.js / shader kanallarındadır. Format okuyucusu için net cümle:

GPU'ya giden NORMAL attribute'un dosyada nereden geldiğini tarif edebilmek. Işığın bu yöne nasıl baktığı Lighting sayfalarının konusudur.

§5 — durma noktası

Bu bölüm NORMAL'in glTF → accessor → buffer → vertex attribute zincirini POSITION ile paralel kurdu; fark semantic ve yön yorumudur. Sonraki bölümler edge case: eksik NORMAL (§6), hard edge split (§7). Light Response Lab GPU NORMAL modu bu zincirin görsel özetini sunar.

Eksik Normal Verisi

Her glTF primitive'inde NORMAL zorunlu değildir. Özellikle ham export veya prosedürel üretimde yalnızca POSITION (+ indices) gelebilir. Three.js ve benzeri motorlar bu durumda çoğu zaman geometriden normal üretebilir.

Dosyada NORMAL yok

{
  "meshes": [{
    "primitives": [{
      "attributes": {
        "POSITION": 0
      },
      "indices": 1,
      "mode": 4
    }]
  }]
}

attributes içinde yalnızca POSITION vardır; NORMAL anahtarı yoktur. Loader uyarı verebilir veya sessizce devam eder; sonuçta mesh görünür ama aydınlatma «düz» veya hatalı olabilir; çünkü shader'ın okuyacağı yön tablosu boştur. Format okuyucusu için kural: eksiklik bir hata sınıfıdır; motor tarafı üretim ile telafi edilebilir.

Runtime: computeVertexNormals()

Three.js'te yaygın yol:

geometry.computeVertexNormals();

Bu çağrı, POSITION ve index'ten vertex normal'leri türetir ve normal attribute'unu doldurur. Algoritma detayı (hangi komşu yüzler, ağırlık formülü) bu sayfanın kapsamı dışındadır; mesaj şudur: motor, dosyada NORMAL yoksa geometriden üretebilir. Kısa köprü: Three.js · Geometri Giriş ve Custom BufferGeometry.

DCC'den (Blender vb.) gelen profesyonel asset'lerde NORMAL genelde dosyada hazır bulunur; smooth / flat export tercihi exporter'a bağlıdır. Format okurken «NORMAL var mı, count POSITION ile uyumlu mu?» kontrolü yeterli ilk adımdır.

Eksik NORMAL ≠ bozuk POSITION. Koordinatlar ve index sağlamken yalnızca yön tablosu eksik olabilir; üretim veya düzeltme runtime katmanındadır.

Hard Edge — Aynı Konum, Farklı Normal

Smooth shading köşeleri yumuşatır; bazen tasarım keskin kenar ister (küp köşesi, kristal facet, panel birleşimi). Bu durumda aynı uzaysal konumda birden fazla vertex satırı bulunur: POSITION aynı (veya çok yakın), NORMAL farklı; «normal split» veya hard edge.

Küp köşesi — neden split?

Küpün bir köşesinde üç dik yüz buluşur. Smooth ortalama bu yüzeyleri «yuvarlar»; hard edge istiyorsanız her yüz komşuluğu için ayrı normal gerekir. Indexed mesh'te bu, aynı konumun farklı vertex indekslerinde tekrarlanması ile çözülür; Index Buffer'daki «shared vertex» mantığının bilinçli olarak kırılmasıdır (Index Buffer §5 ile karşılaştırın: orada paylaşım verimlilik içindi; burada split görsel kenar içindir).

Hard edge köşe (kavramsal):

Uzaydaki nokta P  →  3 vertex satırı:
  idx 10  POSITION P  NORMAL +X yüzüne bakar
  idx 11  POSITION P  NORMAL +Y yüzüne bakar
  idx 12  POSITION P  NORMAL +Z yüzüne bakar

Vertex sayısı arttı · konum tekrarlandı · yön ayrıldı

Tangent sayfasına köprü

Normal map ve anizotropik materyaller için TANGENT (ve genelde bitangent) gerekir; tangent uzayı NORMAL'e bağlıdır. Hard edge'te normal split, tangent'in de tutarlı split edilmesini gerektirir; aksi halde normal map yüzeyler arasında yırtılır. Detay Tangent Data sayfasına bırakılır; burada yalnızca şu cümle yeterli: aynı vertex pozisyonu farklı normal taşıyabilir; bu ileri seviye ama format okumasında sık karşılaşılan bir durumdur.

Normal Data sayfası — durma noktası

§1–§7 birlikte şu zinciri tamamlar: normal nedir → face vs vertex → flat vs smooth → glTF NORMAL accessor → eksik veri → hard edge. Işık formülleri, PBR, normal map dokusu ve tangent uzayı bu hattın dışındadır.

Normal Data format okuması: Vertex Data «nerede?» · Index Buffer «nasıl bağlanıyor?» · Normal Data «hangi yöne bakıyor?»; üç soru, üç semantic, üç sayfa.

Demo · Light Response Lab

Vertex Field Lab Geometry Data hattını anlatır; konum nerede? Mesh Compiler Geometry Topology hattıdır; köşeler nasıl bağlanıyor? Bu laboratuvar Geometry Orientation hattıdır; soru değişir: derlenmiş yüzey hangi yöne bakıyor?

Referans model Facet Crystal Vein aynı indexed mesh üzerinde çalışır. Tek directional ışık yeterlidir; amaç PBR vitrini veya ışık formülü değil, NORMAL tablosunun okuma farkını hissettirmektir. Işık hesabı Lighting sayfalarında kalır; burada yalnızca «shader bu yöne bakar» sınırı korunur.

Nasıl kullanılır? Üstteki mod düğmeleri aynı kristal mesh'te farklı NORMAL okuma katmanını gösterir. Sol panelde POSITION / indices sayıları kilitli kalır; değişen yalnızca shading modu ve NORMAL strip'tir. Vertex Field'daki küre halo ve Mesh Compiler'daki bakır index izleri burada bilinçli olarak yok; farklı metafor, aynı geometri disiplini.

Beş okuma modu

  • Silhouette Lock — Yalnızca wireframe; POSITION + indices sabit, NORMAL henüz devreye girmedi (§4).
  • Flat Facets — Face normal tablosu; her facet ayrı panel (§2).
  • Smooth Blend — Vertex normal ortalaması; köşelerde yön birleşimi (§3).
  • Split Compare — Aynı mesh yan yana flat | smooth; §4'ün doğrudan görsel karşılığı.
  • GPU NORMAL — Accessor → attribute zinciri; metin §5 ile eşleşir.
Demo · Facet Crystal Vein · NORMAL

Silhouette lock · flat facets · smooth blend · split · GPU strip

Okuma modu

Veri paneli · NORMAL

Primitive attribute

GPU yolculuğu

3D · orientation read

Bu demo ne hissettirir? Silhouette Lock: şekil var, yön okunmuyor. Flat Facets: facet panelleri belirgin. Smooth Blend: aynı silüet, yumuşak geçiş. Split Compare: POSITION + indices kilitli, NORMAL tablosu farklı. GPU NORMAL: dosyadaki accessor zincirinin görsel özeti. Demo bittiğinde şu cümleyi kurabilmelisiniz: «Aynı kristal mesh; değişen ışık değil, shader'ın okuduğu yön tablosu.»