opengl中的坐标变换

原文地址:http://thatax.blog.163.com/blog/static/208926802008711115558846/



opengl 中使用的是右手坐标系。

以下部分来自 http://www.v-galaxy.com/neo6/tut/tut5.html

OpenGL入门(五):坐标变换
========================
neo6

坐标变换是OpenGL最基本功能之一,然而却不容易讲清楚,网上已有的教程好象都没有做到这一点。

其实弄懂坐标变换的关键是理解什么是“眼睛坐标系”(eye coordinate system) 和“目标坐标系”(object coordinate system),以及各个API函数使用的是哪套坐标:是“眼睛坐标”还是“目标坐标”。

所谓“眼睛坐标系”就是“全局坐标系”,可以这样设想:以窗口中心为原点,水平向右为+x轴,竖直向上为+y轴,垂直于屏幕指向我们为+z轴。长度单位这样来定:
窗口范围按此单位恰好是(-1,-1)-(1,1)。

所谓“目标坐标系”就是“局部坐标系”。在下面我们简称眼睛坐标系为ECS,目标
坐标系为OCS。点的“眼睛坐标”为EC, “目标坐标”为OC。

任何时候OpenGL都保存着一个当前的OCS,在初始条件下,OCS与ECS是重合的,但
我们可以对“目标坐标系”作各种变换: 平移、伸缩、旋转。经过这些变换之后, OCS与ECS不再重合。
非常重要的一点:glVertex3f所用的坐标是OC而不是EC。

为什么要这样设计API呢?因为我们一般习惯于在一个合适的局部坐标系中作图, 然后把它通过坐标变换放到合适的位置或者转到合适的角度。

比如我们要在(xc,yc)-(xc+dx,yc+dy)这样一个框子里画一条正弦曲线,我们更喜欢先在(0,0)-(1,1)范围内画这条曲线, 然后把它先伸缩到(0,0)-(dx,dy)的范围, 再平移(xc,yc)。这个过程如果用ECS和OCS的观点来看,平移和伸缩的顺序正好反过来:
(1)开始时,调用glLoadIdentity(),OCS0与ECS重合
(2)调用glTranslatef(xc,yc,0),OCS0平移,变成OCS1
(3)调用glScalef(dx,dy,0),OCS1伸缩,变成OCS2
(4)调用glBegin(),glVertex3f(),glEnd(),在OCS2中作图

现在我们考虑同一个点在OCS0,OCS1,OCS2中的坐标:(x0,y0,z0),(x1,y1,z1)和(x2,y2,z2)。

x0=x1+xc
y0=y1+yc
z0=z1

用矩阵和向量的形式,可以写成v0=T1*v1

x1=x2*dx
y1=y2*dy
z1=z2

用矩阵和向量的形式,可以写成v1=T2*v2 因此,v0=T1*T2*v2=T*v2

OpenGL只保存关于当前OCS到ECS变换所需的矩阵,记它为T。那么上述过程中T的变
化是:
(1)glLoadIdentity(),T=I(单位矩阵)
(2)glTranslatef(xc,yc,0),T=I*T1=T1
(3)glScalef(dx,dy,0),T=T*T2
(4)glVertex3f() 利用T把各点的OC变换成EC再作图

当OpenGL计算出点的EC之后,它要用投影变换把三维坐标变成两维坐标。有两种投影
方式:平行投影,用glOrtho()来设置;透视投影,用glFrustum()或者gluPerspective()来设置。

下面的这个例子利用坐标变换在不同的位置作四个正弦曲线。

//tut5.cpp

#include <math.h>
#include <gl/glut.h>

#define PI 3.1415926
#define N 100
float x[N];
float y[N];

void prepare_curve()
//在x[N]和y[N]里填入正弦曲线的x坐标和y坐标
{
     for(int i=0;i<N;i++)
     {
         x[i]=(float)i/N;
         y[i]=(sin(x[i]*2*PI)+1)/2;
     }
}

