The Coder

I am a humble coder.

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  4 随笔 :: 4 文章 :: 9 评论 :: 0 Trackbacks

OpenGL 中场景进行变换,要经历一些过程:视图变换 à 模型变换 à 投影变换,然后到了窗口坐标。这几个变换开始的时候把我搞很混,这几天整理一下。

       一般书上把这几个变换用照相机类比,其实每个变换都是产生着一个 4x4 矩阵,然后与当前矩阵 (Current Matrix) 相乘,得到一个坐标变换矩阵,最后把世界坐标系(欧式空间)中的物体变换到屏幕坐标系中。这里梳理一下概念:

       1 、视图变换( VIEW Transformation ):它类似将照相机指向物体,即确定视点(观察点)的位置和观察方向。一般用的函数为 glu 封装的函数:

void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez,  --------- 观察点

       GLdouble centrex, GLdouble centrey, GLdouble centrez,  -- 视线方向:从 eye 指向 centre

       GLdouble upx, GLdouble upy, GLdouble upz ------------ 视图体自下而上的方向

 )

       这个函数会产生一个视图矩阵,并右乘到当前矩阵上。模型变换通常发生在模型变换之前。其实,视图变换也是通过平移和旋转得到的,观察位置与物体位置之间是个相对的状态,我们也把视图变换和模型变换统一成一个变换,产生一个矩阵:模型视图变换矩阵。

       2 、模型变换 (MODEL Transformation) :它确定模型的位置和方向,对模型进行旋转、平移和缩放。用到三个子函数: glTranslate*(x, y, z) glRotate*(x, y, z) glScale*(x, y, z) 。每个函数都会产生一个矩阵,并右乘当前矩阵。

       3 、投影变换( PROJECTION Transformation ):产生一个六面的视图体,把视图体以外的场景剪裁掉,把视图体内的物体、场景作为绘制对象,让“照相机拍摄”。两种投影方式,两个投影函数: glFrustum(left, right, bottom, top, near, far)

glOrtho(left, right, bottom, top, near, far )

这两个函数的参数非常对称,都是构筑了一个六面体,形成可视范围。它们都产生一个矩阵,并左乘当前矩阵。(当然,还有 glu 的两个函数)。

      

       要理解整个过程,关键在理解当前变换矩阵 CTM ,(简称为 C )。它是一个状态概念,应用到 OpenGL 流水线中的每一个定点: P = C*P’ 。这条等式是对同一个点在两个坐标系体统之间进行转换,从右边的坐标系下的坐标( P’ )转换到左边的坐标系下的坐标( P )。而矩阵 C 4x4 的齐次坐标矩阵,它都蕴含着一个局部坐标系信息:以右边坐标系为参考坐标系统,左边坐标系的位置和方向。

       用手工定义一个矩阵,如下(按 OpenGL 矩阵方式定义。与数学定义矩阵的方式转置):

       CTM[16] = { a0, a1, a2, a3,         // x 轴的方向向量

                        a4, a5, a6, a7,          // y 轴的方向向量

                        a8, a9, a10, a11,              // z 轴的方向向量

                        a12, a13, a14, a15    // 原点的位置

                            }

      

       再来考察当前变换矩阵 CTM ,它是在 OpenGL 流水线中一个模型视图矩阵和一个投影矩阵的复合。 CTM = P*C*M 。(注意到上面提到的左乘右乘了吗?)
 aa.bmp

我们来分析一个简单的例子: 

 

 

 1 #define  NUM 0.70710678118654746
 2 // 注意这个矩阵是正交的,没有正交就用,好像有放缩作用
GLfloat Tmat1[16] = { NUM, NUM, 0.0, 0.0,
                    -NUM, NUM, 0.0, 0.0,
                     0.0, 0.0, 1.0, 0.0,
                     0.0, 0.0, 0.0, 1.0};

