OpenGl从零开始之坐标变换(上)
坐标变换是深入理解三维世界的基础,非常重要。学习这部分首先要清楚几个概念:视点变换、模型变换、投影变换、视口变换。
在现实世界中,所有的物体都具有三维特征,但计算机本身只能处理数字,显示二维的图形,因此我们要将三维物体用二维数据表示出来,这一联系的点就是坐标。在OpenGL三维空间中坐标的形式有两种:世界坐标系和局部坐标系。
①世界坐标系:始终固定不变。举例,以太阳系中心太阳为中心原点,建立世界坐标系的话,地球绕太阳的公转运动是世界坐标的变换。
②局部坐标系:物体本身的中心。地球的自传就是局部坐标系下的旋转变换,缩放也是局部坐标系下的变换。
三维物体到二维图像的转换是通过视点变换、模型变换、投影变换、视口变换来实现的,这点类似于用照相机拍照的过程。
(1)视点变换-->选择观察点,对准物体
(2)模型变换-->调整物体(即模型),包括旋转、缩放、平移
(3)投影变换-->将物体进行投影放大或缩小,投影到二维画布或者叫底片上,形成二维图像
(4)视口变换-->最终决定生成的二维图像到底显示在屏幕的什么位置和显示窗口的大小,可以理解对图像进行缩放。
投影变换和矩阵栈的操作稍微复杂点,放在下一篇,由于视口变换是针对投影变换形成的二维图像做操作的,所以和投影变换一起放到下一篇,这里先介绍其他两种变换。
首先,先了解在OpenGL中是怎么变换的:
OpenGL中的各种转换是通过矩阵运算实现的,无论是移动、旋转还是缩放大小,都是通过在当前矩阵的基础上乘以一个新的矩阵来达到目的。假设当前矩阵为C(即目前栈顶的矩阵,如果之前没有做过变换,栈顶矩阵默认为单位矩阵),旋转变换命令构成的矩阵为R,则发出转换命令后,生成的新的当前矩阵为CR,这个矩阵再乘以顶点坐标v,从而构成新的顶点坐标(CR)v,这样就完成了旋转变换,且当前矩阵变为CR,。如果是连续做多种变换,例如上面的旋转变换之后继续做移动变换T,则生成新的当前矩阵为CRT,用这个总变换矩阵乘以顶点坐标v,得到最终的顶点坐标(CRT)v,由于矩阵乘法的结合率, (CRT)v = (C(R(Tv))),从而看出,程序中绘制顶点前的最后一个变换命令最先作用于顶点之上。这同时也说明,OpenGL编程中,实际的变换顺序与指定的顺序是相反的,即实际上是先进行移动,然后进行旋转。
实体绘制
在讲变换之前,首先需要知道OpenGL中绘制三维物体的具体函数,以便理解给予的程序实例。OpenGL中的实体绘制函数主要在glut库中(glut是实用工具库,gl是核心库,glu是对gl的部分封装)。以下列出部分常用的实体绘制函数,并给与代码实例:
//以下所有函数中,radius表示球体的半径,slices表示球体纵向分割的数目(经线),主要是设置绘制的细密度,stacks表示球体横向分割的数目(纬线),创建了绘制中心在模型坐标原点,半径为radius的球体
void glutWireSphere(GLdouble radius, GLint slices, GLint stacks); //网状球
void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks); //实心球
void glutWireCube(GLdouble size); //网状立方体
void glutSolidCube(GLdouble size); //实心立方体
void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings); //网状圆环
void glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings); //实心圆环
void glutWireCone(GLdouble radius, GLdouble height, GLint slices, GLint stacks); //网状圆锥体
void glutSolidCone(GLdouble radius, GLdouble height, GLint slices, GLint stacks); //实心圆锥体
void glutWireTeapot(GLdouble size); //网状茶壶
void glutSolidTeapot(GLdouble size); //实心茶壶
使用代码实例:
void display(void)
{
glColor3f(0.0f, 1.0f, 0.0f);//绿色
glutSolidSphere(0.5,,);//半径r=0.5,细密程度横纵200份 glFlush();
}
void display(void)
{
glColor3f(0.0f, 1.0f, 0.0f);//绿色
glutSolidSphere(0.5,,);//半径r=0.5,细密程度横纵10份,会发现,细密程度越高越精准,这是微积分的体现。但是高精度对性能的要求高,过高则渲染效率会降低,俗话会卡 glFlush();
}
示例图:


