(每次调用 glTranslate 在模型视图矩阵上都是累积的)这是啥意思以及如何禁用此功能?

Posted

技术标签:

【中文标题】(每次调用 glTranslate 在模型视图矩阵上都是累积的)这是啥意思以及如何禁用此功能?【英文标题】:(each call to glTranslate is cumulative on the modelview matrix) what does it mean and how to disable this feature?(每次调用 glTranslate 在模型视图矩阵上都是累积的)这是什么意思以及如何禁用此功能? 【发布时间】:2013-08-27 16:05:20 【问题描述】:

学习OpenGL SuperBible fram Addison-Wesley这本书,我读到:每次调用 glTranslate 在模型视图矩阵上都是累积的 这是什么意思? 是否意味着例如这段代码:

glTranslatef(2.0,3.0,0);
glTranslatef(4.0,5.0,0);  

首先将原点上的对象移动到点(2,3,0),然后将其从(2,3,0) 转换为(2+4,3+5,0+0) = (6,8,0),而不是再次从原点?

glScalefglRotatef 也是这样吗? 例如这段代码:

glScalef(2.0,3.0,4.0);
glScalef(3.0,4.0,5.0);  

首先将1x1x1 长方体转为2x3x4 立方矩形,然后将此立方矩形转为6x12x20 一个? 最后,这段代码是否意味着绕 x 轴旋转 75 度?

glRotatef(30.0,1,0,0);
glRotatef(45.0,1,0,0);  

最重要的一点:在每次调用这些函数之前调用 glLoadIdentity() 会取消这些功能吗? 我的意思是你认为这段代码能保证每次translates will be done from the origin?scale changes will be done from the initial state?

void COpenGLControl::ZoomToFullExtent()

float zoom1 = (float)oglWindowWidth/(float)ImageWidth;
float zoom2 = (float)oglWindowHeight/(float)ImageHeight;
m_fZoom = min(zoom1,zoom2);
m_fZoomInverse = 1/m_fZoom;
m_fPosX = 0;
m_fPosY = 0;
OnDraw(NULL);
  

void COpenGLControl::FixedZoomIn()

m_fZoom = 2*m_fZoom;
m_fZoomInverse = 1/m_fZoom;
OnDraw(NULL);


void COpenGLControl::FixedZoomOut()

m_fZoom = 0.5*m_fZoom;
m_fZoomInverse = 1/m_fZoom;
OnDraw(NULL);


void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point)

// TODO: Add your message handler code here and/or call default
if (WantToPan)

    if (m_fLastX < 0.0f && m_fLastY < 0.0f)
    
        m_fLastX = (float)point.x;
        m_fLastY = (float)point.y;
    
    diffX = (int)(point.x - m_fLastX);
    diffY = (int)(point.y - m_fLastY);
    m_fLastX = (float)point.x;
    m_fLastY = (float)point.y;
    if (nFlags & MK_MBUTTON)
    
        m_fPosX += (float)0.2f*m_fZoomInverse*diffX;
        m_fPosY += (float)0.2f*m_fZoomInverse*diffY;
    
    OnDraw(NULL);

CWnd::OnMouseMove(nFlags, point);
  

void COpenGLControl::OnDraw(CDC *pDC)

// TODO: Camera controls
wglMakeCurrent(hdc,hrc);
glLoadIdentity();
gluLookAt(0,0,1,0,0,0,0,1,0);
glScalef(m_fZoom,m_fZoom,1.0);
glTranslatef(m_fPosX, m_fPosY, 0.0f);
wglMakeCurrent(NULL, NULL);

【问题讨论】:

【参考方案1】:

glTranslate、glScale、glRotate 不作用于“对象”(无论对象是什么。OpenGL 不知道“对象”是什么,它只知道点、线和三角形)。

在旧的固定函数 OpenGL 中,您有几个矩阵堆栈。堆栈是一种类似于列表的数据结构,有两个操作 push 和 pop。实际上,您可以从列表中派生它:

stack : list;

void stack::push() 
    this->append( copy(this->last_element) );


void stack::pop() 
    this->drop( this->last_element );

