[转载]详解OpenGL的坐标系、投影和几何变换

详解OpenGL的坐标系、投影和几何变换

转载http://blog.csdn.net/blues1021/article/details/51535398#

OPengl的渲染流程是先全部设置好数据和状态,GL_MODELVIEW是将当前要变换的空间向量和模型视图矩阵当前最顶矩阵(会乘以摄像机变换乘投影矩阵的矩阵得最终变换矩阵)关联存储好包括状态设置,提交渲染时候glflush才会提交渲染数据和命令。

glPushMatrix()和glPopMatrix()的配对使用目的是方便得到需要变换的最顶矩阵,同时消除上一次的变换对本次变换的影响。使本次变换是以世界坐标系(或父坐标系)的原点为参考点进行。

作者:charlee

按:我也是在迷茫中走过来的,初学OpenGL时,略微了解了一些有关变换的基本知识,但是却不知道具体的使用方法,因此经常需要在布置场景时反复调整各种参数。当我终于有一天明白了它们的用法时,就觉得应该把这些心得体会写下来,让那些和我一样曾经迷茫过的人能够迅速地找到出路。本文的读者对象为那些初学OpenGL,了解了一些坐标系、几何变换等基本知识,但是又不知道具体应该如何运用这些操作的人。如果你对OpenGL一无所知,建议你先去学学OpenGL的基本知识。

1 坐标系

OpenGL中使用的坐标系有两种,分别为世界坐标系和屏幕坐标系。世界坐标系即OpenGL内部处理时使用的三维坐标系,而屏幕坐标系即为在计算机屏幕上绘图时使用的坐标系。

通常,OpenGL所使用的世界坐标系为右手型,如下图所示。

从计算机屏幕的角度来看,x轴正方向为屏幕从左向右,y轴正方向为屏幕从下向上,z轴正方向为屏幕从里向外。而进行旋转操作时需要指定的角度θ的方向则由右手法则来决定,即右手握拳,大拇指直向某个坐标轴的正方向,那么其余四指指向的方向即为该坐标轴上的θ角的正方向(即θ角增加的方向),在上图中用圆弧形箭头标出。

2 投影

将世界坐标系中的物体映射到屏幕坐标系上的方法称为投影。投影的方式包括平行投影和透视投影两种。

平行投影的投影线相互平行,投影的结果与原物体的大小相等,因此广泛地应用于工程制图等方面。

透视投影的投影线相交于一点,因此投影的结果与原物体的实际大小并不一致,而是会近大远小。因此透视投影更接近于真实世界的投影方式。

2.1 平行投影

OpenGL中使用下面的函数来设置投影方式为平行投影。

glOrtho(xleft, xright, ybottom, ytop, znear, zfar);

各参数的含义如下图所示。

注意,只有位于立方体之内的物体才可见。

2.2 透视投影

OpenGL中使用下面的函数来设置投影方式为透视投影。

gluPerspective(fovy, aspect, znear, zfar);

各参数的含义如下图所示。

fovy为四棱台的顶角,aspect为投影面的纵横比,znear和zfar为四棱台的顶面和底面到视点的距离(注意不是z坐标)。

注意,只有位于四棱台之内的物体才可见。

3 几何变换

OpenGL中可以使用的几何变换有平移、旋转、缩放三种。

glTranslatef(x, y, z);

该函数可以实现平移变换,x、y、z为各坐标轴上的平移量。

glRotatef(θ, x, y, z);

该函数实现旋转变换。θ为旋转角度,x、y、z为旋转轴。旋转方向由右手法则决定(参见第一节“坐标系”)。

glScalef(x, y, z);

该函数实现缩放变换。x、y、z为各轴方向的扩大量。若为负值,则沿着坐标轴的反方向进行缩放。

实际上,几何变换并不是针对坐标系中的某个物体进行变换,而是对整个坐标系进行变换。进行绘图时,世界坐标系上的点将以如下的方式被投影到屏幕坐标系上:

其中(x y z 1)T为该点在世界坐标系中的坐标,(x‘ y‘ z‘ 1)T为该点在屏幕坐标系上的投影的坐标。P为投影变换矩阵,An为几何变换矩阵。在处理变换和投影时,OpenGL先把几何变换矩阵A1、A2、…、An从左侧依次与点坐标矩阵相乘,最后再将投影矩阵从左侧与经过几何变换之后的点坐标相乘,即得到该点的屏幕坐标。也就是说,在OpenGL中进行几何变换的方式为,首先通过glTranslatef、glRotatef、glScalef等函数设置好几何变换矩阵(相当于对坐标系进行了变换),然后再进行绘图,那么图形的投影坐标将受到设置好的几何变换矩阵所影响而显现出几何变换的效果;而并不是首先进行绘图然后再通过几何变换函数对已经存在的图形进行变换。

