Quick answer: Add row_major qualifier to the HLSL matrix uniform, or transpose the matrix in code before shader_set_uniform_matrix.
A 3D-effect shader expects a world transformation matrix. Object rotates around the wrong axis. Translation in the wrong direction. The matrix arrived transposed.
Row-Major Qualifier in HLSL
row_major float4x4 u_world;
PS_OUTPUT vs_main(VS_INPUT v) {
PS_OUTPUT o;
o.pos = mul(v.pos, u_world);
return o;
}
Add row_major in front of the type. HLSL adjusts multiplication semantics — data is row-major, math still works correctly.
GLSL Equivalent
For OpenGL targets (HTML5, Linux):
layout(row_major) uniform mat4 u_world;
Or transpose in shader code: u_world = transpose(u_world). Match the convention; pick one.
Or Transpose in Code
function transpose_matrix(m) {
return [m[0], m[4], m[8], m[12],
m[1], m[5], m[9], m[13],
m[2], m[6], m[10], m[14],
m[3], m[7], m[11], m[15]];
}
shader_set_uniform_matrix_array(u_world_loc, transpose_matrix(world_mat));
Transposes 4×4 in GML. Costly per-frame; prefer the row_major qualifier in shader.
Multiplication Direction
In HLSL, mul(v, m) treats v as row vector; mul(m, v) treats v as column. Pick one convention; if results look transposed, swap multiplication order.
Verify with Identity
Send the identity matrix from CPU. Object should render unchanged. Any transformation = orientation mismatch — either data or shader convention is wrong.
Verifying
Send a known rotation (45° around Y). Object rotates as expected. Translation moves correctly. Cross-test on each target platform (HTML5 + Win + macOS).
“Matrix layout is a convention. Pick row-major end-to-end and the math works.”
For shaders shared across engines, document matrix layout in a header comment — saves the next dev hours of debugging.