网状球体:
void display(void)
{
glColor3f(0.0f, 1.0f, 0.0f);
glutWireSphere(0.5,,);//网状球体,更有立体感 glFlush();
}
示例图:

接下来做变换,改变视图图像。
1、视点变换
视点,可以用摄影师的观察点来形用,三维物体则看作是被拍摄的人或物,那么视点的变换就是摄影师在寻找拍摄视角。
OpenGL实用库提供了gluLookAt()函数,该函数有三个变量,分别定义了视点的位置、相机瞄准方向的参考点以及相机的向上方向。
void gluLookAt(
GLdouble eyex,GLdouble eyey,GLdouble eyez,//(eyex,eyey,eyez)视点的位置;
GLdouble centerx,GLdouble centery,GLdouble centerz,//(centerx,centery,centerz)注视的点,和视点之间连线形成注视方向;
GLdouble upx,GLdouble upy,GLdouble upz //(upx,upy,upz)向上的方向,即头顶的方向
);

视点比做人的眼睛,当眼睛看物体时,头朝天时(站立时)看到的物体和头朝地时(倒立时)看到的物体是不一样的.你还可以将头部左右倾斜从不同的角度来看物体.那么,gluLookAt的朝上向量就是用来确定这个方向的.不过在默认情况下,头是朝天的,即朝上向量为(0, 1, 0)。
这里,头顶的方向和视线的方向用参数来设置不容易理解。
向上的方向(即头顶方向)一旦确定后,视线的范围就确定了,和人站住不动后,所能看到的角度是有限的一样。
还是用代码实例测试来看效果,我们以茶壶为三维实体。屏幕朝外的方向是Z轴正向。
1>默认状态下,头顶方向:(0,1,0)
void display(void)
{
glColor3f(0.0f, 1.0f, 0.0f);//画笔颜色
{
glMatrixMode(GL_MODELVIEW); //gluLookAt函数的作用范围就是模型视图下,这里设置是必要的
glLoadIdentity();//初始化当前矩阵为单位矩阵,不设置默认单位矩阵
gluLookAt(,,, ,,, ,,);//头顶方向:Y轴正向,视点位置是Z轴正向1个单位(屏幕外),沿着Z轴看向原点
glutWireTeapot(0.5);//绘制茶壶
}
glFlush();
}
效果图:

2>超出视野范围时,看不到物体了
/* 视点在Y轴正向1单位处,而头顶的方向是Y轴正向,这时,视线看不到脚下的原点,除非低头,而低头的话就改变了头顶的方向,所以如下的设置是看不到物体的! */
gluLookAt(,,, ,,, ,,);
3>俯视观察设置
gluLookAt(,,, ,,, ,,-);//俯视:头朝前,视点沿Y轴俯视看向原点
效果图:

2、模型变换
从“相对移动”的观点来看,改变观察点(即摄影师)的位置与方向和改变物体本身(被拍摄的对象)的位置与方向具有等效性。其实视点本身也算一种模型,也可以调用模型变换函数。
主要有三种模型变换:
(1)模型平移
void myDisplay(void)
{
glColor3f(0.0f, 1.0f, 0.0f);
{
glMatrixMode(GL_MODELVIEW);
//这是没有平移的时候默认设置,(x,y,z)平移量为0
glTranslatef(,,);
//画一个茶壶:一定要先做变换在绘制,先变换是改变了矩阵栈中的当前矩阵,然后用当前矩阵对要绘制的图形做变换
glutWireTeapot(0.5);//画一个茶壶
}
glFlush();
}
需要注意的是,默认情况下,我们的视点位置在(0,0,1)处(float参数下),所以如果平移的超出视点所能看到的位置,则看不到物体。如glTranslatef(0,0,2):到了视点的后脑勺处,自然看不到物体。
glTranslatef(0.5,0.5,);//向右上方偏移

(2)模型旋转
void myDisplay(void)
{
glColor3f(0.0f, 1.0f, 0.0f);
{
glMatrixMode(GL_MODELVIEW);
glRotatef(, ,,);//后三个参数确定了一个方向点,和原点连线构成旋转的轴线,这里是Z轴,即绕Z轴旋转45度
glutWireTeapot(0.5);
}
glFlush();
}