变换的一般形式如下式所示:

常见的变换矩阵如下。

(1)平移变换

(2)旋转变换

沿x轴旋转

沿y轴旋转

沿z轴旋转

(3)缩放变换

(4)平行投影(投影到xy平面的情况)

(5)透视投影(投影到xy平面的情况。投影中心为z轴上的点(0, 0, R))

在实际编程中,为了保存几何变换和投影变换的操作,OpenGL维护两个栈,即投影变换栈和几何变换栈。栈中保存的元素即为投影变换和几何变换的变换矩阵。使用下面的函数可以在两个栈之间进行切换:

切换当前操作的栈为投影变换栈:glMatrixMode(GL_PROJECTION);

切换当前操作的栈为几何变换栈:glMatrixMode(GL_MODELVIEW);

此外,下面的函数可以清除当前操作的栈中的内容,并向栈中压入一个单位矩阵:

glLoadIdentity();

两个栈之间的关系如下图所示:

一般情况下,我们在进行OpenGL初始化时都要执行下面的命令:

glMatrixMode(GL_PROJECTION);                            // 切换到投影变换栈

glLoadIdentity();                                       // 初始化投影变换栈

gluPrespective(30.0, aspect, 1.0, 50.0);                // 压入透视投影矩阵

glMatrixMode(GL_MODELVIEW);                             // 切换到几何变换栈

另外,在调用glMatrixMode(GL_MODELVIEW)时,系统会自动将几何变换栈清空并压入单位矩阵,因此不必再调用glLoadIdentity()函数。

对于几何变换栈,还有以下两个常用的操作:

glPushMatrix();                                         // 保存当前坐标系

glPopMatrix();                                          // 恢复当前坐标系

在调用几何变换操作时,OpenGL将该几何变换操作的变换矩阵与当前栈的栈顶元素相乘,得到一个新的矩阵并将其作为几何变换栈的栈顶。

作为例子,我们来看下面的这段程序。

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluPerspective(30.0, aspect, 1.0, 50.0);

glMatrixMode(GL_MODELVIEW);

glPushMatrix();

glTranslatef(0.0, 0.0, -20.0);

glPushMatrix();

glTranslatef(0.0, 1.0, 0.0);

myWireCylinder(1.0, 2.0, 12);

glTranslatef(0.0, 1.0, 0.0);

glRotatef(-90.0, 1.0, 0.0, 0.0);

glutWireCone(1.0, 2.0, 12, 3);

glPopMatrix();

glTranslatef(0.0, -1.0, 0.0);

myWireCylinder(1.0, 2.0, 12);

glPopMatrix();

下面我们来分析一下该程序执行过程中两个栈的变化情况。图中左侧的栈为透视变换栈,右侧的栈为几何变换栈。黄色表示当前操作栈。I表示单位矩阵,P表示投影变换矩阵,T为平移变换矩阵,R为旋转变换矩阵,S为缩放变换矩阵。


glMatrixMode(GL_PROJECTION);

glLoadIdentity();



gluPerspective(30.0, aspect, 1.0, 50.0);



glMatrixMode(GL_MODELVIEW);



glPushMatrix();



glTranslatef(0.0, 0.0, -20.0);



glPushMatrix();



glTranslatef(0.0, 1.0, 0.0);

myWireCylinder(1.0, 2.0, 12);



glTranslatef(0.0, 1.0, 0.0);



glRotatef(-90.0, 1.0, 0.0, 0.0);

glutWireCone(1.0, 2.0, 12, 3);



glPopMatrix();



glTranslatef(0.0, -1.0, 0.0);

myWireCylinder(1.0, 2.0, 12);



glPopMatrix();


列式存储:

具体到OpenGL的实现,OpenGL和数学中相同采用右手系,OpenGL把模型变换和视图变换合二为一,即模型视图矩阵。OpenGL和GLM的变换矩阵都是按照列优先存储在内存中,这和C++二维数组不同,其实,GLM中的4x4矩阵是由4个列向量组成的。按照上面的分析,当OpenGL的模型视图矩阵和投影矩阵均为单位阵时, 这时摄像机位于世界坐标系原点看向z负方向,向右方向沿x轴正方向,向上方向沿y正方向,由于投影矩阵为单位阵,这时为正交投影(另一种是透视投影),裁 剪面为xyz的±1,也就是说,对应到最后的显示窗口,x方向向右,y方向向上,z方向垂直屏幕向外,窗口中心对应坐标原点,窗口边缘对应±1,并且z值 小的片断遮挡z值大的片断(正好和离摄像机的远近关系反了,这是因为没有对z坐标进行变号)。对了,OpenGL除了模型视图矩阵和投影矩阵之外,还有纹理坐标变换矩阵和颜色变换矩阵。请见OpenGL官方手册文献[8]2.12和2.16。

