Quick answer: Declare UBO blocks identically across shaders. Use layout(std140, binding = N) consistently. Place the block definition in a shared header file.

A multi-pass renderer shares camera matrices via a UBO at binding 0. Shader A renders correctly. Shader B renders with wrong projection. Same UBO, different layouts.

The Bug

// shader A
layout(std140, binding = 0) uniform Camera {
    mat4 view;
    mat4 projection;
};

// shader B
layout(std140, binding = 0) uniform Camera {
    mat4 projection;     // swapped!
    mat4 view;
};

Both shaders bind the same buffer but interpret it differently. B reads view’s memory as projection.

Fix: Shared Header

// camera_block.glsl — included in every shader
layout(std140, binding = 0) uniform Camera {
    mat4 view;
    mat4 projection;
    vec3 cameraPos;
};

Define once, include in all shaders. GLSL doesn’t have native #include — preprocess offline or use ARB_shading_language_include if available.

std140 Padding Rules

std140 layout has strict alignment: vec3 takes 16 bytes, arrays stride to 16, etc. Common mistake:

layout(std140, binding = 0) uniform Data {
    float a;        // offset 0, padded to 16
    vec3 b;         // offset 16, occupies 12, padded to 32
    float c;        // offset 28!? No — std140 puts it at 28 but next aligns to 16
};

If your CPU struct doesn’t match this padding, the GPU reads garbage. Match the CPU struct using explicit alignas or careful field ordering.

CPU Side

struct alignas(16) CameraData {
    glm::mat4 view;
    glm::mat4 projection;
    glm::vec3 cameraPos;
    float _padding;
};

glBindBufferBase(GL_UNIFORM_BUFFER, 0, cameraUBO);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(CameraData), &cameraData);

Bind once per frame. Update buffer when camera changes. Shaders auto-pick up.

Verifying

Render with both shaders displaying view matrix; both show identical scene. Edit camera in real time; both shaders update. RenderDoc UBO inspector confirms member layout.

“UBO is a memory contract between CPU and shaders. Break the contract, get visible bugs.”

For cross-shader sharing, always std140. std430 is denser but only available for SSBOs — UBOs need std140 for portability.