Shader

Materials, your own GLSL colour for anything Kiln draws, and raw vertex and fragment programs.

Shader

Shader(material=None, *, vertex=None, fragment=None)

Your own GLSL colour for whatever is drawn inside with shader:.

Kiln shaders are materials: one GLSL function that receives each pixel’s Material and returns its colour,

struct Material {
    vec4 color;       // the shape's colour (images: texel times tint), 0-1
    vec2 uv;          // 0-1 across the shape's bounding box, or an image's pixels
    vec2 position;    // the pixel's position in window points, origin top-left
    float time;       // seconds since the app started, at this frame's start
    vec2 resolution;  // the window's size in points (an Fbo's, in its pixels)
    vec2 mouse;       // the mouse, in window points
};
vec4 shade(Material m);

It applies to every shape, custom shape, text, image and mesh drawn inside with shader:, and Kiln applies their anti-aliased edges after it, so outlines stay smooth. Inside an Fbo, position and resolution are in the Fbo’s pixels, like everything drawn there; a material may sample other Fbos (upright, in straight alpha, as Fbo.draw shows them) but not the one being drawn into, which raises ValueError. Declare your own uniforms and helper functions above shade; set them with shader["name"] = value. A thousand shapes drawn with one shader are still one draw call. Changing a uniform between two draws gives each draw its own value.

Example:

class Sketch(kiln.App):
    def setup(self):
        self.glow = kiln.Shader('''
            uniform vec3 tint;
            vec4 shade(Material m) {
                return vec4(mix(m.color.rgb, tint, m.uv.y), m.color.a);
            }
        ''')

    def draw(self):
        self.glow["tint"] = kiln.Color(255, 120, 40)
        with self.glow:
            kiln.circle(200, 150, 80)

Raw programs. Shader(vertex=..., fragment=...) (or load) is the other mode: a whole vertex and fragment shader, each with its own main(), for drawing Vbos and Meshes your own way (GPGPU particles, displacement, instancing). A Shader is one or the other; shapes, text and images drawn under a raw program raise RuntimeError (use a material for them). Kiln compiles the code at #version 330 core (or the 400 or 410 you ask for) and binds the Vbo’s data to these vertex inputs, by name; declare the ones you use:

| Input              | Location | From the Vbo (or Mesh)                     |
|--------------------|----------|--------------------------------------------|
| in vec4 position;  | 0        | vertices: x, y, z (w = 1); vec2/vec3 too   |
| in vec4 color;     | 1        | colors, 0-1 straight alpha; white without  |
| in vec3 normal;    | 2        | normals; (0, 0, 1) without                 |
| in vec2 texcoord;  | 3        | tex_coords, 0-1; (0, 0) without            |

Any other input is a named attribute: in float age; reads the Vbo’s upload_attribute("age", ...) (or a Mesh’s attributes["age"]) values. Kiln gives named inputs the locations from 4 up, in declaration order (or the layout location 4-15 you write); each is a float, vec2-vec4, int, ivec2-ivec4, uint or uvec2-uvec4, with data of the same components and number type. Drawing a Vbo that lacks one, or has it in another shape, raises ValueError; a named attribute the shader doesn’t read warns once. A Vbo’s per-instance attributes (instance_attributes=, or divisor=1) are read once per instance of an instanced draw (vbo.draw(instances=n), one draw call), and gl_InstanceID numbers the instances; built-in inputs the Vbo lacks read their constants in every instance. Under a raw program a Mesh is drawn through a Vbo Kiln fills at each draw; for a mesh drawn many times a frame, build a Vbo once and draw that. Kiln sets these uniforms when a stage declares them:

| Uniform                          | Value                                       |
|----------------------------------|---------------------------------------------|
| mat4 modelViewProjectionMatrix   | position to clip space: the current         |
|                                  | transform, then the target's projection     |
| mat4 modelViewMatrix             | the current transform (2D, as a mat4)       |
| mat4 projectionMatrix            | drawing units to clip space, y down; z -> 0 |
| mat4 textureMatrix               | the identity                                |
| vec4 globalColor                 | the tint (``tint``), 0-1                    |
| vec2 resolution                  | the target's size in pixels (gl_FragCoord's) |
| float pixelRatio                 | pixels per drawing unit (1 in an Fbo)       |
| float time                       | seconds since the app started               |
| vec2 mouse                       | the mouse, in window points                 |