实例代码:

 1 #include <GL/glut.h>
 2 #include <stdlib.h>
 3
 4 void init(void)
 5 {
 6    glClearColor (0.0, 0.0, 0.0, 0.0);
 7    glShadeModel (GL_FLAT);
 8 }
 9
10 void display(void)
11 {
12    glClear (GL_COLOR_BUFFER_BIT);
13    glColor3f (1.0, 1.0, 1.0);
14    //OGL中都是和当前矩阵相乘作为变换矩阵,所以这里要清理下;
15    //虽然模型视图矩阵栈中调用gluLookAt会初始化单位矩阵但是屏幕大小改变时候,还是会出现错乱所以视图变换前glLoadIdentity是好习惯。
16    //glLoadMatrix指定的矩阵,glMultMatrix是乘以当前矩阵作为结果矩阵;
17    //glLoadTransposeMatrix,glMultTransposeMatrix是行主序矩阵类似D3D,转置矩阵刚好把列主序矩阵变换为行主序矩阵。
18    glLoadIdentity ();             /* clear the matrix */
19            /* viewing transformation  */
20    gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
21    //glTranslatef( .0, .0f, -5.0f); // 变换坐标系等于相反的方向变换物体,故这里模型和视图变换放到一起
22    // glTranslate glRotate glScale生成恰当的矩阵,且调用了glMulteMatrix作为结果;
23    // 这些变换矩阵放置到display list中比每帧计算更快
24    glRotatef(45, 0, 0, 1); // 绕原点到指定点的直线(指定轴),逆时针旋转45度
25    glPushMatrix();
26    // 若当前物体点用局部坐标系统刻画那么在局部坐标系中缩放
27    // 使用缩放会降低光照计算效率,因为要重新计算物体表面的法向量; 一般不缩放为0而是使用投影矩阵而不是模型视图矩阵(例如平面阴影)
28    glScalef (1.0, 2.0, 1.0);      /* modeling transformation */
29    // OGL的矩阵乘法,默认是列主序矩阵,M[i][j]是i列j行,矩阵乘法是左乘当前矩阵或向量
30    glutWireTeapot(1);
31    glPopMatrix();
32    glTranslatef(-2.0, -2.0f, 0);
33    glScalef(-1.0, 2.0, 1.0);
34    glutWireTeapot(1);
35    glFlush ();
36 }
37
38 void reshape (int w, int h)
39 {
40    // 视口变换,将[-1,-1,-1]->[1,1,1]空间变换到OGL左下角[0,0]右上角[1,1]中
41    glViewport (0, 0, (GLsizei) w, (GLsizei) h);
42    // 透视投影变换到裁剪4D空间(裁剪掉视景体外的),变换到视口变换前硬件会进行透视除法/w,建筑师需要实际大小用gluOrtho2D
43    glMatrixMode (GL_PROJECTION);
44    glLoadIdentity ();
45    glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
46    //gluPerspective(90.0, w / h, 1, 1000.0);
47    // 切换到模型视图变换
48    glMatrixMode (GL_MODELVIEW);
49 }
50
51 void keyboard(unsigned char key, int x, int y)
52 {
53    switch (key) {
54       case 27:
55          exit(0);
56          break;
57    }
58 }
59
60 int main(int argc, char** argv)
61 {
62    glutInit(&argc, argv);
63    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
64    glutInitWindowSize (500, 500);
65    glutInitWindowPosition (100, 100);
66    glutCreateWindow (argv[0]);
67    init ();
68    glutDisplayFunc(display);
69    glutReshapeFunc(reshape);
70    glutKeyboardFunc(keyboard);
71    glutMainLoop();
72    return 0;
73 }  

4 参考文献

OpenGLによる3次元CGプログラミング,林武文、加藤清敬著,コロナ社

时间: 2024-08-08 19:18:13

[转载]详解OpenGL的坐标系、投影和几何变换的相关文章

NVIDIA Jetson TK1学习与开发(八):图文详解OpenGL在Jetson TK1上的安装和使用

图文详解OpenGL在Jetson TK1上的安装和使用 1.入门介绍与资源推介 OpenGL(全写Open Graphics Library)是个定义了一个跨编程语言.跨平台的编程接口规格的专业的图形程序接口.它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库. OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机.PDA和游戏主机等嵌入式设备而设计.该API由Khronos集团定义推广,Khron

