Interaktif ders / Anatomi
WGSL Anatomi
Bu seri boş modülden çalışan shader modülüne doğru gider. Amaç WGSL dosyasının nasıl kurulduğunu, hangi declaration türlerinin nerede yaşayacağını ve entry point eklenince pipeline şeklinin nasıl değiştiğini görmektir.
01 / comment · whitespace
Yorumlar
WGSL iki tür yorum tanır: // satır sonuna kadar ve
/* blok */. Blok yorumlar iç içe de olabilir
— C/C++/JS'in aksine bu WGSL'in özelliği. Parser bunları görmezden gelir,
sadece blankspace olarak sayar. Boş bir modül de geçerlidir;
derler ama hiçbir şey çizmez.
/* … */
yerleştir — hâlâ derler. const X satırını sil; yine derler
(modülde hiçbir şey olmak zorunda değil).// Bu bir line comment — parser görmez.
//
// const X = 1; ← yorum içindeki kod hâlâ yorum
//
/* Bu bir blok yorum, çoklu satır.
/* WGSL blok yorumları İÇ İÇE olabilir — sayılır */
parser yine görmez.
*/
// Aşağıda tek bir deklarasyon var:
const TEMP: f32 = 273.15;
02 / literal · suffix
Sayı literal'leri
WGSL sayıların tipini suffix'le belirtmene izin verir.
42 abstract int (bağlama göre i32 veya u32),
42u kesin u32, 3.14 abstract float,
3.14f kesin f32, 0x1Fu hex unsigned,
1.5e2 bilimsel gösterim. 0.5h f16 — ama
önce enable f16; direktifi gerekir.
-1 aslında
pozitif 1 + unary minus. 0xFF hex int dene.
0.5h'i aç → "f16 not enabled" hatası.// WGSL sayı literal'leri — tip eki belirleyici:
const A: i32 = 42; // int (whole numbers için varsayılan)
const B: u32 = 42u; // unsigned int (suffix u)
const C: f32 = 3.14; // float
const D: f32 = 3.14f; // float (açık suffix)
const E: u32 = 0x1Fu; // hex unsigned (= 31)
const F: f32 = 1.5e2; // bilimsel (= 150.0)
// const G: f16 = 0.5h; // f16 — enable f16; gerekir
03 / naming · reserved
Identifier kuralları
Identifier'lar harf veya _ ile başlamalı, sonrasında
harf/rakam/_ gelebilir. $ izinli değil. İki ya da
daha fazla underscore ile başlayan isimler (__foo)
ya da tek underscore'la biten birkaç pattern reserved'dır —
compiler hata atar. Tek _ ise özel: unused result
ifadesi.
// Geçerli identifier'lar:
const myValue: f32 = 1.0;
const my_value: f32 = 2.0;
const x1: f32 = 3.0;
const _hidden: f32 = 4.0; // tek underscore başı OK
// Geçersiz olanları dene (yorumu kaldır):
// const __internal: f32 = 5.0; // çift underscore RESERVED
// const $bad: f32 = 1.0; // $ karakteri yasak
// const 1bad: f32 = 1.0; // rakamla başlayamaz
// const my-name: f32 = 1.0; // tire yasak
04 / keyword · reserved
Anahtar kelime çakışması
WGSL ~30 keyword (anahtar kelime) ve onlarca
reserved word (gelecek için ayrılmış) tanımlar.
Bunları identifier olarak kullanırsan parser kafayı yer.
let, var, const, fn, struct, if, else, for, while, loop, return,
true, false, switch, override... hepsi rezerve.
// WGSL'de ayrılmış kelimeler identifier olamaz.
const value: f32 = 1.0; // OK — 'value' ayrılmış değil
// Aşağıdakiler error verir (yorumu kaldır, gör):
// const let: f32 = 1.0; // 'let' anahtar kelime
// const fn: f32 = 1.0; // 'fn' anahtar kelime
// const struct: f32 = 1.0; // 'struct' anahtar kelime
// const return: f32 = 1.0; // 'return' anahtar kelime
05 / module scope · address space
const · var · override
Module scope'ta üç bildirim formu var:
const (derleme zamanı sabit, tip otomatik veya açık),
override (pipeline create sırasında JS'ten geçersiz
kılınabilir), ve var<address_space> (yazılabilir,
address space zorunlu — örn. private, workgroup).
let sadece function scope'tadır — modülde
yazarsan error.
// let X = 1.0;'ı
aç — "let declaration is not allowed at module scope"
hatası gelir. override'ı JS'ten override etmek için
pipeline create'te { constants: { BLUR: 2.5 } }.// Module scope: 3 bildirim formu
const PI: f32 = 3.14159; // sabit, derleme zamanı
override BLUR: f32 = 1.0; // pipeline anında set edilebilir
var<private> counter: i32 = 0; // yazılabilir, address space lazım
// Kısa form (tip inferred):
const TAU = 6.28318; // tip f32 olarak çıkarılır
// Address space örnekleri:
// var<private> counter: i32 — her invocation'a özel
// var<workgroup> shared: array<f32, 64> — workgroup paylaşımlı
// var<storage> data: array<f32> — buffer'a bağlı
// 'let' module scope'ta YASAK — yorumu aç, hatayı gör:
// let X: f32 = 1.0;
06 / composite · attributes
Struct deklarasyonu
struct ad verilmiş alanlardan oluşan kompozit tip.
Üye'ler @attribute taşıyabilir — örn. @location(0)
vertex input'u, @align(16) hizalama. Constructor şeklinde
çağırarak instance oluşturursun: Color(1.0, 0.0, 0.0).
Color'a a: f32
alanı ekle, RED constructor'ı 4 argümanlı yap. Tipler
eşleşmezse parser hata verir.// Struct: ad verilmiş alanlardan oluşan kompozit tip
struct Color {
r: f32,
g: f32,
b: f32,
}
struct Vertex {
@location(0) pos: vec3<f32>,
@location(1) uv: vec2<f32>,
}
// Constructor çağrısı:
const RED: Color = Color(1.0, 0.0, 0.0);
const CYAN: Color = Color(0.13, 0.83, 0.93);
// Struct alanı '.' ile erişilir (sadece function scope'ta):
// fn foo() { let r = RED.r; }
07 / function · let · var
Function & yerel scope
Function scope'ta let (immutable) ve scope-yerel
var (mutable, address space ZORUNLU değil — varsayılan
function) izinli. Module scope'taki const/struct/var'lar
buradan görünür. Function içindeki bildirimler dışarıdan
görünmez — scope'lar tek yönlü.
area içindeki
let r2'yi var r2 yap, sonra
r2 = r2 * 2.0; ile yeniden ata. let ile
yapamazsın.// Function scope: let ve var burada serbest
const PI: f32 = 3.14159; // module scope
fn area(r: f32) -> f32 {
let r2 = r * r; // function scope, IMMUTABLE
var temp: f32 = 0.0; // function scope, mutable
temp = PI * r2; // module scope const'a erişim OK
return temp;
}
// area() içindeki 'r2' DIŞARIDAN görünmez:
// const X = r2; ← error: undeclared identifier 'r2'
// Henüz vs/fs entry point'i yok — canvas hâlâ blank.
08 / render pipeline · entry point
Tam shader: vs + fs
Burada modül "renderable" oluyor.
@vertex fn vs(...) ve @fragment fn fs(...)
eklenince WebGPU bir render pipeline kurabiliyor — canvas birden
açılıyor. Bu, anatomi'nin son halkası: literal → identifier →
const/var → struct → function → entry points. Geri kalan
her şey (UV, SDF, raymarching) bu iskelet üstünde inşa edilir.
cos'u sin yap,
renkler kayar. u.time'ı u.time * 4.0 yap,
hızlanır. Daha derinine inmek için
12 aşamalık playground etüdüne geç.// İSKELET TAMAM. vs + fs eklendi → canvas açıldı.
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> {
// Tek üçgenle ekranı kapla
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 = 0.5 + 0.5 * cos(u.time + uv.xyx + vec3<f32>(0.0, 2.0, 4.0));
return vec4<f32>(c, 1.0);
}