(3)模型缩放
void myDisplay(void)
{
glColor3f(0.0f, 1.0f, 0.0f);
{
glMatrixMode(GL_MODELVIEW);
glScalef(,,0.5);//分别表示x,y,z轴的缩放比例
glutWireTeapot(0.5);
}
glFlush();
}

OpenGl从零开始之坐标变换(上)的更多相关文章
- OpenGl从零开始之坐标变换
http://www.tuicool.com/articles/uiayYrI OpenGL学习脚印: 坐标变换过程(vertex transformation) http://blog.csdn.n ...
- OpenGl从零开始之坐标变换(下)
这节主要来理解投影变换和视口变换的使用. 1.正射投影:glOrtho 函数原型: void glOrtho(GLdouble left,GLdouble right,GLdouble bottom, ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(四)-使用Travis自动部署Hexo(2)
前言 前面一篇文章介绍了Travis自动部署Hexo的常规使用教程,也是个人比较推荐的方法. 前文最后也提到了在Windows系统中可能会有一些小问题,为了在Windows系统中也可以实现使用Trav ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(三)-使用Travis自动部署Hexo(1)
前言 前面两篇文章介绍了在github上使用hexo搭建博客的基本环境和hexo相关参数设置等. 基于目前,博客基本上是可以完美运行了. 但是,有一点是不太好,就是源码同步问题,如果在不同的电脑上写文 ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置
前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置
前言 有朋友问了我关于博客系统搭建相关的问题,由于是做开发相关的工作,我给他推荐的是使用github的gh-pages服务搭建个人博客. 推荐理由: 免费:github提供gh-pages服务是免费的 ...
- 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)
在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...
- 从零开始,在windows上用nodejs搭建一个静态文件服务器
从零开始,在windows上用nodejs搭建一个静态文件服务器 首先安装nodejs: 新建一个node文件夹 下载node.exe到该文件夹 下载npm然后解压到该文件夹 现在node文件夹是这样 ...
- 如何从零开始在github上新建项目
准备工作: (1)安装git: Git-2.16.1-64-bit.exe (2)新建一个文件夹grpc007,作为本地git仓库 (3)进入到grpc007目录,右键/打开git bash.使用gi ...
随机推荐
- 278. First Bad Version
题目: You are a product manager and currently leading a team to develop a new product. Unfortunately, ...
- 机器人学 —— 轨迹规划(Sampling Method)
上一篇提到,机器人轨迹规划中我们可以在 Configuration Space 中运行A* 或者 DJ 算法.无论A* 还是DJ 算法,都必须针对邻域进行搜索,如果2自由度则有4邻域,2自由度则有8邻 ...
- Navicat
create table <表名>( <列名> <数据类型及长度> [not null], <列名> <数据类型及长度>, ...
- javascript把IP地址转为数值几种方案,来挑战一下效率吧
先看看什么是IP地址: IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”(也就是4个字节).IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~25 ...
- HDU4675【GCD of scequence】【组合数学、费马小定理、取模】
看题解一开始还有地方不理解,果然是我的组合数学思维比较差 然后理解了之后自己敲了一个果断TLE.... 我以后果然还得多练啊 好巧妙的思路啊 知识1: 对于除法取模还需要用到费马小定理: a ^ (p ...
- iphone/ipad实现自定义的开关UISwitch(continuous,clipsToBounds,userInteractionEnabled属性)
这里主要讲几个UIView的几个属性,具体大家可以下载代码看看, 下载地址是: http://download.csdn.net/detail/rhljiayou/5960003 实现效果是: 代码中 ...
- JavaScript判断浏览器类型及版本
JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本.JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一 ...
- Debian字符模式下修改显示分辨率
Debian字符模式下修改显示分辨率 一.准备工具 a) Git apt-get install git 二.获取屏幕修改辅助软件 a) 创建临时文件 mkdir /tmp/screenModify ...
- 15.Object-C--浅谈Foundation框架OC数组NSArray与NSMutableArray
昨天总结了一下NSString与NSMutableString,今天我在这里总结一下NSArray与NSMutableArray. NSArray数组是:不可变数组. nil 是数组元素结束的标记.O ...
- 四、Emmet:快速编写HTML,CSS代码的有力工具
介绍 Emmet是一个插件,在IDE中安装该插件后即可使用该功能. HTML代码写起来虽简单,但是重复代码很多,Emmet能够存在一种HTML代码简写法(比较类似CSS的选择器写法),比如 div.c ...