Zum Inhalt springen

Uniforms

Während Vertex-Attribute für jeden Vertex unterschiedliche Werte liefern, verwenden wir Uniforms für Daten, die während eines Draw-Calls für alle verarbeiteten Vertices beziehungsweise Fragmente gleich bleiben.

Typische Beispiele sind die Richtung einer Lichtquelle, die Farbe eines Materials oder Einstellungen für die Beleuchtungsberechnung.

In unserem Phong-Beispiel haben wir bereits Uniforms verwendet. Die benötigten Werte fassen wir in einer Struktur zusammen:

WGSL
struct Uniforms {
color: vec4f,
lightDirection: vec4f,
viewDirection: vec4f,
ambientFactor: f32,
diffuseStrength: f32,
shininess: f32,
};

Anschließend deklarieren wir eine Variable, über die der Shader auf diese Daten zugreifen kann:

WGSL
@group(0) @binding(0)
var<uniform> uniforms: Uniforms;

Dabei haben die einzelnen Bestandteile folgende Bedeutung:

  • var<uniform> legt fest, dass die Variable Daten aus dem Uniform-Adressraum enthält.
  • @group(0) bezeichnet die Bind-Gruppe, über die die Daten bereitgestellt werden.
  • @binding(0) bezeichnet den Eintrag innerhalb dieser Bind-Gruppe.
  • uniforms: Uniforms gibt der Variablen ihren Namen und legt ihren Datentyp fest.

Die Werte stammen aus einem Uniform-Buffer, den wir wie gewohnt in JavaScript erstellen und über eine Bind-Gruppe bereitstellen.

Auf die einzelnen Werte greifen wir über den Variablennamen und das entsprechende Feld der Struktur zu:

WGSL
let lightDir = normalize(uniforms.lightDirection.xyz);
let materialColor = uniforms.color.rgb;
let ambient = uniforms.ambientFactor;

So können wir dieselben Licht- und Materialeigenschaften bei der Verarbeitung vieler Vertices oder Fragmente verwenden.

Uniforms sind im Shader schreibgeschützt. Wir können ihre Werte lesen, aber nicht innerhalb einer Shader-Funktion verändern.

In JavaScript können wir dagegen den Inhalt des Uniform-Buffers zwischen Draw-Calls aktualisieren. So lassen sich beispielsweise die Lichtrichtung oder die Materialfarbe verändern, ohne den Shader-Code anzupassen.

Uniforms können sowohl im Vertex-Shader als auch im Fragment-Shader verwendet werden.

Beide Shader-Stufen können auf dieselbe Uniform-Variable zugreifen, sofern die entsprechende Bindung für sie verfügbar ist.

Der Unterschied zu Vertex-Attributen ist dabei entscheidend: Vertex-Attribute liefern individuelle Daten für jeden Vertex. Uniforms stellen gemeinsame Werte bereit, die sich nicht von Vertex zu Vertex oder Fragment zu Fragment ändern.

Uniforms verwenden wir für gemeinsame Shader-Daten wie Licht-, Material- oder Kameraeigenschaften.

In WGSL deklarieren wir sie mit var<uniform> und ordnen sie über @group und @binding einem Uniform-Buffer zu. Der Shader kann diese Werte lesen, während JavaScript den Buffer für spätere Draw-Calls aktualisieren kann.