详解OpenGL中的各种变换(投影变换,模型变换,视图变换)(二)——投影变换

下面介绍投影变换矩阵.这个相比较上一遍的就比较难了.主要分为透视投影矩阵和正交投影矩阵,本文主要介绍透视投影矩阵,正交比较简单,就给出矩阵形式. (1)透视投影变换矩阵 我们先来说一下透视投影需要哪些参数.如图1所示,深色部分表示最终显示的区域.照相机的z轴是穿过视锥体正中心的,显示区域离相机最近的平面我们称为Znear,最远的平面我们称为Zfar.其中Znear就是最终的投影窗口.所以我们要把Znear的宽映射到坐标[-1,1],把Znear的高映射到坐标[-1,1],把Znear到Zfar的

阿里云部署 Flask + WSGI + Nginx 转载详解

我采用的部署方案是: Web 服务器采用 uwsgi host Flask 用 Supervisor 引用 uwsgi 作常规启动服务 基于 Nginx 作反向代理 首先, 阿里云服务器可以通过 SSH 指令在本机的终端进行远程连接 ssh [email protected]云服务器地址 输入密码进入后所有的操作与本地终端完全一至. 安装 Python 环境 接下来是python , Ubuntu 的默认环境已经预装 python 2.7 所以只需要安装 python 的 pip 安装工具即可.

[转载]详解主流浏览器多进程架构:Chrome、IE

http://www.cnbeta.com/articles/109595.htm 随着Web浏览器重要性的日益突出,恶意软件.木马.间谍软件等网络攻击也呈现逐渐的上升.而面对 如此众多的潜在威胁,为了确保用户的安全性和稳定性,浏览器不得不改进浏览器的性能,其中之一就是向用户提供多进程浏览.在浏览器中添加多进程浏览功能之 后,即使是浏览器其中的一个进程出现了崩溃现象,其他的进程也不会受到影响.例如,如果一个网站中有漏洞或包含恶意代码,它就有可能摧毁当前运行在这个网 站上的标签,但是它却不会影响其

Cocos2d-x 3.0坐标系详解(转载)

Cocos2d-x 3.0坐标系详解Cocos2d-x坐标系和OpenGL坐标系相同,都是起源于笛卡尔坐标系.笛卡尔坐标系笛卡尔坐标系中定义右手系原点在左下角,x向右,y向上,z向外,OpenGL坐标系为笛卡尔右手系.屏幕坐标系和Cocos2d坐标系标准屏幕坐标系使用和OpenGL不同的坐标系,而Cocos2d则使用和OpenGL相同的坐标系.iOS, Android, Windows Phone等在开发应用时使用的是标准屏幕坐标系,原点为屏幕左上角,x向右,y向下.Cocos2d坐标系和Ope

【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解

原文:3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解 3D中z值会影响屏幕坐标系到世界坐标系之间的转换,2D中Z值不会产生影响(而只是屏幕宽高比会产生影响,z值只对深度剔除产生影响).所以U3D中如果用2D摄像机那么屏幕坐标和世界坐标之间的转换需要用指定的2D摄像机才行,如果用主3D摄像机那么UI转换会产生计算结果异常. 一.D3DXMatrixPerspectiveFovLH函数 作用:Builds a left-handed

OpenGL一些函数详解(二)

OpenGL ES顶点数据绘制技巧 在OpenGL中,绘制一个长方体,需要将每个顶点的坐标放在一个数组中.保存坐标时有一些技巧(由于字母下标不好表示,因此将下标表示为单引号,如A1将在后文中表示为A' ): (1)将对立面坐标保存在相邻的位置,如坐标的保存顺序为:前面(A'ABB'),后面(D'DCC'),上面(D'A'B'C'),下面(DABC),左面(D'A'AD),右面(C'B'BC).因为对立面的坐标除了其垂直的那根轴的坐标相反以外,其他坐标值一样:如前面和后面(垂直于z轴),x和y的坐

Spring AOP详解(转载)

此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题.最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP 来解决.一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容.本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况 监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员

转载iOS---&gt;NSRunLoop详解

转载--->NSRunLoop(详解) NSRunLoop大部分情况在多线程编程的时候才会用到..但是一般不会用NSRunLoop,因为它不是线程安全的.一般都建议用CFRunLoop,这个是线程安全的.input source and port-based custom source这些操作,是向线程里面添加操作的.添加的这些操作,会在该线程执行空间的调度下执行. 通俗的理解就是如果你创建的了一个子线程,子线程的运行函数如下- (void) subThread (void*)unused {