The first five are Kiln’s alone: shader[name] = value for one raises. The last four (resolution, pixelRatio, time and mouse) are Kiln additions: once you set one (shader["time"] = t), Kiln stops filling it for that shader and your value wins, and declaring one with another type (Shadertoy’s uniform vec3 resolution) makes it entirely yours, never filled.

The fragment shader writes one out vec4, a straight-alpha colour; Kiln premultiplies it after your main() so every blend mode works as for Kiln’s own drawing, except under Blend.REPLACE, which writes your value unchanged (data passes). Textures bound with shader["name"] = texture read as in a material (uv 0-1 and texelFetch top-left first, over the texture’s own pixels) but as stored: an Fbo’s texels stay premultiplied and float data comes back exactly, so a simulation can keep any value in any channel. with texture: doesn’t reach a raw program. To get straight alpha back from an Fbo’s premultiplied texel (for output under Blend.ALPHA, which would darken it again), both stages have kiln_unpremultiply(vec4):

fragColor = kiln_unpremultiply(texture(canvas, uv));

gl_FragCoord counts pixels from the bottom-left; inside an Fbo, ivec2(gl_FragCoord.x, resolution.y - gl_FragCoord.y) is the texel being written, top-left first. gl_VertexID numbers a Vbo’s vertices, so a Vbo of N points can draw N particles whose positions a vertex shader texelFetches from an Fbo (see the GPGPU tutorial).

render = kiln.Shader(vertex=VERTEX, fragment=FRAGMENT)
with render:
    points.draw()

Parameters

Name Type Description Default
material str | None A material’s GLSL source to load right away, like load_material(material). None
vertex str | PathLike | None A raw program’s vertex shader (source or file), with fragment; loads it like load(vertex, fragment). None
fragment str | PathLike | None Its fragment shader. None

Raises

Name Type Description
TypeError Both a material and a raw program, or only half of one.

Attributes

Name Description
loaded Returns whether a material or raw program is loaded.

Methods

Name Description
load Loads a raw program: a whole vertex and fragment shader.
load_material Loads a material: GLSL code defining vec4 shade(Material m). Kiln-specific.
release Frees the shader’s GPU programs and pipelines.

load

Shader.load(vertex, fragment)

Loads a raw program: a whole vertex and fragment shader.

See the class docstring for the attributes and uniforms Kiln provides. Each argument is GLSL source, or a file: a Path, or a one-line str without {, ; or a newline (load("shader.vert", "shader.frag")). It replaces whatever the shader had loaded, material or program, and resets its uniforms. With an App running it compiles right away; otherwise at the first with shader:.

Parameters
Name Type Description Default
vertex str | PathLike The vertex shader. required
fragment str | PathLike The fragment shader. required
Raises
Name Type Description
ShaderError The code doesn’t compile, or uses an attribute or a built-in uniform Kiln doesn’t provide; the message names the stage and your line. The shader keeps what it had loaded.
ValueError A source is empty.
RuntimeError The shader is active (inside with shader:).
OSError A file can’t be read (FileNotFoundError for a str that is neither GLSL with a main() nor an existing file).
TypeError An argument is neither a str nor a path.

load_material

Shader.load_material(material)

Loads a material: GLSL code defining vec4 shade(Material m). Kiln-specific.

A material is one function, which Kiln compiles into its own shaders so every kind of drawing keeps its anti-aliasing. Uniforms (uniform float speed;, uniform sampler2D photo;) and helper functions go above shade. Loading again replaces the material, and resets its uniforms. With an App running it compiles right away; otherwise at the first with shader:.

The material is GLSL source or a file, by load’s rule: a Path, or a one-line str without {, ; or a newline, is a file.

Parameters
Name Type Description Default
material str | PathLike The material’s GLSL, without #version, or its file. required
Raises
Name Type Description
ShaderError The code doesn’t compile. The message names your lines. The shader keeps the material it had, so a live-coding reload can fail without breaking the sketch.
RuntimeError The shader is active (inside with shader:).
OSError The file can’t be read.
TypeError material is neither a str nor a path.

release

Shader.release()

Frees the shader’s GPU programs and pipelines.

Draws made with it earlier in the frame are kept. The shader is unloaded and can load another material. Shaders are also released when garbage collected, and with their window.

ShaderError

ShaderError(message, lines=None, log='')

A shader that doesn’t compile.

The message lists each error with the line number in your material, or in the vertex or fragment shader of a raw program, which it names (counting the first line as 1), and that line’s code, followed by the driver’s own message.

Attributes

Name Type Description
lines Your line numbers the driver reported, in order.
log The driver’s raw error log, or “” when Kiln caught the problem.