wgsl.runWGSL Öğren

Interaktif ders / Tipler

WGSL Tipler

Bu seri WGSL tip sistemini somut shader parçalarıyla anlatır. Scalar tiplerden explicit dönüşümlere, vector swizzle ve matrix çarpımından struct layout ve alias kullanımına kadar her aşama Studio içinde açılabilir.

01 / i32 · u32 · f32 · bool

Skalar tipler

Studio'da aç

WGSL'in dört somut skalar tipi var: i32, u32, f32, bool (+ enable f16 ile f16). Tip belirtmezsen inference devreye girer. Ham literal'ler abstract tip taşır (AbstractInt / AbstractFloat) — bağlama göre somut bir tipe oturur.

Dene: const PI = 3.14159;'in tipi otomatik f32. const PI: i32 = 3.14159; yap → "cannot convert" hatası gör. RATE'in u suffix'ini sil → "expected u32, got abstract int" tartışması başlar.
tipler-01-skalar-tipler.wgsli32 · u32 · f32 · bool
// Skalar tipler ve type inference

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

const COUNT: i32  = 8;          // explicit i32
const RATE:  u32  = 60u;        // u suffix → u32
const PI          = 3.14159;    // inference → f32 (abstract float)
const ON:    bool = true;
const MIX         = 0.5;        // abstract float → f32 bağlama göre

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let uv = frag.xy / u.resolution;
  // Skalar değerleri renge çevir
  let r = MIX * uv.x + sin(u.time) * 0.1;
  let g = MIX * uv.y;
  let b: f32 = 0.85;
  return vec4<f32>(r, g, b, 1.0);
}

02 / cast · explicit

Tip dönüşümü

Studio'da aç

WGSL katı bir tip sistemine sahip — implicit conversion neredeyse yok. i32'i f32'ye çevirmek için açık cast: f32(x). Tek istisna abstract types (5 literal'i bağlama göre i32 ya da f32 olabilir). Integer division truncate eder: 7 / 2 == 3, ama 7.0 / 2.0 == 3.5.

Dene: f32(N) / 2.0N / 2 ile değiştir, sol-sağ farkı izle. let G: f32 = N; satırını yorumdan çıkar — "cannot use i32 where f32 expected".
tipler-02-tip-donusumu.wgslcast · explicit
// Tip dönüşümü: WGSL implicit cast'e izin vermez

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

const N: i32 = 7;
const F: f32 = f32(N);          // explicit cast OK
// const G: f32 = N;             // ← yasak: implicit i32→f32 yok

const X: f32 = 5;               // OK: 5 abstract int → f32 promotion

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let uv = frag.xy / u.resolution;

  // Sol yarı: integer division (truncate) → 7/2 = 3 → 3/4 = 0.75
  let intDiv   = f32(N / 2) / 4.0;
  // Sağ yarı: float division → 7.0/2.0 = 3.5 → 3.5/4 = 0.875
  let floatDiv = (f32(N) / 2.0) / 4.0;

  let val = select(floatDiv, intDiv, uv.x < 0.5);
  return vec4<f32>(val, val * 0.7, 0.93, 1.0);
}

03 / vec2 · vec3 · vec4

Vektör inşa & swizzle

Studio'da aç

vecN<T> 2/3/4 bileşenli sabit vektör. Constructor esnek: tek skalar tüm bileşenlere yayılır, vec3 + skalar vec4 üretir. Swizzle ile bileşenleri serbestçe yeniden sırala: .xyz, .rgb, .stp aynı şey; .yyx tekrar/permütasyon serbest.

Dene: c.yxzc.zyx, sonra c.xxx (sadece x kanalı = gri ton). c.brg gibi RGB swizzle de çalışır — aynı 3 bileşen, farklı isim.
tipler-03-vektor-insa-swizzle.wgslvec2 · vec3 · vec4
// Vektör inşa kalıpları + swizzle

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

const ORIGIN: vec2<f32> = vec2<f32>(0.0, 0.0);
const RED:    vec3<f32> = vec3<f32>(1.0, 0.0, 0.0);
const RED4              = vec4<f32>(RED, 1.0);    // vec3 + skalar
const GRAY:   vec3<f32> = vec3<f32>(0.5);         // tek skalar yayılır

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let uv = frag.xy / u.resolution;
  let c  = vec3<f32>(uv.x, uv.y, 0.5);

  // Swizzle: x ↔ y takas
  let swap = c.yxz;
  return vec4<f32>(swap, 1.0);
}

