Home > Software design >  What's the meaning of the right multiplication of the GLM transform functions?
What's the meaning of the right multiplication of the GLM transform functions?

Time:01-29

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.

  • Related