【问题标题】:OpenGL: scale then translate? and how?OpenGL:缩放然后平移?如何?
【发布时间】:2011-01-16 13:34:09
【问题描述】:

我有一些 2D 几何图形。我想在我的几何图形周围做一些边界矩形,然后在平面上的其他地方渲染一个较小的版本。这里或多或少是我必须进行缩放和翻译的代码:

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
float translateX = dest.x - source.x;
float translateY = dest.y - source.y;

glScalef(scaleX, scaleY, 0.0);
glTranslatef(translateX, translateY, 0.0);
// Draw geometry in question with its normal verts.

当 dest 原点为 0 时,对于给定维度,这完全符合预期。但是,如果 x 的原点不为零,则结果仍然可以正确缩放,但看起来像 (?) 它被转换为接近零的值无论如何,在那个轴上 - 事实证明它与 dest.x 为零时不完全相同。

有人能指出我明显遗漏的东西吗?

谢谢!

最终更新 根据 Bahbar 和 Marcus 在下面的回答,我做了更多的实验并解决了这个问题。亚当鲍文的评论是提示。我遗漏了两个关键事实:

  1. 我需要围绕我关心的几何图形的中心进行缩放。
  2. 我需要以与直觉相反的顺序应用变换(对我而言)。

回想起来,第一个是显而易见的。但是对于后者,对于像我这样的其他优秀程序员/糟糕的数学家:结果我的直觉是在Red Book 所谓的“大固定坐标系”中运行,其中有一个绝对平面,并且您的几何图形四处移动在那个平面上使用变换。这没关系,但考虑到将多个变换叠加到一个矩阵背后的数学性质,这与实际工作方式相反(请参阅下面的答案或红皮书了解更多信息)。基本上,这些转换以“相反的顺序”“应用”到它们在代码中的出现方式。这是最终的工作解决方案:

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
Point sourceCenter = centerPointOfRect(source);
Point destCenter = centerPointOfRect(dest);

glTranslatef(destCenter.x, destCenter.y, 0.0);
glScalef(scaleX, scaleY, 0.0);
glTranslatef(sourceCenter.x * -1.0, sourceCenter.y * -1.0, 0.0);
// Draw geometry in question with its normal verts.

【问题讨论】:

    标签: opengl graphics transform


    【解决方案1】:

    在 OpenGL 中,您指定的矩阵乘以现有矩阵的右侧,顶点位于表达式的最右侧。

    因此,您指定的最后一个操作位于几何本身的坐标系中。 (第一个通常是视图变换,即与您的相机的世界变换相反。)

    Bahbar 提出了一个很好的观点,即您需要考虑缩放中心点。 (或旋转的枢轴点。)通常你在那里平移,旋转/缩放,然后平移回来。 (或者一般来说,应用基础变换,操作,然后是逆)。这称为Change of Basis,您可能想了解一下。

    无论如何,要了解它的工作原理,请尝试使用一些简单的值(零等),然后稍微改变它们(可能是动画),看看输出会发生什么。然后更容易看到你的变换实际上对你的几何体做了什么。

    更新

    订单被“颠倒”w.r.t。直觉在初学者 OpenGL 编码器中相当普遍。我一直在辅导计算机图形课程,许多人的反应方式类似。如果您在渲染变换和几何图形的树(场景图)时考虑使用 pushmatrix/popmatrix,则更容易思考 OpenGL 是如何做到的。然后,当前的事情顺序变得相当自然,而相反的情况会使完成任何有用的事情变得相当困难。

    【讨论】:

    • 简单来说就是把顺序改成glTranslate(dest); glScale(比例); glTranslate(source);
    • Adam:这似乎不像你建议的那样工作,虽然我很感兴趣——你能详细说明为什么排序会这样,也许可以提供一个更明确的想法看起来相对于现有代码?也许我只是误解了您的伪代码。谢谢!
    • 没关系:在这种情况下,“不起作用”是一个粗心的值。修好之后,我们就开始营业了。有关更多信息,请参阅更新的问题。谢谢!
    【解决方案2】:

    缩放,就像旋转一样,从原点开始操作。因此,如果将跨越段 [10:20](例如在 X 轴上)的对象缩放一半,则会得到 [5:10]。因此,对象被缩放,并移近原点。正是你观察到的。

    这就是为什么您通常首先应用 Scale(因为对象往往定义在 0 附近)。

    因此,如果您想围绕中心点缩放对象,可以将对象从中心平移到原点,在此处缩放,然后再平移回来。

    旁注,如果您先翻译,然后缩放,那么您的缩放将应用于之前的翻译,这就是您可能在使用此方法时遇到问题的原因。

    【讨论】:

    • 感谢您的回复。你能拼出一个矩阵数学笨蛋我会做什么操作,做适当的翻译到原点,然后缩放,然后翻译回来吗?在绘制几何图形之前,我尝试在三个调用中连续堆叠这三件事(按源原点的负数平移,然后按比例因子缩放,然后按目标原点平移),但没有得到可见的结果。
    • 啊,也许我需要相对于矩形的中心而不是原点进行翻译。会试试这个。
    • 不。没有骰子:translate-by-negative-center-of-source,然后缩放,然后translate-by-positive-center-of-dest。
    【解决方案3】:

    我没有玩过 OpenGL ES,只是稍微玩过 OpenGL。

    听起来您想从与原点相反的不同位置进行转换,不确定,但您可以尝试在glPushMatrix() and glPopMatrix() 内进行转换并绘制该位吗?

    e.g.
    
    // source and dest are arbitrary rectangles.
    float scaleX = dest.width / source.width;
    float scaleY = dest.height / source.height;
    float translateX = dest.x - source.x;
    float translateY = dest.y - source.y;
    
    glPushMatrix();
      glScalef(scaleX, scaleY, 0.0);
      glTranslatef(translateX, translateY, 0.0);
      // Draw geometry in question with its normal verts.
      //as if it were drawn from 0,0
    glPopMatrix();
    

    这是我写的一个简单的处理草图来说明这一点:

    import processing.opengl.*;
    import javax.media.opengl.*;
    
    
    void setup() {
      size(500, 400, OPENGL);
    }
    
    void draw() {
      background(255);
      PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;
      GL gl = pgl.beginGL();  
    
    
      gl.glPushMatrix();
        //transform the 'pivot'
        gl.glTranslatef(100,100,0);
        gl.glScalef(10,10,10);
        //draw something from the 'pivot'
        gl.glColor3f(0, 0.77, 0);
        drawTriangle(gl);
      gl.glPopMatrix();
      //matrix poped, we're back to orginin(0,0,0), continue as normal
      gl.glColor3f(0.77, 0, 0);
      drawTriangle(gl);
      pgl.endGL();
    }
    
    void drawTriangle(GL gl){
      gl.glBegin(GL.GL_TRIANGLES);
      gl.glVertex2i(10, 0);
      gl.glVertex2i(0, 20);
      gl.glVertex2i(20, 20);
      gl.glEnd();
    }
    

    这是草图运行的图像,绘制了相同的绿色三角形,应用了平移和缩放,然后是红色的,超出了 push/pop 'block',因此不受变换的影响:

    HTH, 乔治

    【讨论】:

    • 谢谢乔治,但推送/弹出实际上不是问题;我已经在我制作 sn-p 的代码之外进行了推送/弹出。谢谢你用图片说明,虽然!一个很好的可视化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-11
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    相关资源
    最近更新 更多