opengl中的坐标变换

来源:互联网 发布:面向对象编程思维 编辑:程序博客网 时间:2024/04/30 15:28

原文地址: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
有一篇比较理论性的说明。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 文竹长得太高怎么办 桑叶牡l丹长虫怎么办 日本海棠开完花后枯萎了怎么办 长寿冠海棠烂根怎么办 夏季长寿冠海棠掉叶怎么办 竹节海棠有点烂根怎么办? 丽格海棠烂茎怎么办 长寿花徒长不开花怎么办 长寿花植株长了怎么办 丽格海棠不爱长怎么办 竹节海棠太高了怎么办 绿萝叶子发黄烂根怎么办 发财树叶子发黄烂根怎么办 君子兰烂根叶子发黄怎么办 四季海棠花叶子干了怎么办 海棠花叶子枯萎枝干发黑怎么办 长寿花叶尖焦了怎么办 新买的盆栽蔫了怎么办 熬中药水太多了怎么办 秋根海棠烂根怎么办 大叶海棠烂根怎么办 玻璃海棠长得很高怎么办 玫瑰海棠叶子干焦怎么办 海棠树树干生虫怎么办 天竺葵花骨朵有虫子了怎么办? 被刺梅的刺扎了怎么办 被刺梅的刺划手了怎么办 虎刺花叶子黄了怎么办 地栽月季叶子发黄怎么办 梅花浇水浇多了怎么办 深水井底下花管进水量小怎么办 盆景对节白腊树叶尖干枯怎么办 四季海棠茎软了怎么办 玫瑰海棠烂根了怎么办 格丽海棠花腌了怎么办 养殖场不能雨污分流怎么办 药店买的药贵了怎么办 神经损伤小便少尿不出来怎么办 手指夹伤出血了怎么办 手指夹破流血了怎么办 喂了宝宝熊胆粉怎么办