OpenGL编程(八)3D数学与坐标变换
笛卡尔坐标
一维坐标系
以一个点为原点,选定一个方向为正方向(相反的方向为反方向),以一定的距离为标尺建立一维坐标系。一维坐标系一般应用于描述在一维空间中的距离。
举个例子:一维坐标系好比一条拉直的电线(忽略长度),一只老鼠在电线上,对于这只老鼠来说,这个一维坐标系(电线)就是它的世界,只能沿着电线的方向向前或向后运动(当然也可以不动),这只老鼠这个时候是活在“一维”世界里。二维坐标系
百度百科:二维,两根的坐标轴,有平面直角坐标丶自然坐标丶极坐标等,研究平面运动时用。二维坐标系可以解释为在平面内两条相互垂直且有公共原点的数轴组成的坐标系即二维坐标系。
可以理解为:把两个互相垂直,原点重合的一维坐标系组成的新的坐标系为二维坐标系。
注:二维坐标系有多重情况,这只是二维坐标系中的其中。(以正负方向不同区别)
3. 三维坐标系
百度百科:三维笛卡儿坐标系是在二维笛卡儿坐标系的基础上根据右手定则增加第三维坐标(即Z轴)而形成的。
二维坐标的两个轴分别是x轴和y轴,三维坐标系是在二维坐标系上增加一个同时垂直x、y轴的z轴。三维坐标系分左手坐标系和右手坐标系两种,如下图。
三维坐标系
世界坐标系
百度百科:世界坐标系是系统的绝对坐标系,在没有建立用户坐标系之前画面上所有点的坐标都是以该坐标系的原点来确定各自的位置的。
好比在一个游戏场景中,游戏地图就相当于世界,给地图添加笛卡尔三维坐标系后能够通过坐标点来描述地图上的任何一个点的(相对原点)位置。确定坐标系(左手或右手坐标系)和原定后,能通过坐标来描述在地图上的每个点的位置(坐标)
物体坐标系
地图上的每个物体都有一个独立的坐标系,叫做物体坐标系。也许有同学会有英文,有了世界坐标系为啥还需要物体坐标系呢?物体坐标系有什么用途?小时候我们上体育课的时候,体育老师经常让我们左转、右转、后转等。这个时候我们的世界坐标(相对地球)没变,可是我们每次面对不同的方向都不一样,这个就是我们的物体坐标变了。世界坐标系能确定物体的坐标,有了物体坐标系我们还能确定物体的方向。
摄像机坐标系
摄像机坐标系是和观察者有密切关系的坐标系。通过摄像机坐标系,我们能确定在屏幕上显示的内容。
要把3D场景的事物显示到一个2D的屏幕上,需要一个摄像机,可是某个时刻,我们只能显示一个画面的事物。摄像机的位置,方向不同,所拍取的景也就不一样。
惯性坐标系
引入惯性坐标系的概念是为了辅助物体坐标系到世界坐标系的转换。物体坐标系和世界坐标系一样也是一个三维坐标系,而且有左手坐标系、右手坐标系之分。
当物体的方向不是正方向的时候,我们可以通过旋转把物体调为正方向。如果物体的坐标系原点不是在世界坐标系的原点,我们可以通过移动物体的位置来把物体移到原点。
惯性坐标系就是表示当物体的方向是正方向但是坐标不是在世界坐标系原点时成为惯性坐标系。
简单的线性变换
- 平移
通过改变物体的世界坐标系的x、y或者z值,使物体在世界坐标系上的位置发生变化成为平移。如下图:
其实在OpenGL上,是先确定物体的位置再在改位置绘制物体。
如上图,在绘制立方体之前先把它的y轴向上移动10个单位,可以执行以下代码:
glTraslatef(0.0f, 10.0f, 0.0f);
然后再绘制立方体:
glutWireCube(10.0f);
=====================
2. 旋转
绕着某个旋转轴对物体选择0-360度,使物体的物体坐标系旋转。旋转函数:
glRotatef(angle, x, y, z);
与平移一样,在OpenGL中,也是先通过旋转再绘图。如上图的代码为:
glRotatef(90.0f, 1.0f, 1.0f, 1.0);
glutWireCube(10.0f);
=====================
3. 缩放
缩放变换根据指定的因子,将所有顶点在三根轴的方向上都展宽,由此来增加对象的大小。缩放函数:
glScalef(GLfloat x, GLfloat y, GLfloat z);
实现代码:
glScalef(2.0f, 1.0f, 2.0f);
glutWireCube(10.0f);
=====================
4. 矩阵堆栈
在我们使用前面的平移、旋转、伸缩函数时,调用这些函数后,如果没有复原的话,这些函数的效果是会累积的。例如要实现以下效果:
绘制两个球体,一个的坐标是x轴移动10个像素,另外一个球体的坐标是y轴移动10个像素。正常情况下,认为可以通过以下代码实现:
//y轴移动10个单位
glTranslatef(0.0f, 10.0f, 0.0f);
//绘制第一个球体
glutSolidSphere(1.0f);
//x轴移动10个单位
glTranslatef(10.0f, 0.f, 0.0f);
//绘制第二个球体
glutSolidSphere(1.0f);
如果使用以上的代码,实现的效果如下:
因为我们在绘制第一个球体后,原来锚点的坐标y轴移动了10个单位,我们没有实现复原操作。绘制第二个球体前,移动x轴坐标10个单位,两个操作叠加的效果是,x轴正方向移动10个单位,y轴正方向移动10个单位。那应该怎样才能实现我们想要的效果呢?
绘制完第一个球体后,我们应该调用函数实现复位坐标轴。
glLoadIdentity()
实现代码如下:
//y轴移动10个单位
glTranslatef(0.0f, 10.0f, 0.0f);
//绘制第一个球体
glutSolidSphere(1.0f);
glLoadIdentity()
//x轴移动10个单位
glTranslatef(10.0f, 0.f, 0.0f);
//绘制第二个球体
glutSolidSphere(1.0f);
某些情况下需要用到线性变换的叠加,可是有的时候不需要。为了管理好线性变换的,于是引入了矩阵堆栈的概念。
如上图,当我们要执行某个变换前,可以先执行glPushMatrix操作,实现入栈,执行完后,如果需要保留这个线性变换(需要保留这个累积线性变换)即不需要执行其他操作;否则(不需要保留这个累积效果)执行glPopMatrix操作把该线性变换抛出变换矩阵。
附:3D数学与坐标变换这块涉及到的数学知识点比较多(向量、各种变换数学原理及变换矩阵)。感兴趣的可以学习《3D数学基础图形与游戏开发》这本书,书上讲得很详细。
示例
这次的实验代码也是在前面的实验代码框架基础上添加新的内容。
1. 绘制太阳
在坐标系中心(原点)绘制一个红色的太阳。首先设置为红色,绘制一个半径为10的球体。代码如下:
glColor3ub(255, 0, 0);
glutSolidSphere(10.0f, 15, 15);
2. 绘制火星
火星在相对太阳偏移90个单位的距离:
glRotatef(saturnRotAngle, 0.0f, 1.0f, 1.0f);
设置火星的颜色:
glColor3ub(255, 200, 100);
绘制火星,假设火星的半径为5:
glutSolidSphere(5.0f, 15, 15);
由于后面我们还需要绘制地球,而地球的位置时相对于太阳的位置,所以不需要上面的位置偏移积累。所以绘制完火星后要返回上一步的位置,分别在绘制火星的代码前面与后面添加函数glPushMatrix()与glPopMatrix()。完整代码如下:
glColor3ub(255, 200, 100);
glPushMatrix();
glTranslatef(90.0f, 0.0f, 0.0f);
glutSolidSphere(5.0f, 15, 15);
glPopMatrix();
3. 绘制地球
以相同于绘制火星的原理绘制地球,代码如下:
glColor3ub(0, 0, 200);
glPushMatrix();
glTranslatef(60.0f, 0.0f, 0.0f);
glutSolidSphere(7.0f, 15, 15);
glPopMatrix();
4. 绘制月亮
由于月球是地球的卫星,绕着地球转,所以月球的位置时相对于地球的位置。所以绘制月球跟绘制地球的代码要被glPushMatrix()与glPopMatrix()函数包着:
//地球
glColor3ub(0, 0, 200);
glPushMatrix();
glTranslatef(60.0f, 0.0f, 0.0f);
glutSolidSphere(7.0f, 15, 15);
//月亮
glColor3ub(88, 88, 88);
glTranslatef(20.0f, 0.0f, 0.0f);
glutSolidSphere(2.0f, 15, 15);
glPopMatrix();
5. 让天体动起来
每次重新绘制时要给火星、地球一个旋转的角度,每次以相同方向一定的角速度旋转。同样的原理去实现月球绕地球转。
static float earthRotAngle = 0.0f;
static float saturnRotAngle = 0.0f;
static float moonRotAngle = 0.0f;
当旋转角度累加到大于360度时,控制角度0
moonRotAngle += 15.0f;
if(moonRotAngle > 360.0f)
moonRotAngle -= 360.0f;
glutSolidSphere(2.0f, 15, 15);
glPopMatrix();
earthRotAngle += 10.0f;
if(earthRotAngle > 360.0f)
earthRotAngle -= 360.0f;
saturnRotAngle += 6.0f;
if(saturnRotAngle > 360.0f)
saturnRotAngle -= 360.0f;
注册一个回调函数,定时刷新屏幕:
void TimerFunc(int value)
{
glutPostRedisplay();
glutTimerFunc(100, TimerFunc, 1);
}
总结
这次实验涉及到的主要知识点有平移、旋转等线性变换以及矩阵堆栈的工作原理。三维世界里的平移、旋转、缩放涉及到的数学很多,每个线性操作都有对于的矩阵去处理(本科学的线性代数就很有帮助咯),OpenGL都封装好了底层的数学原理,提供了调用接口给我们直接调用。如果有兴趣,可以去了解一下,对掌握底层知识很有帮助。推荐这本书《3D数学基础图形与游戏开发》。
完整代码
#include <windows.h>
#include <gl/glut.h>
#include<math.h>
const GLfloat PI = 3.1415f;
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
void rendererScene(void);
void changeWindowSize(GLsizei w, GLsizei h);
void setupRC(void);
void rotateMode(int key, int x, int y);
//定时刷新显示
void TimerFunc(int value)
{
glutPostRedisplay();//刷新
glutTimerFunc(100, TimerFunc, 1);//定时器
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
//设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//设置窗口大小
glutInitWindowSize(300, 300);
//设置窗口在屏幕上的位置
glutInitWindowPosition(200, 200);
//创建窗口标题
glutCreateWindow("三角形绘3D模型");
glOrtho(-100.0f, 100.0f, -100.0f, 100.0f, -100.0f, 100.0f);
//注册窗口大小改变时回调函数
glutReshapeFunc(changeWindowSize);
//注册点击上下左右方向按钮时回调rotateMode函数
glutSpecialFunc(rotateMode);
//注册显示窗口时回调渲染函数
glutDisplayFunc(rendererScene);
glutTimerFunc(500, TimerFunc, 1);
setupRC();
//消息循环(处理操作系统等的消息,例如键盘、鼠标事件等)
glutMainLoop();
return 0;
}
/**
渲染函数
*/
void rendererScene(void)
{
static float earthRotAngle = 0.0f;
static float saturnRotAngle = 0.0f;
static float moonRotAngle = 0.0f;
BOOL bCull = TRUE; //是否开启回溯剔除
BOOL bDepth = TRUE; //是否开启深度检测
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(bCull) glEnable(GL_CULL_FACE);
else glDisable(GL_CULL_FACE);
if(bDepth) glEnable(GL_DEPTH_TEST);
else glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//glTranslatef(0.0f, 0.0f, -100.0f);
//太阳
glColor3ub(255, 0, 0);
glutSolidSphere(10.0f, 15, 15);
//火星
glColor3ub(255, 200, 100);
glPushMatrix();
glRotatef(saturnRotAngle, 0.0f, 1.0f, 1.0f);
glTranslatef(90.0f, 0.0f, 0.0f);
glutSolidSphere(5.0f, 15, 15);
glPopMatrix();
//地球
glColor3ub(0, 0, 200);
glPushMatrix();
glRotatef(earthRotAngle,0.0f, 1.0f, 1.0f);
glTranslatef(50.0f, 0.0f, 0.0f);
glutSolidSphere(7.0f, 15, 15);
//月亮
glColor3ub(88, 88, 88);
glRotatef(moonRotAngle, 0.0f, 1.0f, 1.0f);
glTranslatef(20.0f, 0.0f, 0.0f);
moonRotAngle += 15.0f;
if(moonRotAngle > 360.0f)
moonRotAngle -= 360.0f;
glutSolidSphere(2.0f, 15, 15);
glPopMatrix();
earthRotAngle += 10.0f;
if(earthRotAngle > 360.0f)
earthRotAngle -= 360.0f;
saturnRotAngle += 6.0f;
if(saturnRotAngle > 360.0f)
saturnRotAngle -= 360.0f;
glutSwapBuffers();
}
/**
改变窗口大小时回调函数
*/
void changeWindowSize(GLsizei w, GLsizei h)
{
GLfloat length = 100.0f;
if(h == 0) h = 1;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w <= h) glOrtho(-length, length, -length * h / w, length * h / w, -length*2.0f, length*2.0f);
else glOrtho(-length * w / h, length * w / h, -length, length, -length*2.0f, length*2.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
/**
设置
*/
void setupRC(void)
{
//背景颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glShadeModel(GL_FLAT);
}
/**
旋转
*/
void rotateMode(int key, int x, int y)
{
if(key == GLUT_KEY_UP) xRot -= 5.0f;
else if(key == GLUT_KEY_DOWN) xRot += 5.0f;
else if(key == GLUT_KEY_LEFT) yRot -= 5.0f;
else if(key == GLUT_KEY_RIGHT) yRot += 5.0f;
if(xRot < 0) xRot = 355.0f;
if(xRot > 360.0f) xRot = 0.0f;
if(yRot < 0) yRot = 355.0f;
if(yRot > 360.0f) yRot = 0.0f;
glutPostRedisplay();
}
OpenGL编程(八)3D数学与坐标变换的更多相关文章
- 编译opengl编程指南第八版示例代码通过
最近在编译opengl编程指南第八版的示例代码,如下 #include <iostream> #include "vgl.h" #include "LoadS ...
- Qt OpenGL:学习现代3D图形编程之四,透视投影浅析
一.非真实的世界 与之前几篇文章不同的是,这里要画12个三角形,这个12个三角形构造一个方形棱柱(这里为长方体).棱柱的每个四边形表面由两个三角形组成.这两个三角形其中的一条边重合,而且它们的六个顶点 ...
- [OpenGL ES 03]3D变换:模型,视图,投影与Viewport
[OpenGL ES 03]3D变换:模型,视图,投影与Viewport 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议 系列 ...
- 【Qt编程】3D迷宫游戏
说起迷宫想必大家都很熟悉,个人感觉迷宫对人的方向感是很大的考验,至少我的方向感是不好的,尤其是在三维空间中.由于这段时间帮导师做项目用到了三维作图,便心血来潮想做个三维迷宫玩玩.要想画出三维的迷宫游戏 ...
- Unity3D之空间转换学习笔记(三):3D数学
3D数学基础 向量 向量可以看做具有方向和大小的一条线段. 比如:我们如果用点A减去点B,则可以得到一个向量,该向量的方向为点B面向点A的方向,而大小为两点的距离.这个方法在游戏开发中经常用到,比如我 ...
- 用MFC实现OpenGL编程
一.OpenGL简介 众所周知,OpenGL原先是Silicon Graphics Incorporated(SGI公司)在他们的图形工作站上开发高质量图像的接口.但最近几年它成为一个非常优秀的开放式 ...
- VS15 openGL 编程指南 配置库 triangle例子
最近去图书馆借了一本书<OpenGL编程指南(原书第八版)>,今天倒腾了一天才把第一个例子运行出来. 所以,给大家分享一下,希望能快速解决配置问题. 一.下载需要的库文件 首先,我们需要去 ...
- 3D数学读书笔记——四元数
本系列文章由birdlove1987编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhurui_idea/article/details/25400659 什么是四元数 ...
- 3D数学读书笔记——矩阵基础番外篇之线性变换
本系列文章由birdlove1987编写.转载请注明出处. 文章链接:http://blog.csdn.net/zhurui_idea/article/details/25102425 前面有一篇文章 ...
随机推荐
- Lazy Stored Properties--无括号时为匿名函数
第一次使用的时候进行计算和初始化,后面的引用不在进行计算. A lazy stored property is a property whose initial value is not calcul ...
- 使用jquery获取css的top和left属性
使用jquery获取css的top和left属性 因为left和top也都是普通的css属性所以可以使用如下代码来获取 var left = $('#test').css('left'); var t ...
- 操作ajax生成页面的一个问题
一般而言,js代码都放在页面的底部.在做项目的过程中,发现放在底部的代码没有执行,原来操作的是ajax生成的部分.这时候,页面加载js的顺序就要小心了.例子如下: <!doctype html& ...
- 解决远程登录mysql命令行无法登录问题
由于甲骨文公司收购了mysql,mysql有商业化的趋势,所以想尝试安装下mariadb . yum install mariadb-server mariadb systemctl start ma ...
- Ubuntu Server 与 Ubuntu Desktop区别
今天有位朋友问我,Ubuntu Server 与 Ubuntu Desktop的区别在哪里!区别如下: SERVER没有GUI SERVER没有一堆的桌面软件 SERVER在编译时使用的参数不一样,会 ...
- 纯css3实现箭头、关闭按钮旋转效果
说起css3的旋转效果,那就要说为什么不用js去实现,CSS3的动画效果,能够减少对JavaScript和Flash文件的HTTP请求这是原因之一.但是css3可能要求浏览器执行很多的工作来完成这个动 ...
- Problem 3
Problem 3 # Problem_3.py """ The prime factors of 13195 are 5, 7, 13 and 29. What is ...
- Hibernate的事务机制
http://www.cnblogs.com/jerryxing/archive/2012/04/24/2468999.html http://www.cnblogs.com/xiaofeilee/p ...
- BA--暖通系统常见设计细节要点
(一)系统设计问题 1.水泵在系统的设计位置: 一般而言,冷冻水泵应设在冷水机组前端,从末端回来的冷冻水经过冷冻水泵打回冷水机组:冷却水泵设在冷却水进机组的水路上,从冷却塔出来的冷却水经冷却水泵打回机 ...
- BA--冷源系统原理图解