GLfloat Tmat2[16] = { 1.0, 0.0, 0.0, 0.0,
                      0.0, 1.0, 0.0, 0.0,
                      0.0, 0.0, 1.0, 0.0,
                      1.0, 0.0, 0.0, 1.0,
                        };

 7 void  setupRC( void )
 8 {
 9     glClearColor( 0.0f 0.0f 0.0f 1.0f );
10     glShadeModel(GL_FLAT);
11 }

12
13 void  RenderScene( void )
14 {
15     printf( " RenderScene\n " );  
16 glClear(GL_COLOR_BUFFER_BIT);
17 glColor3f( 0.0f 1.0f 1.0f );
18     glMatrixMode(GL_MODELVIEW);
19     glLoadIdentity();
20     gluLookAt( 0.0 0.0 5.0 ,         //  view point
21                0.0 0.0 0.0 ,         //  focus point
22                0.0 1.0 0.0 );        //  up vector
23
24    glutSolidCube( 0.5 );                 //  原点的参考位置
25     glMultMatrixf(Tmatr1);                 // 这个矩阵的动作和下面的两个变换是一样的。
               glMultMatrixf(Tmatr2);
26 //     glRotatef(45.0, 0.0, 0.0, 1.0);
27 //     glTranslatef(3.0, 0.0, 0.0);
28     glutSolidCube( 1.0 );
29
30     glutSwapBuffers();
31 }

32
33 void  ChangeSize( int  w,  int  h)
34 {
35     printf( " ChangeSize\n " );    // 从这里看出,是先调用ChangeSize()的
36     GLfloat nRange  =   10.0f ;
37
38      if (h  ==   0 )
39         h  =   1 ;
40     GLfloat fRatio  =  (GLfloat)w  /  (GLfloat)h;
41
42     glMatrixMode(GL_PROJECTION);
43     glLoadIdentity();
44
45      if (w  <=  h)
46         glOrtho( - nRange, nRange,  - nRange  /  fRatio, nRange  /  fRatio,  1.0 , nRange);
47      else
48         glOrtho( - nRange  *  fRatio, nRange  *  fRatio,  - nRange, nRange,  1.0 , nRange);
49
50     glViewport( 0 0 , w ,h);
51
52     glMatrixMode(GL_MODELVIEW);
53     glLoadIdentity();
54 }

55
56 int  main( int  argc,  char *  argv[])
57 {
58     glutInit( & argc, argv);
59     glutInitDisplayMode(GLUT_RGB  |  GLUT_DOUBLE);
60     glutInitWindowSize( 800 600 );
61     glutCreateWindow( " example " );
62
63     glutReshapeFunc(ChangeSize);
64     glutDisplayFunc(RenderScene);
65
66     setupRC();
67
68     glutMainLoop();
69
70     std::cout  <<   " Hello world! "   <<  std::endl;
71      return   0 ;
72 }

73

glRotatef(45.0, 0.0, 0.0, 1.0)   

glTranslatef(3.0, 0.0, 0.0);  

这两个变换,可以看成:

       glMultMatrixf(R);

       glMultMatrixf(T);

R,T 都是右乘到 CTM CTM = CTM * R * T

对模型变换的理解有两种:

1、在全局固定坐标系下,对物体进行变换。这时候,我们要以相反的顺序来考虑代码中的变换函数了,它的实际过程是这样 P = CTM *( R*(T* p’))

首先、对物体进行平移,平移到坐标( 3.0, 0.0, 0.0 )。  然后,把物体相对原点绕z轴旋转45度。

2、物体捆绑在局部坐标系下,所有的变换都是坐标系进行的。这时,我们用顺序来看这个变换。

glRotatef(45.0, 0.0, 0.0, 1.0) 产生一个齐次矩阵 R( 这可是代表一个局部坐标系哦 )   ,即局部坐标系 R 相对刚才开始的坐标系 I (单位矩阵)作了旋转变换,绕旋转了45度。

glTranslatef(3.0, 0.0, 0.0) 产生一个齐次矩阵 T (也是代表了一个局部坐标系),相对 R 坐标系沿x轴( R 系)平移了3个单位,得到了自己的局部坐标系 T

