#version 460 struct Transformation { vec4 rotation; vec3 translation; }; layout(binding = 0) uniform UniformBlock { vec3 scale; Transformation model; Transformation view; mat4 projection; } uniform_block; layout(location = 0) in vec2 inPosition; layout(location = 1) in vec3 inColor; layout(location = 2) in vec2 inTextureCoordinate; layout(location = 0) out vec3 fragmentColor; layout(location = 1) out vec2 fragmentTextureCoordinate; // ///////////////// // // Quaternions // // ///////////////// // // To mathematicians, the components of a quaternion are traditionally // multiplied by 1, i, j, and k, where 1 is a real number and i, j, and k are // unit values along the three imaginary axes. GLSL doesn't give us component // names that map cleanly onto these, and xyzw are confusing in this context // because it's unclear whether x or w is the real-valued component, so we // reference the components by numeric index. Yes, that sucks, but everything // else sucks worse. // // That is: 0 is the real value and 1, 2, and 3 are the i, j, and k values. // // When reading code samples elsewhere, keep in mind that this is not the // only possible choice. Checking how components are labeled should be the // very first thing you do in understanding any quaternion arithmetic, // anywhere. Also keep in mind that there are many equivalent ways to write // any algebraic expression, and check carefully whether things are identical // in meaning. // // This same challenge arises with matrices, it's just that the designers // of GLSL have chosen to encapsulate that complexity inside the language. // This is kept carefully the same as quaternion_conjugate() in // linear_algebra.rs. vec4 quaternion_conjugate(vec4 a) { return vec4(a[0], -a[1], -a[2], -a[3]); } // This is kept carefully the same as quaternion_product() in // linear_algebra.rs. vec4 quaternion_product(vec4 a, vec4 b) { return vec4((a[0] * b[0]) - (a[1] * b[1]) - (a[2] * b[2]) - (a[3] * b[3]), (a[0] * b[1]) + (a[1] * b[0]) + (a[2] * b[3]) - (a[3] * b[2]), (a[0] * b[2]) - (a[1] * b[3]) + (a[2] * b[0]) + (a[3] * b[1]), (a[0] * b[3]) + (a[1] * b[2]) - (a[2] * b[1]) + (a[3] * b[0])); } vec3 quaternion_rotate(vec3 position, vec4 rotation) { vec4 result = quaternion_product(quaternion_product(rotation, vec4(0.0, position)), quaternion_conjugate(rotation)); return vec3(result[1], result[2], result[3]); } vec3 transform(vec3 a, Transformation transformation) { vec3 rotated = quaternion_rotate(a, transformation.rotation); vec3 translated = rotated + transformation.translation; return translated; } vec4 apply_all_transforms(vec3 position) { vec3 scaled = vec3(position.x * uniform_block.scale.x, position.y * uniform_block.scale.y, position.z * uniform_block.scale.z); vec3 model = transform(scaled, uniform_block.model); vec3 view = transform(model, uniform_block.view); vec4 projected = vec4(view, 1.0) * uniform_block.projection; return projected; } void main() { gl_Position = apply_all_transforms(vec3(inPosition, 0.0)); fragmentColor = inColor; fragmentTextureCoordinate = inTextureCoordinate; }