04 / dot · cross · length

Vektör aritmetiği

Studio'da aç

+ - * / vektörler arasında bileşen-bileşen işler. dot(a, b) skalar verir (iki vektörün ne kadar aynı yöne baktığı), length(v) büyüklük, normalize(v) birim vektör, cross(a, b) 3D'de iki vektöre dik üçüncü. mix(a, b, t) linear interpolation.

Dene: max(dot(...), 0.0)'daki 0.00.2 yap → ambient ışık eşiği. Işığı sabit tut: vec2<f32>(0.3, 0.0).
tipler-04-vektor-aritmetigi.wgsldot · cross · length
// Vektör aritmetiği — element-wise + reductions

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let aspect = u.resolution.x / u.resolution.y;
  let uv = (frag.xy / u.resolution - 0.5) * vec2<f32>(aspect, 1.0);

  // Yön ve mesafe
  let dir = normalize(uv);                       // birim vektör
  let dist = length(uv);                          // skalar mesafe

  // Animasyonlu ışık
  let lightPos = vec2<f32>(0.4 * cos(u.time), 0.4 * sin(u.time));
  let lightDir = normalize(lightPos - uv);

  // dot: iki yönün hizalanması (1 = aynı, -1 = ters, 0 = dik)
  let bright = max(dot(dir, lightDir), 0.0);
  let glow   = mix(0.05, 1.0, bright);

  return vec4<f32>(vec3<f32>(0.13, 0.83, 0.93) * glow, 1.0);
}

05 / mat2x2 · column-major

Matris

Studio'da aç

matCxR<T> = C sütun, R satır. WGSL column-major — constructor argümanları sütun sütun verilir. mat * vec ve vec * mat ikisi de tanımlı; mat * mat matrix kompozisyonu (sırası önemli — sağdaki önce uygulanır).

Dene: R * S'i S * R ile değiştir — sıra önemli. S'in scale değerini değiştir; 2.0 yap, kare küçülür (uv ölçeklendi, sahne aynı).
tipler-05-matris.wgslmat2x2 · column-major
// Matris yapısı: column-major, mat * vec

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let aspect = u.resolution.x / u.resolution.y;
  var uv = (frag.xy / u.resolution - 0.5) * vec2<f32>(aspect, 1.0);

  // mat2x2: 2 sütun × 2 satır = 4 f32
  // Constructor SÜTUN sütun: (col0.x, col0.y, col1.x, col1.y)
  let a = u.time * 0.7;
  let R = mat2x2<f32>(
    cos(a), -sin(a),     // sütun 0
    sin(a),  cos(a)      // sütun 1
  );
  let S = mat2x2<f32>(1.5, 0.0, 0.0, 1.5);

  // Önce S, sonra R uygulanır (sağdan sola okuma)
  uv = R * S * uv;

  // Kare SDF
  let r = 0.25;
  let d = max(abs(uv.x), abs(uv.y)) - r;
  let aa = 1.5 / u.resolution.y;
  let inside = 1.0 - smoothstep(-aa, aa, d);

  let bg = vec3<f32>(0.05, 0.07, 0.10);
  let fg = vec3<f32>(0.13, 0.83, 0.93);
  return vec4<f32>(mix(bg, fg, inside), 1.0);
}

06 / array<T, N>

Array — sabit boyut

Studio'da aç

array<T, N> sabit boyutlu, derleme zamanı bilinmesi gereken N. Index'leme bounds-check yapmaz; index out-of-bounds tanımsız davranış. Storage'da array<T> (boyutsuz) da var — runtime-sized, bind layout'tan boyut çıkar. Burada bir palette uyguluyoruz: 5 renkli array, uv.x'e göre seçiyoruz.

Dene: palette'e renk ekle, dizi boyutunu 6, sonra u32(uv.x * 6.0) ve min(idx, 5u) güncelle. Renkleri kendi seçtiklerinle değiştir.
tipler-06-array-sabit-boyut.wgslarray<T, N>
// Sabit boyutlu array — bir palette uygulaması

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

