【问题标题】:glm rotation with scaling stretched aspect ratio具有缩放拉伸纵横比的 glm 旋转
【发布时间】:2020-11-25 10:18:01
【问题描述】:

我正在尝试使用 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));

一些建议与 glm::ortho 相乘,但它给出方形

glm::mat4 projection(1.0f);    
projection = glm::ortho(
              -imgAspectRatio,             
              imgAspectRatio,              
              -1.0f,      
              1.0f,       
              -1.0f,              
              1.0f                
      );

【问题讨论】:

  • 如何变换顶点?如果你计算一个模型视图投影矩阵,它必须是mvp = projection * model;。如果在着色器代码中应用这两个矩阵,那么它必须是gl_Position = projection * model * vertex;
  • 转换仅在 cpu 中完成,例如 mvp= projection * model;,在着色器中就像 gl_Position = modelMat*vec4(vertex, 1.0);

标签: c++ opengl rotation scaling glm-math


【解决方案1】:

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

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

  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));

与您的示例一样,前四行假设视图和图像的宽度都大于各自的高度。如果反过来为真,您可能需要添加一些逻辑来改变这一点。

更新: 根据您想要实现的翻译效果,您可能需要将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(...) 设置为第一行代码来完成的。这就是 Nile Qor 想要的。在这种情况下,代码的最后几行变成这样:

    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));

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

【讨论】:

  • 抱歉回复缓慢。您能否详细说明您想要翻译的内容与您得到的内容?如果您不希望/不需要两个轴上的翻译“单位”匹配,而是希望纯粹根据 [-1, 1] OpenGL 范围进行翻译,那么您可以将调用移至 glm::translate( ) 上一行。
  • 你是救生员,这对我帮助很大,我很笨,用你在最后一步添加的 glm::mat4(1.0f) 进行翻译。谢谢大哥。
  • 太棒了!高兴听到!很抱歉造成任何混乱;我会改进我原来的答案,以便在其他人遇到这个问题时更清楚。
  • 对于最新的编辑,如果您在翻译之前先进行缩放是正确的,例如 model = glm::translate(model, glm::vec3(xTans, yTrans, 0)); model = glm::scale(model, glm::vec3(xScaleView, yScaleView, 1.0f)); ,不确定其他情况,但在我的情况下这是必需的。
  • 对,我的编辑刚开始只是解决了 mat4(1.0f) 的问题,但我想包含关于翻译的免责声明也很好,以防人们不阅读这些 cmets。我已经再次更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-17
  • 1970-01-01
  • 2014-02-16
相关资源
最近更新 更多