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
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.
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.// 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ü
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.
f32(N) / 2.0'ı N / 2
ile değiştir, sol-sağ farkı izle. let G: f32 = N; satırını
yorumdan çıkar — "cannot use i32 where f32 expected".// 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
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.
c.yxz → c.zyx,
sonra c.xxx (sadece x kanalı = gri ton). c.brg
gibi RGB swizzle de çalışır — aynı 3 bileşen, farklı isim.// 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
+ - * / 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.
max(dot(...), 0.0)'daki
0.0'ı 0.2 yap → ambient ışık eşiği. Işığı
sabit tut: vec2<f32>(0.3, 0.0).// 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
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).
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ı).// 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
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.
6, sonra u32(uv.x * 6.0) ve min(idx, 5u)
güncelle. Renkleri kendi seçtiklerinle değiştir.// 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
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.
roughness'ı 0.9 yap (mat
donuklaşır). Yeni alan ekle: metallic: f32 ve
constructor'a 0.7 ekle. @align(16)
baseColor'a koy.// 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
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).
alias Direction = vec2<f32>;
ekle, rotate içinde kullan. Renk paletini değiştir —
SKY'ı sıcak bir tona çek, hava değişir.// 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);
}