Zum Inhalt springen

Fragment Shader

Nachdem wir den Vertex-Shader betrachtet haben, wenden wir uns dem Fragment-Shader unseres Beispiels zu. Er ist für das schwarz-weiße Streifenmuster und die Beleuchtung der Kugel verantwortlich.

WGSL
@fragment
fn fragmentMain(
@location(0) worldNormal: vec3f,
@location(1) worldPosition: vec3f
) -> @location(0) vec4f {
let normal = normalize(worldNormal);
let lightDir = normalize(uniforms.lightDirection.xyz);
let viewDir = normalize(
uniforms.cameraPosition.xyz - worldPosition
);
// Geschwungene schwarz-weiße Streifen
let stripePosition =
worldPosition.y * 180.0 +
sin(worldPosition.x * 80.0) * 5.0;
let stripe = smoothstep(
-0.15,
0.15,
sin(stripePosition)
);
let zebraColor = mix(
vec3f(0.025, 0.025, 0.035),
vec3f(0.95, 0.95, 0.90),
stripe
);
// Phong-Beleuchtung
let ambient =
uniforms.ambientFactor * zebraColor;
let diffuseBrightness =
max(dot(normal, lightDir), 0.0);
let diffuse =
zebraColor *
diffuseBrightness *
uniforms.diffuseStrength;
let reflected = reflect(-lightDir, normal);
let specularFactor = pow(
max(dot(reflected, viewDir), 0.0),
uniforms.shininess
);
let specular = vec3f(1.0) * specularFactor;
return vec4f(
ambient + diffuse + specular,
1.0
);
}

Auch hier sollten wir bereits einiges wiedererkennen.

Mit @fragment kennzeichnen wir den Einstiegspunkt des Fragment-Shaders. Die Parameter worldNormal und worldPosition sind Interstage-Variablen. Ihre Werte stammen aus dem Vertex-Shader und wurden während der Rasterisierung interpoliert.

Über uniforms greifen wir auf die gemeinsamen Uniform-Werte zu, die wir bereits im vorherigen Abschnitt kennengelernt haben.

Auch die Datentypen f32, vec3f und vec4f, die Vektorkomponenten .xyz, .x und .y sowie die Rechenoperationen sollten uns vertraut sein.

Der Rückgabewert @location(0) vec4f enthält die berechnete Fragmentfarbe. Die vier Komponenten stehen für Rot, Grün, Blau und Alpha.

Unser Fragment-Shader kombiniert die bereits bekannte Phong-Beleuchtung mit einem prozedural erzeugten Streifenmuster. Die Funktionen sin(), smoothstep() und mix() zeigen dabei, wie sich Oberflächenfarben direkt im Shader berechnen lassen.

Das ist allerdings nur ein kleiner Ausschnitt dessen, was Fragment-Shader leisten können. In späteren Kapiteln beschäftigen wir uns ausführlicher mit Farben und Farbräumen, Texturen und UV-Koordinaten, Materialien sowie Transparenz und Blending.

Dabei werden wir sehen, wie sich das Erscheinungsbild dreidimensionaler Oberflächen gezielt gestalten lässt – von einfachen Farben über Bildtexturen bis hin zu komplexeren Materialeffekten.