I encountered some problems when using matrix multiplication to transform the coordinates of a point, which made me realize that the default multiplication order of the glm transform functions are opposite to what I need.
The test code is as follows:
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_ENABLE_EXPERIMENTAL
#include <iostream>
#include <glm/glm.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <gtest/gtest.h>
void Print(const std::string &title, glm::vec3 point) {
std::cout << std::fixed << std::setprecision(3)
<< title << ": " << point.x << ", " << point.y << ", " << point.z << std::endl;
}
glm::vec3 RotateAroundPointA(glm::vec3 ori_point, glm::vec3 center, float rotation_x, float rotation_y) {
std::cout << "-- RotateAroundPointA ----------------------------------------------------------" << std::endl;
auto transform = glm::translate(glm::mat4(1), -center);
auto curr_point = transform * glm::vec4(ori_point, 1);
Print("Recenter ", curr_point);
transform = glm::rotate(transform, rotation_x, glm::vec3(1, 0, 0));
curr_point = transform * glm::vec4(ori_point, 1);
Print("RotateX ", curr_point);
transform = glm::rotate(transform, rotation_y, glm::vec3(0, 1, 0));
curr_point = transform * glm::vec4(ori_point, 1);
Print("RotateY ", curr_point);
transform = glm::translate(transform, center);
curr_point = transform * glm::vec4(ori_point, 1);
Print("Move back", curr_point);
curr_point = transform * glm::vec4(ori_point, 1);
Print("Returned ", curr_point);
return curr_point;
}
glm::vec3 RotateAroundPointB(glm::vec3 ori_point, glm::vec3 center, float rotation_x, float rotation_y) {
std::cout << "-- RotateAroundPointB ----------------------------------------------------------" << std::endl;
auto recenter = glm::translate(glm::mat4(1), -center);
auto curr_point = recenter * glm::vec4(ori_point, 1);
Print("Recenter ", curr_point);
auto rotate_x = glm::rotate(glm::mat4(1), rotation_x, glm::vec3(1, 0, 0));
curr_point = rotate_x * curr_point;
Print("RotateX ", curr_point);
auto rotate_y = glm::rotate(glm::mat4(1), rotation_y, glm::vec3(0, 1, 0));
curr_point = rotate_y * curr_point;
Print("RotateY ", curr_point);
auto move_back = glm::translate(glm::mat4(1), center);
curr_point = move_back * curr_point;
Print("Move back", curr_point);
curr_point = move_back * rotate_y * rotate_x * recenter * glm::vec4(ori_point, 1);
Print("Returned ", curr_point);
return curr_point;
}
TEST (GlmTests, Default) {
const glm::vec3 kCenter{1, 1, 1};
const glm::vec3 kOriPoint(kCenter.x, kCenter.y, kCenter.z 1);
const auto kOriDistance = glm::distance(kOriPoint, kCenter);
const auto kPi = glm::pi<float>();
auto curr_point = RotateAroundPointA(kOriPoint, kCenter, kPi / 4, kPi / 4);
EXPECT_FLOAT_EQ(kOriDistance, glm::distance(curr_point, kCenter));
curr_point = RotateAroundPointB(kOriPoint, kCenter, kPi / 4, kPi / 4);
EXPECT_FLOAT_EQ(kOriDistance, glm::distance(curr_point, kCenter));
}
The output is:
-- RotateAroundPointA ----------------------------------------------------------
Recenter : 0.000, 0.000, 1.000
RotateX : 0.000, -1.707, 1.121
RotateY : 1.121, -0.793, 0.207
Move back: 2.536, -0.086, 0.914
Returned : 2.536, -0.086, 0.914
D:\cpp\test20\test\glm_tests.cpp(70): error: Expected equality of these values:
kOriDistance
Which is: 1
glm::distance(curr_point, kCenter)
Which is: 1.8825928
-- RotateAroundPointB ----------------------------------------------------------
Recenter : 0.000, 0.000, 1.000
RotateX : 0.000, -0.707, 0.707
RotateY : 0.500, -0.707, 0.500
Move back: 1.500, 0.293, 1.500
Returned : 1.500, 0.293, 1.500
I googled and found someone said:
Note that GLM transformation functions, like glm::rotate, apply the transformation on the right; i.e.
m = m * glm::rotate(glm::mat4(1.0f), glm::radians(5.f), axis);
So, what is the reason that GLM is designed to have the opposite order as compared to the general matrix multiplication?
CodePudding user response:
It isn't the "opposite order"; it is the correct order... from a certain point of view.
What we're doing is right-multiplication. That means if you have a transform T and you want to transform a point P by it, you perform T * P
.
So if you have two transforms T1 and T2, if you do this: T2 * (T1 * P)
, then that means "transform P by T1, then transform the result by T2". Matrix multiplication is associative, so that's equivalent to (T2 * T1) * P
.
So if you want to transform P by T1 then T2, you would build the appropriate matrix by generating T2, then right-multiplying T1 into it. If you have 3 transforms in sequence from T1 to T2 to T3, you do ((T3 * T2) * T1)
.
It is the correct order relative to how you intend to transform your vectors. The first transform you build is the last one that gets applied to the point.