Skip to content

ShaderTool

This tool parses GLSL shader files (mainly *.vert, *.frag, *.geom and *.comp) and generates C++ source files for them.

You automatically get the shaders added back to the code after saving a shader file and trigger a re-build.

The cmake macros expect the shader source below the module in a shaders/ directory.

set(SHADERS first second)
engine_generate_shaders(mymodulename ${SHADERS})

The shaders given in this example would be located at src/modules/mymodulename/shaders/first.*. The tool automatically detects the type of programs that should be connected in the final shader.

The code is generated into the build directory in generated/shaders.

Description

The generator uses ShaderTemplate.h.in and UniformBufferTemplate.h.in as a base to generate the C++ source files.

There are several variables in the template file that are replaced by the generator.

  • $includes$
  • $namespace$
  • $name$

  • $setters$

  • $attributes$
  • $uniforms$
  • $uniformarrayinfo$

  • $shutdown$

  • $uniformbuffers$

The parser includes a preprocessor.

You can export constants from the GLSL shader code to the generated C++ code by using $constant.

Use $constant varname 42 to generate a method that returns 42 with a name getVarname

Uniform Buffer Objects (UBO)

Uniform buffer objects allow you to share uniform data between multiple shaders efficiently. The shadertool generates C++ structs that match the GLSL memory layout.

layout(std140) uniform MaterialBlock {
    vec4 diffuseColor;
    vec4 specularColor;
    float shininess;
};

This generates a MaterialBlockData struct with proper std140 alignment and padding. The generated code includes: - A struct with correct padding for the memory layout - Methods to create and update the uniform buffer - Static assertions to verify struct size at compile time

Shader Storage Buffer Objects (SSBO)

SSBOs allow read/write access to buffer data in shaders. They are parsed using the buffer keyword and support the std430 layout which has tighter packing rules than std140.

layout(std430, binding = 0) buffer ParticleBuffer {
    vec4 positions[64];
    vec4 velocities[64];
    float masses[];  // Dynamic array (must be last member)
};

This generates: - A ParticleBufferData struct with std430 alignment - A separate header file YourShaderSSBO.h with the buffer wrapper class using video::ShaderStorageBuffer - Methods for creating, updating, and binding the buffer - The binding index as a static constexpr

Generated SSBO Class

The generated SSBO wrapper class provides:

// Create the buffer with initial data
ssbo.create(&data, count);

// Update the buffer
ssbo.update(&data, count);

// Bind to the shader binding point
ssbo.bind();

// Access the underlying video::ShaderStorageBuffer
ssbo.getParticleBufferBuffer();

std430 vs std140 Layout

The key difference between std430 (SSBOs) and std140 (UBOs) is array padding: - std140: Arrays of scalars (float, int) are padded to vec4 boundaries (16 bytes) - std430: Arrays use natural alignment without vec4 padding

// std430 example - tighter packing
layout(std430, binding = 1) buffer DataBuffer {
    mat4 transforms[16];
    int count;
    uint flags;
};

Dynamic Arrays

SSBOs support unsized arrays as the last member:

layout(std430, binding = 0) buffer DynamicBuffer {
    int fixedData;
    float dynamicArray[];  // Size determined at runtime
};

The generated struct uses [1] as a placeholder, and you allocate the actual size when creating the buffer.