投影和模型视图是最常用的。总是有一个特定的矩阵堆栈可用于操作。 glMatrixMode 选择哪一个;将其视为参考。

stack<mat4x4> modelview;
stack<mat4x4> projection;
stack<mat4x4> *M;

void glMatrixMode(mode) 
    switch(mode) 
    case GL_MODELVIEW:
        M = &modelview; break;

    case GL_PROJECTION:
        M = &projection; break;
    


void glPushMatrix() 
    M->push();


void glPopMatrix() 
    M->pop();

OpenGL 固定函数矩阵操作函数作用于有源矩阵堆栈 (M) 的顶部元素。

void glLoadIdentity() 
    M->last_element = identity_matrix;


void glTranslate(x,y,z) 
    /* make a translation matrix and R-multiply in place */
    mat4x4 T = translate_matrix(x,y,z);
    M->last_element = M->last_element * T;


void glScale(x,y,z) 
    /* make a scaling matrix and R-multiply in place */
    mat4x4 S = scaling_matrix(x,y,z);
    M->last_element = M->last_element * S;


void glRotate(a,x,y,z) 
    /* make a rotation matrix and R-multiply in place */
    mat4x4 R = rotation_matrix(a,x,y,z);
    M->last_element = M->last_element * R;

这就是调用这些函数时幕后发生的所有事情。

【讨论】:

【参考方案2】:

OpenGl 保留一个模型视图矩阵,该矩阵将您的顶点坐标相乘。每次调用平移、旋转、缩放等都会将该矩阵乘以右边。所以如果你有:

glLoadIdentity();
glTranslatef(2.0,3.0,0);
glTranslatef(4.0,5.0,0);  

结果将首先将您的顶点平移 4,5,0,然后再平移 2,3,0。在内部,这将按如下方式工作: 1.模型视图矩阵将是身份。 2. 当前的模型视图矩阵(恒等式)将与具有值(4、5、0)的平移矩阵相乘以获取更多详细信息,请参阅(http://en.wikipedia.org/wiki/Translation_%28geometry%29) 3.当前的modelViewmatrix(步骤2之一)将右乘第二个平移矩阵。

在您的缩放示例中:

glScalef(2.0,3.0,4.0);
glScalef(3.0,4.0,5.0);  

相当于先把 1x1x1 的长方体变成 3x4x5 的长方体,然后再变成 6x12x20 的长方体。 旋转情况下,先旋转45度,再旋转30度。

关于使用 glLoadIdentity() 的问题,modelView 矩阵将是独立于矩阵先前值的标识。

您可能也有兴趣检查opengl的转换堆栈系统。

【讨论】:

【参考方案3】:

特别注意包含以下描述的 OpenGL API 函数:“does ... to the current ...”。

OpenGL 是一个美化的状态机,诸如绑定对象和矩阵(在旧版 OpenGL 中)之类的东西会保留它们的状态。当您调用 glTranslatef (...) 时,它会乘以当前矩阵(由矩阵模式和矩阵堆栈的 top 定义)。除非您发出glLoadMatrixf (...)glLoadIdentity (...) 或修改矩阵堆栈,否则glTranslatef (...) 将在您每次调用时简单地累加。 glLoadIdentity (...) 将用它的身份替换 当前 矩阵:

    1, 0, 0, 0
    0, 1, 0, 0
    0, 0, 1, 0
    0, 0, 0, 1

如果您每帧都设置变换矩阵,通常需要您执行此操作。否则,您的所有转换都将与之前的状态相关(尽管有时这是需要的)。

【讨论】:

以上是关于(每次调用 glTranslate 在模型视图矩阵上都是累积的)这是啥意思以及如何禁用此功能?的主要内容,如果未能解决你的问题,请参考以下文章

gluUnproject 返回 0,似乎与模型视图矩阵有关

OpenGL的视图变换模型变换投影变换视口变换

OpenGL 方向。 (从负 z 看的身份旋转)

使用 glRotate 和 glTranslate 进行碰撞检测

为啥是模型视图矩阵?

获取当前 ModelView 矩阵