1、变换种类
OpenGL提供了计算机图形学中最基本的三维变换,包括:视点变换(gluLookAt)、模型变换、投影变换、剪取变换(附加裁剪面)和视口变换等,同时提供了特殊的变换和用法,如矩阵堆栈。
视点变换(gluLookAt):通过改变相机的方向和位置,可以改变出现在取景器中的景物,而景物在世界坐标系中并不发生变化。
模型变换:简单的改变被摄物体在取景器中的位置。
投影变换:使用变焦镜头gluPerspective还可以在不改变相机与被摄物体之间的空间距离的情况下,改变被摄物体在取景器中投影的大小。
视口变化(glViewport):冲洗相片底片时,还可以通过调整成像的位置,仅仅使影像的一部分显现出来。
最终,三维的现实世界变换成了二维的图像。
由于在某个方向上移动摄像机与向相反的方向移动物体对象所产生的效果是一样的,所以视点变换(gluLookAt)和模型变换统称为几何变换。
2、坐标系与坐标变换
Mode | 含义 |
---|---|
GL_MODELVIEW | 指定后续矩阵操作的对象是几何矩阵(世界坐标系)堆栈 |
GL_PROJECTIONVIEW | 指定后续矩阵操作的对象是投影矩阵堆栈 |
GL_TEXTURE | 指定后续矩阵操作的对象是纹理矩阵堆栈 |
GL_COLOR | 指定后续矩阵操作的对象是颜色矩阵堆栈 |
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixf(N);
glMultMatrixf(M);
glMultMatrixf(L);
glBegin(GL_POINTS);
glVertex3f(v);
glEnd();
这段代码中,
矩阵模型矩阵中相继含有I,N,N×M,N×M×L矩阵,变换后的顶点为N×M×L×v。因此顶点实际变换为N×(M×(L×v)),即
实际变换顺序与指定顺序相反。
3、平移变换绘制基本图形
BOOL COpenglbaseView::RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕及深度缓存
glLoadIdentity();
当您调用glLoadIdentity()之后,您实际上将当前局部坐标系原点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对当前所在的局部坐标系原点位置。
glTranslatef(-1.5f,0.0f,-6.0f); // 物体局部坐标系左移 1.5 单位,并移入屏幕 6.0
现在我们已经移到了屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。glBegin(GL_TRIANGLES)的意思是开始绘制三角形,glEnd() 告诉OpenGL三角形已经创建好了。
glBegin(GL_TRIANGLES); // 绘制三角形
glVertex3f( 0.0f, 1.0f, 0.0f); // 上顶点
glVertex3f(-1.0f,-1.0f, 0.0f); // 左下
glVertex3f( 1.0f,-1.0f, 0.0f); // 右下
glEnd();
在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。为此要再次使用glTranslate。这次右移,所以X坐标值为正值。因为前面左移了1.5个单位,这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。总共要向右移3.0个单位。
glTranslatef(3.0f,0.0f,0.0f); // 右移3单位
现在使用GL_QUADS绘制正方形。与绘制三角形的代码相类似,画四边形也很简单。唯一的区别是用GL_QUADS来替换了GL_TRIANGLES。并增加了一个点。我们使用顺时针次序来画正方形-左上-右上-右下-左下。采用顺时针绘制的是对象的后表面。这就是说我们所看见的是正方形的背面。逆时针画出来的正方形才是正面朝着我们的。现在这对您来说并不重要,但以后您必须知道。
glBegin(GL_QUADS); // 绘制正方形
glVertex3f(-1.0f, 1.0f, 0.0f); // 左上
glVertex3f( 1.0f, 1.0f, 0.0f); // 右上
glVertex3f( 1.0f,-1.0f, 0.0f); // 左下
glVertex3f(-1.0f,-1.0f, 0.0f); // 右下
glEnd();
完整的渲染绘制代码
BOOL COpenglbaseView::RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕及深度缓存
glLoadIdentity(); // 重置当前的模型观察矩阵
glTranslatef(-1.5f,0.0f,-6.0f); // 左移 1.5 单位,并移入屏幕 6.0
glBegin(GL_TRIANGLES); // 绘制三角形
glVertex3f( 0.0f, 1.0f, 0.0f); // 上顶点
glVertex3f(-1.0f,-1.0f, 0.0f); // 左下
glVertex3f( 1.0f,-1.0f, 0.0f); // 右下
glEnd();
glTranslatef(3.0f,0.0f,0.0f); // 右移3单位
glBegin(GL_QUADS); // 绘制正方形
glVertex3f(-1.0f, 1.0f, 0.0f); // 左上
glVertex3f( 1.0f, 1.0f, 0.0f); // 右上
glVertex3f( 1.0f,-1.0f, 0.0f); // 左下
glVertex3f(-1.0f,-1.0f, 0.0f); // 右下
glEnd();
return TRUE;
}
------------------------------------------------------------------------------
为多边形添加颜色
这节不是必须,为了有更漂亮的效果,给上面的三角形和四边形添加颜色。
使用Flat coloring(单调着色)给四边形涂上固定的一种颜色。使用Smooth coloring(平滑着色)将三角形的三个顶点的不同颜色混合在一起,创建漂亮的色彩混合。
下一行代码是我们第一次使用命令glColor3f(r,g,b)。括号中的三个参数依次是红、绿、蓝三色分量。取值范围可以从0,0f到1.0f。类似于以前所讲的清除屏幕背景命令。
我们将颜色设为红色(纯红色,无绿色,无蓝色)。接下来的一行代码设置三角形的第一个顶点(三角形的上顶点),并使用当前颜色(红色)来绘制。从现在开始所有的绘制的对象的颜色都是红色,直到我们将红色改变成别的什么颜色。
glColor3f(1.0f,0.0f,0.0f); // 设置当前色为红色
glVertex3f( 0.0f, 1.0f, 0.0f); // 上顶点
设置第二个绿色顶点。三角形的左下顶点被设为绿色。
glColor3f(0.0f,1.0f,0.0f); // 设置当前色为绿色
glVertex3f(-1.0f,-1.0f, 0.0f); // 左下
开始绘制之前将颜色设为蓝色。这将是三角形的右下顶点。glEnd()出现后,三角形将被填充。但是因为每个顶点有不同的颜色,因此看起来颜色从每个角喷出,并刚好在三角形的中心汇合,三种颜色相互混合。这就是平滑着色。
glColor3f(0.0f,0.0f,1.0f); // 设置当前色为蓝色
glVertex3f( 1.0f,-1.0f, 0.0f); // 右下
glEnd();
现在我们绘制一个单调着色-蓝色的正方形。最重要的是要记住,设置当前色之后绘制的所有东东都是当前色的。即便是在完全采用纹理贴图的时候,glColor3f仍旧可以用来调节纹理的色调。等等....,以后再说吧。
我们必须要做的事只需将颜色一次性的设为我们想采用的颜色(本例采用蓝色),然后绘制场景。每个顶点都是蓝色的,因为我们没有告诉OpenGL要改变顶点的颜色。最后的结果是.....全蓝色的正方形。再说一遍,顺时针绘制的正方形意味着我们所看见的是四边形的背面。
glColor3f(0.5f,0.5f,1.0f); // 一次性将当前色设置为蓝色
glBegin(GL_QUADS); // 绘制正方形
glVertex3f(-1.0f, 1.0f, 0.0f); // 左上
glVertex3f( 1.0f, 1.0f, 0.0f); // 右上
glVertex3f( 1.0f,-1.0f, 0.0f); // 左下
glVertex3f(-1.0f,-1.0f, 0.0f); // 右下
glEnd();
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4、旋转
学习目的:学会如何旋转三角形和四边形。左图中的三角形沿Y轴旋转,四边形沿着X轴旋转。
我们增加两个变量来控制这两个对象的旋转。这两个变量加在程序的开始处其他变量的后面( bool fullscreen=TRUE;下面的两行)。它们是浮点类型的变量,使得我们能够非常精确地旋转对象。浮点数包含小数位置,这意味着我们无需使用1、2、3...的角度。你会发现浮点数是OpenGL编程的基础。新变量中叫做m_fRotTriangle的用来旋转三角形,m_fRotQuadrangle旋转四边形。
public:
GLfloat m_fRotTriangle;
GLfloat m_fRotQuadrangle;
Archie注:进行初始化,虽然这一步不是必须的,但对于养成良好的变量定义并初始化的编程习惯还是很有帮助的。
COpenglbaseView::COpenglbaseView()
{
// TODO: add construction code here
m_hRC=NULL;
m_pDC=NULL;
m_fRotQuadrangle=20.0f;
m_fRotTriangle=7.0f;
下一行代码是新的。glRotatef(Angle,Xvector,Yvector,Zvector)负责让对象绕某个轴旋转。这个命令有很多用处。 Angle 通常是个变量代表对象转过的角度。 Xvector , Yvector 和 Zvector 三个参数则共同决定旋转轴的方向。比如(1,0,0)所描述的矢量经过X坐标轴的1个单位处并且方向向右。(-1,0,0)所描述的矢量经过X坐标轴的1个单位处,但方向向左。Archie注:根据右手定则确定转的方向。
D. Michael Traub:提供了对 Xvector , Yvector 和 Zvector 的上述解释。
下面的一行代码中,如果m_fRotTriangle等于7,我们将三角形绕着Y轴从左向右旋转7 。您也可以改变参数的值,让三角形绕着X和Y轴同时旋转。
glTranslatef(-1.5f,0.0f,-6.0f); // 左移 1.5 单位,并移入屏幕 6.0
glRotatef(m_fRotTriangle,0.0f,1.0f,0.0f); // 绕Y轴旋转三角形
glBegin(GL_TRIANGLES); // 绘制三角形
glColor3f(1.0f,0.0f,0.0f); // 设置当前色为红色
glVertex3f( 0.0f, 1.0f, 0.0f); // 上顶点
glColor3f(0.0f,1.0f,0.0f); // 设置当前色为绿色
glVertex3f(-1.0f,-1.0f, 0.0f); // 左下
glColor3f(0.0f,0.0f,1.0f); // 设置当前色为蓝色
glVertex3f( 1.0f,-1.0f, 0.0f); // 右下
glEnd();
您会注意下面的代码中我们增加了另一个glLoadIdentity()调用。目的是为了重置模型观察矩阵。如果我们没有重置,直接调用glTranslate的话,会出现意料之外的结果。因为坐标轴已经旋转了,很可能没有朝着您所希望的方向。所以我们本来想要左右移动对象的,就可能变成上下移动了,取决于您将坐标轴旋转了多少角度。试试将glLoadIdentity() 注释掉之后,会出现什么结果。
重置模型观察矩阵之后,X,Y,Z轴都以复位,我们调用glTranslate。您会注意到这次我们只向右移了1.5单位,而不是上节课的3.0单位。因为我们重置场景的时候,焦点又回到了场景的中心(0.0处)。这样就只需向右移1.5单位就够了。
当我们移到新位置后,绕X轴旋转四边形。正方形将上下转动。
glLoadIdentity();
glTranslatef(1.5f,0.0f,-6.0f);//右移1.5单位
glRotatef(m_fRotQuadrangle,1.0f,0.0f,0.0f); // 绕X轴旋转四边形
glColor3f(0.5f,0.5f,1.0f); // 一次性将当前色设置为蓝色
glBegin(GL_QUADS); // 绘制正方形
glVertex3f(-1.0f, 1.0f, 0.0f); // 左上
glVertex3f( 1.0f, 1.0f, 0.0f); // 右上
glVertex3f( 1.0f,-1.0f, 0.0f); // 左下
glVertex3f(-1.0f,-1.0f, 0.0f); // 右下
glEnd();
根据段落2实际变换顺序与指定顺序相反可知,变换矩阵是按先平移后旋转,实际处理时则是先旋转后平移。
下两行是新增的。倘若把m_fRotTriangle和 m_fRotQuadrangle 想象为容器,那么在程序的开始我们创建了容器( GLfloat m_fRotTriangle, 和 GLfloat m_fRotQuadrangle)。当容器创建之后,里面是空的。下面的第一行代码是向容器中添加0.2。因此每次当我们运行完前面的代码后,都会在这里使 m_fRotTriangle容器中的值增长0.2。后面一行将 m_fRotQuadrangle容器中的值减少0.15。同样每次当我们运行完前面的代码后,都会在这里使m_fRotQuadrangle容器中的值下跌0.15。下跌最终会导致对象旋转的方向和增长的方向相反。
尝试改变下面代码中的+和-,来体会对象旋转的方向是如何改变的。并试着将0.2改成1.0。这个数字越大,物体就转的越快,这个数字越小,物体转的就越慢。
m_fRotTriangle+=0.2f; // 增加三角形的旋转变量
m_fRotQuadrangle-=0.15f; // 减少四边形的旋转变量
return TRUE; // 继续运行
这里并没有看到不断变化的图像,原因就在于我们没有刷新图像。为了看到连续变化的情况,我们添加一个计时器。
添加计时器,定时刷新
在OnCreate函数中设置定时器1刷新频率20ms。
m_pDC=new CClientDC(this);
SetTimer(1,20,NULL);
if(!InitializeOpenGL(m_pDC))
return 0;
OnDestroy中销毁定时器KillTimer(1)
void COpenglbaseView::OnDestroy()
{
CView::OnDestroy();
//
//删除调色板和渲染上下文、定时器
wglMakeCurrent(NULL,NULL);
if(m_hRC)
wglDeleteContext(m_hRC);
m_hRC=0;
if (m_pDC)
{
delete m_pDC;
}
KillTimer(1);
}
添加OnTimer消息响应函数
void COpenglbaseView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
//
//添加定时器响应函数和场景和更新函数
Invalidate(FALSE);
//
CView::OnTimer(nIDEvent);
}
大功告成,终于可以看到连续旋转的多边形了。