Pass CPU-side data into Kraken fragment shaders.
Uniforms are small packets of data you send from Python to a shader. Use them for values that change per frame or per draw call: time, tint, strength, resolution, offsets, thresholds, and similar effect controls.
Shader.set_uniform(binding, data) accepts a buffer-like object containing the exact bytes your shader expects.
That can be bytes, bytearray, memoryview, or a small class that exposes bytes through a method or property you control.
The Python bytes and the shader uniform block must have the same field order, sizes, and padding.
For portable fragment uniforms, prefer packing values into 16-byte groups, such as float4 in HLSL or vec4 in GLSL.
This shader expects one uniform buffer at binding 0.
The buffer is a single float4: time, strength, scale, and an unused padding value.
cbuffer WindUniform : register(b0, space3) {
float4 wind; // x = time, y = strength, z = scale, w = unused
};
The matching Python data can be packed with struct:
import struct
def pack_wind_uniform(time: float, strength: float, scale: float) -> bytes:
return struct.pack("<4f", time, strength, scale, 0.0)
"<4f" means four little-endian 32-bit floats.
That produces 16 bytes, matching the shader's float4 or vec4.
For anything you update every frame, a tiny data structure keeps the code pleasant without hiding the byte layout.
from dataclasses import dataclass
import struct
@dataclass
class WindUniform:
time: float = 0.0
strength: float = 1.0
scale: float = 0.1
def to_bytes(self) -> bytes:
return struct.pack("<4f", self.time, self.strength, self.scale, 0.0)
Then create the shader with one uniform buffer:
wind_shader = kn.shaders.Shader(
"assets/shaders/wind.frag",
uniform_buffer_count=1,
sampler_count=2,
)
Update and send the uniform before drawing with the shader:
wind = WindUniform(strength=0.8, scale=0.1)
wind.time = kn.time.get_elapsed()
wind_shader.set_uniform(0, wind.to_bytes())
The 0 in set_uniform(0, ...) matches register(b0, space3) in HLSL and layout(set = 3, binding = 0) in GLSL.
If you use more than one uniform buffer, keep the bindings explicit and sequential:
cbuffer WindUniform : register(b0, space3) {
float4 wind;
};
cbuffer ColorUniform : register(b1, space3) {
float4 tint;
};
shader = kn.shaders.Shader(
"assets/shaders/effect.frag",
uniform_buffer_count=2,
sampler_count=1,
)
shader.set_uniform(0, wind_uniform.to_bytes())
shader.set_uniform(1, tint_uniform.to_bytes())
The constructor count is not optional bookkeeping. It tells Kraken how many uniform bindings the shader will use, so set it to the highest binding you use plus one.
Common mistake:
Changing a Python object does not update the GPU by itself. After changing fields like time or strength, call shader.set_uniform(binding, data) with freshly packed bytes.