Custom Shaders

ShaderMaterial lets you write custom GLSL vertex and fragment shaders for any MeshInstance3D.

Basic Usage

from simvx.graphics.materials.custom_shader import ShaderMaterial
from simvx.core import MeshInstance3D, Mesh

mat = ShaderMaterial(
    vertex_source="""
    #version 450
    layout(location = 0) in vec3 position;
    layout(push_constant) uniform PC { mat4 mvp; };
    void main() {
        gl_Position = mvp * vec4(position, 1.0);
    }
    """,
    fragment_source="""
    #version 450
    layout(location = 0) out vec4 frag_colour;
    layout(set = 2, binding = 0) uniform UBO { float time; vec3 tint; };
    void main() {
        frag_colour = vec4(tint * (0.5 + 0.5 * sin(time)), 1.0);
    }
    """,
)
mat.set_uniform("time", 0.0)
mat.set_uniform("tint", (1.0, 0.3, 0.1))

cube = MeshInstance3D(mesh=Mesh.cube(), material=mat)
self.add_child(cube)

Update uniforms each frame in process():

def process(self, dt):
    mat.set_uniform("time", self.elapsed_time)

Loading from Files

mat = ShaderMaterial(
    vertex_path="shaders/wave.vert",
    fragment_path="shaders/wave.frag",
)

GLSL files are compiled to SPIR-V automatically via glslc. Hot-reload is supported — modified shader files are detected and recompiled at runtime.

Uniforms

Set uniforms by name. Types are inferred from the Python value:

mat.set_uniform("speed", 2.5)                    # float
mat.set_uniform("offset", (1.0, 0.0))            # vec2
mat.set_uniform("colour", (1.0, 0.5, 0.0))       # vec3
mat.set_uniform("tint", (1.0, 0.5, 0.0, 1.0))   # vec4
mat.set_uniform("count", 10)                      # int

For explicit type control:

mat.set_uniform_typed("grid_size", (8, 8), "ivec2")

Supported types: float, int, uint, vec2, vec3, vec4, ivec2, ivec3, ivec4, mat4.

Example

See packages/graphics/examples/3d_custom_shader.py for custom ShaderMaterial with animated uniforms.

API Reference

See simvx.graphics.materials.custom_shader for the complete shader API.