最后在这个局部坐标系 T 下画了 Cube

代码中的 Tmat = R * T ,它也是从 T 坐标系变换到 R 坐标系,再变换到最后的模型视图的世界坐标系。

 

 

后注:

       对坐标系的几何变换是 既采用基于齐次坐标的矩阵表达形式 ! 又在欧氏几何的 Cartesian 坐标系下以对其进行说明性的定义 . 由于齐次坐标是射影几何的语言工具 ! 前者表明几何变换的表达是基于射影几何的后者则带有欧氏几何色彩 ,所以对它的表述清晰统一的表述比较难。本文写的也比较零散,有语焉不详、理解错误指出,请多多指正!

 

posted on 2006-08-11 22:27 TH 阅读(5362) 评论(8)  编辑 收藏 引用

评论

# re: OpenGL的视图变换 2006-08-15 12:09 可冰
可以完全用OpenGL来做GUI吗,比如文本输入框、对话框等等,比起MFC来哪个好用一些。我最近要做GUI程序,但又不想学MFC,不知道该用什么好。  回复  更多评论
  

# re: OpenGL的视图变换 2006-08-15 16:03 TH
@可冰
呵呵,OpenGL本身是没有对窗口进行处理能力的.需要借助GLUT库,处理一些简单的窗口应用.当然处理能力可能不是很强.
还有一个库GLUI也许可以满足你的要求:
/**
GLUI is a GLUT-based C++ user interface library which provides controls such as buttons, checkboxes, radio
buttons, spinners, and listboxes to OpenGL applications. It is window-system independent, relying on GLUT to
handle all system-dependent issues, such as window and mouse management.
**/
当然还有很多很优秀的GUI库.如smartwin++,wxWidgets等.每种都有其不同的设计特点.按需学习了~~~  回复  更多评论
  

# re: OpenGL的视图变换 2006-09-08 00:49 嘿嘿
被这个冬冬搞的晕死了   回复  更多评论
  

# re: OpenGL的视图变换 2006-10-02 20:01 hrbeu
我的头都晕死了 变换感觉没有什么难得的啊 可是操作起来 我已经迷糊了
还好 还有一起学OpenGl的朋友 让我觉得不再孤独   回复  更多评论
  

# re: OpenGL的视图变换[未登录] 2007-03-22 09:08 sophia
好东东
这两天看得头晕
上午正想准备,看到了,
收藏了!

模型变换是对坐标系的变换!  回复  更多评论
  

# re: OpenGL的视图变换 2007-03-31 20:06 hao a
hao a a a a   回复  更多评论
  

# re: OpenGL的视图变换 2007-10-21 23:48 roger
好像有点小错误,“它都蕴含着一个局部坐标系信息:以右边坐标系为参考坐标系统,左边坐标系的位置和方向。”
P=MP',设P是O系坐标,P'是O'系坐标, 那么,按照上述结论,以O‘系为参考,O坐标系的描述为M。那么带入P=MP',有:
I=MM

这是错误的。

其实:设S为O系下O‘系的坐标,那么有:
O系下O‘系的描述=M*O’系下O‘系的描述,即:
S=M*I
S=M
所以M是O系下O‘系的坐标,即左边坐标系下,右边坐标系的坐标。

比如,O系下O'系的原点在(3,0,0)’处,那么有:
M=
1003
0100
0010
0001

假设O‘系中有一个点(3,0,0,1)‘,那么,O系中,它在:
M*P‘=(6,0,0,0)  回复  更多评论
  

# re: OpenGL的视图变换 2009-02-06 04:17 xinxin
博主这文章的观众还是挺多的,而且博主挺谦虚的呵呵.
但是,我在此留言是希望读者在以此文为参考的同时也再在搜索引擎上搜索一下其他文章...
因为这里头的讲述的某些小地方实在是容易让人误解的。特别是左右乘的概念。
而我相信博主思路是正确的,这些都是表述上的问题而已。  回复  更多评论
  


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理