具有缩放拉伸纵横比的 glm 旋转

Nil*_*Qor 1 c++ opengl scaling rotation glm-math

我正在尝试使用 glm 旋转纹理图像,但输出看起来被拉伸或未沿 z 轴正确旋转。对于这个问题,可能的解决方案是什么?

在此输入图像描述

float imgAspectRatio = imageWidth / (float) imageHeight;
float viewAspectRatio = viewWidth / (float) viewHeight;

if (imgAspectRatio > viewAspectRatio) {
    yScale = viewAspectRatio / imgAspectRatio;
} else {
    xScale = imgAspectRatio / viewAspectRatio;
}

glm::mat4 model(1.0f);
model = glm::translate(model, glm::vec3(0, 0, 0));
model = glm::rotate(model, glm::radians(angle),
                    glm::vec3(0, 0, 1));
model = glm::scale(model, glm::vec3(xScale, yScale, 1.0f));
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述 有些人建议与 glm::ortho 相乘,但它给出的是正方形

glm::mat4 projection(1.0f);    
projection = glm::ortho(
              -imgAspectRatio,             
              imgAspectRatio,              
              -1.0f,      
              1.0f,       
              -1.0f,              
              1.0f                
      );
Run Code Online (Sandbox Code Playgroud)

Fil*_*der 5

如果您有一个方形窗口,您当前拥有的代码实际上可以工作。

通过将图像纵横比缩放和视图纵横比缩放合并为一个步骤,您当前的操作顺序基本上如下:

  1. 按图像长宽比缩放图像平面。
  2. 按视图纵横比缩放场景中的所有 xy 坐标。
  3. 旋转图像平面。
  4. 平移图像平面。

但是,您确实希望在最后按视图纵横比进行缩放。一个简单的练习可以帮助形象化原因:假设您有一个完美的方形窗口,在其中渲染一个 xy 坐标为 (-1, -1), (1, -1), (0, 1) 的三角形。现在假设您的窗口高度加倍,但您希望形状保持不变:显然,您只需将每个 y 坐标乘以 1/2 即可。现在假设您想要将三角形逆时针旋转 90 度,但仍保持形状不变。您是否先旋转,然后按视图纵横比缩放?或相反亦然?通过运行这两个选项,很明显您必须最后按视图纵横比进行缩放。

换句话说,您需要以下顺序:

  1. 按图像长宽比缩放图像平面。
  2. 旋转图像平面。
  3. 平移图像平面。
  4. 按视图纵横比缩放场景中的所有 xy 坐标。

这是通过如下代码实现的:

    float xScaleImg = 1.0f;
    float yScaleImg = xScaleImg / imgAspectRatio;

    float xScaleView = 1.0f;
    float yScaleView = viewAspectRatio;

    glm::mat4 model(1.0f);
    model = glm::scale(model, glm::vec3(xScaleView, yScaleView, 1.0f));
    model = glm::translate(model, glm::vec3(0, 0, 0));
    model = glm::rotate(model, glm::radians(angle), glm::vec3(0, 0, 1));
    model = glm::scale(model, glm::vec3(xScaleImg, yScaleImg, 1.0f));
Run Code Online (Sandbox Code Playgroud)

前四行假设(如示例中的情况)视图和图像宽度都大于各自的高度。如果情况相反,您可能需要添加一些逻辑来改变这种情况。

更新: 根据您想要实现的翻译效果,您可能需要将glm::translate(...)命令向上移动一行。我在原始答案中给出的顺序使平移单位以像素为单位相等。例如,如果您传入glm::vec3(1.0f, 1.0f, 0.0f),并且窗口的宽度为 1280 像素,则图像将向左平移 640 像素,向上平移 640 像素。但是,您可能希望在两个轴上保持 OpenGL [-1, 1] 范围。也就是说,当您传递平移时glm::vec3(1.0f, 1.0f, 0.0f),您可能希望将图像向右平移窗口宽度的一半,向上平移窗口高度的一半。在这种情况下,您需要将翻译作为对图像执行的最后一个操作,这是通过编写glm::translate(...)第一行代码来完成的。这就是尼罗·科尔想要的。在这种情况下,代码的最后几行变成如下所示:

    glm::mat4 model(1.0f);
    model = glm::translate(model, glm::vec3(1.0f, 1.0f, 0));
    model = glm::scale(model, glm::vec3(xScaleView, yScaleView, 1.0f));
    model = glm::rotate(model, glm::radians(angle), glm::vec3(0, 0, 1));
    model = glm::scale(model, glm::vec3(xScaleImg, yScaleImg, 1.0f));
Run Code Online (Sandbox Code Playgroud)

有关更多上下文,您可以查看此答案的评论部分中的讨论。