const PALETTE: array<vec3<f32>, 5> = array<vec3<f32>, 5>(
  vec3<f32>(0.04, 0.12, 0.20),  // koyu lacivert
  vec3<f32>(0.06, 0.36, 0.50),
  vec3<f32>(0.13, 0.65, 0.78),
  vec3<f32>(0.13, 0.83, 0.93),  // cyan
  vec3<f32>(0.96, 0.62, 0.04),  // amber
);

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let uv = frag.xy / u.resolution;
  // uv.x → 0..5 → 0..4 index'i
  let raw = u32(uv.x * 5.0);
  let idx = min(raw, 4u);     // bounds güvenliği
  let col = PALETTE[idx];
  return vec4<f32>(col, 1.0);
}

07 / struct · @align

Struct & bellek düzeni

Studio'da aç

struct ad verilmiş alanlardan oluşan kompozit tip. Üyelerin sırası bellek düzenini belirler; WGSL otomatik olarak hizalama ekler (vec3 16-byte hizalı vs.). @align(16) ve @size(N) ile elle ayarlayabilirsin. Buffer içinde struct kullanırken bu düzen JS-tarafıyla bire bir eşleşmeli.

Dene: roughness'ı 0.9 yap (mat donuklaşır). Yeni alan ekle: metallic: f32 ve constructor'a 0.7 ekle. @align(16) baseColor'a koy.
tipler-07-struct-bellek-duzeni.wgslstruct · @align
// Struct: ad verilmiş kompozit tip + memory layout

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

struct Material {
  baseColor: vec3<f32>,
  roughness: f32,
  emission:  vec3<f32>,
  // Derleyici hizalama padding'i ekleyebilir
}

const M: Material = Material(
  vec3<f32>(0.13, 0.83, 0.93),  // baseColor (cyan)
  0.4,                           // roughness
  vec3<f32>(0.0, 0.05, 0.10)    // emission (subtle glow)
);

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let aspect = u.resolution.x / u.resolution.y;
  let uv = (frag.xy / u.resolution - 0.5) * vec2<f32>(aspect, 1.0);

  let d = length(uv) - 0.32;
  let aa = 1.5 / u.resolution.y;
  let inside = 1.0 - smoothstep(-aa, aa, d);

  // Member access: M.baseColor, M.roughness, M.emission
  let surface = M.baseColor * (1.0 - M.roughness * 0.4);
  let col = mix(M.emission, surface, inside);
  return vec4<f32>(col, 1.0);
}

08 / alias · semantik isim

Type alias

Studio'da aç

alias Name = T; mevcut bir tipe yeni isim takar — tip aynı, sadece okunabilirlik için. vec3<f32> yerine Color, vec2<f32> yerine Pos2. Birden çok parametre alan fonksiyonların imzaları anlam kazanır. Tipler birbiri yerine geçer (alias yeni tip oluşturmaz, sadece nickname).

Dene: alias Direction = vec2<f32>; ekle, rotate içinde kullan. Renk paletini değiştir — SKY'ı sıcak bir tona çek, hava değişir.
tipler-08-type-alias.wgslalias · semantik isim
// Type alias — tipe semantik isim ver

struct U { resolution: vec2<f32>, time: f32 };
@group(0) @binding(0) var<uniform> u: U;

alias Color = vec3<f32>;
alias Pos2  = vec2<f32>;

const SKY:    Color = Color(0.02, 0.05, 0.10);
const ACCENT: Color = Color(0.13, 0.83, 0.93);
const WARM:   Color = Color(0.96, 0.62, 0.04);

fn rotate(p: Pos2, a: f32) -> Pos2 {
  let c = cos(a);
  let s = sin(a);
  return Pos2(c * p.x - s * p.y, s * p.x + c * p.y);
}

@vertex
fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
  let p = vec2<f32>(f32((vi << 1u) & 2u), f32(vi & 2u));
  return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
}

@fragment
fn fs(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
  let aspect = u.resolution.x / u.resolution.y;
  var p: Pos2 = (frag.xy / u.resolution - 0.5) * Pos2(aspect, 1.0);
  p = rotate(p, u.time * 0.4);

  let r     = length(p);
  let theta = atan2(p.y, p.x);
  let bands = sin(r * 24.0 - u.time * 2.0) * 0.5 + 0.5;
  let arms  = pow(cos(theta * 5.0) * 0.5 + 0.5, 3.0);

  var col: Color = mix(SKY, ACCENT, bands);
  col = mix(col, WARM, arms * 0.4);
  return vec4<f32>(col, 1.0);
}