void plot(int n, float x[], float y[], float color[])
//在z=0的平面上先作一黑色方框(0,0)-(1,1),
//然后作x[]和y[]所表示的曲线
//曲线的范围最好不要超出(0,0)-(1,1),不然会很难看
{
     glBegin(GL_LINE_LOOP); //draw a box (0,0)-(1,1)
     glColor3f(0,0,0);
     glVertex2f(0,0);
     glVertex2f(0,1);
     glVertex2f(1,1);
     glVertex2f(1,0);
     glEnd();

     glBegin(GL_LINE_STRIP);
     glColor3fv(color);
     for(int i=0;i<n;i++)
     {
         glVertex2f(x[i],y[i]);
     }
     glEnd();
}

void display()
{
     float red[3]={1,0,0};
     float blue[3]={0,1,0};
     float green[3]={0,0,1};
     float yellow[3]={1,1,0};

     glClearColor(1,1,1,1);
     //设置刷新背景色
     glClear(GL_COLOR_BUFFER_BIT);
     //刷新背景
     glMatrixMode (GL_PROJECTION); //设置矩阵模式为投影矩阵
     glLoadIdentity();     //初始化投影矩阵
     glOrtho(-1,1,-1,1,-1,1);   //设置平行投影的投影矩阵

     glMatrixMode(GL_MODELVIEW); //设置矩阵模式为modelview矩阵
     glLoadIdentity();
     plot(N,x,y,red);

     glTranslatef(-1,0,0);
     plot(N,x,y,green);

     glTranslatef(0,-1,0);
     plot(N,x,y,blue);

     glTranslatef(1,0,0);
     glScalef(0.5,0.5,1);
     plot(N,x,y,yellow);

     glFlush();
     //更新窗口
}

int main(int argc, char** argv)
{
     prepare_curve();
         //生成一条正弦曲线
     glutInit(&argc, argv);
     glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA);
         //设置显示模式:单缓冲区, RGBA颜色模式
     glutInitWindowSize (200, 200);
       //设置窗口宽度、高度
     glutInitWindowPosition(100,100);
         //设置窗口位置
     glutCreateWindow (argv[0]);
       //弹出窗口
     glutDisplayFunc (display);
       //设置窗口刷新的回调函数
     glutMainLoop();
       //开始主循环
     return 0;
}

注意我们对上一个例子中的plot()加了一个颜色参数,用来控制曲线的颜色。
glColor3fv(color)的参数是一个数组float color[3];
里面是RGB颜色值。v的意思是vector。

glMatrixMode(GL_MODELVIEW)把当前矩阵模式设置为ModelView Matrix,因为
OpenGL中还有用于其他用途的Perspective Matrix和Texture Matrix。

glMatrixMode(GL_PERSPECTIVE)设置当前矩阵模式为Perspective Matrix。随后先调用
glLoadIdentity()初始化投影矩阵(perspective matrix),然后调用glOrtho(left,right,bottom,top,znear,zfar)
设置投影矩阵。由此生成的投影矩阵的作用相当于在ECS中取以(left,bottom,-znear)~(right,top,-zfar)
为对角线的立方体为clipping volume,眼睛从z = -znear的位置向z = -zfar 的位置看,像成在 z = -znear 的
平面上。注意眼睛看的方向很重要,因为这决定什么东西会被遮住。在这个程序里
我们用:
glOrtho(-1,1,-1,1,-1,1)
znear = -1, zfar = 1
所以我们从 -znear = 1 向 -zfar = -1 的方向看,即向 -z 方向看。所以在这里我们可以
把ECS 的 z 轴想象成是指向我们的。

通常情况下我们在调用glOrtho时总是取znear 小于 zfar,这时候我们总是朝 -z 方向看的。

另外 http://www.chinaai.org/Article_Show.asp?ArticleID=294
有一篇比较理论性的说明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值