OpenGL实现3D漫游的理解
这篇文章主要参考以下两篇博客:
上面的第一篇是理论的讲解,第二篇有实例代码,我在后面会给出自己写的主函数,依赖的类可以从第二篇参考中下载。
我这篇文主要谈我个人对OpenGL中实现3D漫游的思路的理解。经过这些天的学习,主要是研究别人写的代码和网上的的博客,我初步理解了OpenGL中对于多方位观察物体的实现策略。其实,对于3维坐标变换,每个人都可以有自己的理解方式,有的人喜欢研究一堆矩阵,有些人喜欢从空间几何的角度去理解。
一 要实现3D漫游,第一步就要确定投影方向
OpenGL首先定义了一个世界坐标系(xyz),还有一个UVN坐标系,两个都是右手系。观察者总是沿着N轴负方向去观察xyz,而物体也总是放在xyz中的。在OpenGL中,xyz其实是一个与物体保持相对位置不动的坐标系,而UVN坐标系其实是一个与观察点位置保持相对不动的坐标系。那么怎样才能全方位观察物体呢?无外乎两个思路:1,让xyz绕着UVN转;2,让UVN绕着xyz转。从OpenGL提供的接口函数及其实现的效果来看,其采用的是前者。
OpenGL中,屏幕中所呈现的视平面其实是UOV平面,屏幕上方为V轴正方向,屏幕右方为U轴正方向,屏幕由里到外为N轴正方向。因此视线方向总是沿N轴负方向。要想从某个特殊的角度去观察xyz坐标系(物体),只需要确定一个变换矩阵,将xyz先绕着UVN旋转,然后再沿着NO的方向平移,就能一览无余了。其实又可以这样理解,最初xyz与UVN其实是重合的,然后让xyz先乘一个旋转矩阵,再乘一个平移矩阵,这样xyz就以一个特定的姿态出现在视野中了,准确的说是,物体在UOV平面产生了一个特定的投影。
下面是一些变换矩阵:
1.平移矩阵:
其逆矩阵从平移过程去思考的话,显然是反向平移对应的矩阵,因此其逆矩阵只是在x,y,z前面加个负号。
2.绕x,y,z轴旋转的矩阵:
三个旋转矩阵Rx,Ry,Rz,它们的列项向量都是两两互相垂直,并且都是单位向量,所以Rx,Ry,Rz都是正交矩阵,它们的逆就是其自身的转置。并且可以证明:有限个正交矩阵的乘积仍为正交矩阵。
3.绕空间某一向量(x, y, z)旋转的矩阵。注意向量是有方向的,旋转规则仍是右手螺旋定则:
二 投影方向确定了,下一步就是确定投影面的位置,并且还需要在投影面中选取一个区域,该区域就是最终显示在屏幕上的图像
下面这张图我认为很清晰地展示了投影面的确定过程
上图采用的是透视投影,OpenGL中还有一种平行投影,分别由函数
void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar )
void glOrtho( GLdouble left, GLdouble right,
GLdouble bottom, GLdouble top,
GLdouble near_val, GLdouble far_val )
确定投影的参数。gluPerspective参数中的fovy就是图中的视角,以度为单位,zNear为近平面距离,zFar为远平面距离。
选定投影区域后,会将该区域的坐标标准化到[-1, 1]之间,再通过函数void glViewport( GLint x, GLint y, GLsizei width, GLsizei height )
将标准化的坐标映射到图像坐标,映射公式如下
图中xnd,ynd是归一化后的坐标,xw,yw是对应的图像坐标,以像素为单位,当xnd=-1,ynd=-1时,映射的是图像左下角的坐标,即以像素为单位时,图像坐下角坐标为(0, 0)。下面是一段实现3D漫游的代码,按x键让茶壶绕U轴旋转,按y键让茶壶绕V轴旋转,也可以用鼠标控制茶壶旋转。代码中依赖的矩阵类可从参考博客中获得。
#include <GL/glut.h>
#include "Matrices.h"
#include "Vectors.h" // GLUT CALLBACK functions
void displayCB();
void reshapeCB(int w, int h);
void timerCB(int millisec);
void keyboardCB(unsigned char key, int x, int y);
void mouseCB(int button, int stat, int x, int y);
void mouseMotionCB(int x, int y);
void initGL();
void initLights();
void drawAxis(float size = 0.6f); const int SCREEN_WIDTH = ;
const int SCREEN_HEIGHT = ;
const float CAMERA_DISTANCE = 1.5f;
const int TEXT_WIDTH = ;
const int TEXT_HEIGHT = ;
const float DEG2RAD = 3.141593f / ; // global variables
int screenWidth;
int screenHeight;
bool mouseLeftDown;
bool mouseRightDown;
float mouseX, mouseY;
float cameraAngleX;
float cameraAngleY;
float cameraDistance;
Matrix4 matrixView;
Matrix4 matrixModel;
Matrix4 matrixModelView;
Matrix4 matrixProjection; int main(int argc, char **argv)
{
// init global vars
screenWidth = SCREEN_WIDTH;
screenHeight = SCREEN_HEIGHT;
mouseLeftDown = mouseRightDown = false;
mouseX = mouseY = ;
cameraAngleX = cameraAngleY = ;
cameraDistance = CAMERA_DISTANCE; glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); // display mode
glutInitWindowSize(screenWidth, screenHeight); // window size
glutInitWindowPosition(, ); // window location
glutCreateWindow(argv[]); // param is the title of window glEnable(GL_DEPTH_TEST);
glutReshapeFunc(reshapeCB);
glutDisplayFunc(displayCB);
glutTimerFunc(, timerCB, ); // redraw only every given millisec glutKeyboardFunc(keyboardCB);
glutMouseFunc(mouseCB);
glutMotionFunc(mouseMotionCB); glutMainLoop(); return ;
} void drawAxis(float size)
{
glDepthFunc(GL_ALWAYS); // to avoid visual artifacts with grid lines // draw axis
glLineWidth();
glBegin(GL_LINES);
glColor3f(, , );
glVertex3f(, , );
glVertex3f(size, , );
glColor3f(, , );
glVertex3f(, , );
glVertex3f(, size, );
glColor3f(, , );
glVertex3f(, , );
glVertex3f(, , size);
glEnd();
glLineWidth(); // draw arrows(actually big square dots)
glPointSize();
glBegin(GL_POINTS);
glColor3f(, , );
glVertex3f(size, , );
glColor3f(, , );
glVertex3f(, size, );
glColor3f(, , );
glVertex3f(, , size);
glEnd();
glPointSize(); // restore default settings
glDepthFunc(GL_LEQUAL);
} void displayCB()
{
// clear buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // transform camera
matrixView.translate(, , cameraDistance);
matrixView.rotate(cameraAngleX, , , ); //cameraAngleX
matrixView.rotate(cameraAngleY, , , ); //cameraAngleY
matrixView.translate(, , -cameraDistance);
cameraAngleX = ;
cameraAngleY = ;
glLoadMatrixf(matrixView.get()); glColor3f(1.0f, 0.3f, 1.0f);
glutWireTeapot(0.6f);
drawAxis(); glutSwapBuffers();
} void reshapeCB(int w, int h)
{
// set viewport to be the entire window
glViewport(, , (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION);
gluPerspective(80.0f, (float)(w)/h, 0.5f, 10.0f); // FOV, AspectRatio, NearClip, FarClip // switch to modelview matrix in order to set scene
glMatrixMode(GL_MODELVIEW);
matrixView.identity();
matrixView.translate(, , -cameraDistance);
glLoadMatrixf(matrixView.get());
} void timerCB(int millisec)
{
glutTimerFunc(millisec, timerCB, millisec);
glutPostRedisplay();
} void keyboardCB(unsigned char key, int x, int y)
{
switch(key)
{
case 'x':
cameraAngleX = ;
break;
case 'X':
cameraAngleX = -;
break;
case 'y':
cameraAngleY = ;
break;
case 'Y':
cameraAngleY = -;
break;
case : // ESCAPE
exit();
break;
default:
break;
}
} void mouseCB(int button, int state, int x, int y)
{
mouseX = x;
mouseY = y; if(button == GLUT_LEFT_BUTTON)
{
if(state == GLUT_DOWN)
{
mouseLeftDown = true;
}
else if(state == GLUT_UP)
mouseLeftDown = false;
}
else if(button == GLUT_RIGHT_BUTTON)
{
if(state == GLUT_DOWN)
{
mouseRightDown = true;
}
else if(state == GLUT_UP)
mouseRightDown = false;
}
} void mouseMotionCB(int x, int y)
{
if(mouseLeftDown)
{
cameraAngleY = (x - mouseX);
cameraAngleX = (y - mouseY);
mouseX = x;
mouseY = y;
}
if(mouseRightDown)
{
matrixView.translate(, , cameraDistance);
cameraDistance += (y - mouseY) * 0.01f;
mouseY = y;
matrixView.translate(, , -cameraDistance);
}
}
效果如下图:
OpenGL实现3D漫游的理解的更多相关文章
- OpenGL中的像素包装理解
OpenGL中的像素包装理解 像素包装 位图和像素图很少会被紧密包装到内存中.在许多硬件平台上,考虑到性能的原因位图和像素图的每一行的数据会从特殊的字节对齐地址开始.绝大多数编译 器会自动把变量和缓冲 ...
- 3DTools TrackballDecorator实现3D漫游
原文:3DTools TrackballDecorator实现3D漫游 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/m0_37591671/art ...
- 3D漫游的分类 3D Navigation Taxonomy
在2001年CHI发表的论文中1,Tan等人提出了一种对3D漫游的分类方法. 当时关于3D漫游(3D Navigation)的研究主要分为两种:一种是发掘有关漫游的认知原则,一种是开发一些具体的漫游技 ...
- KTL 一个支持C++14编辑公式的K线技术工具平台 - 第六版,支持OpenGL,3D上帝视角俯视K线概貌。
K,K线,Candle蜡烛图. T,技术分析,工具平台 L,公式Language语言使用c++14,Lite小巧简易. 项目仓库:https://github.com/bbqz007/KTL 国内仓库 ...
- OpenGL中各种坐标系的理解[转]
OPENGL坐标系可分为:世界坐标系和当前绘图坐标系. 世界坐标系:在OpenGL中,世界坐标系是以屏幕中心为原点(0, 0, 0),且是始终不变的.你面对 屏幕,你的右边是x正轴,上面是y正轴,屏幕 ...
- Android 3D emulation 架构理解
Android Emulator 给用户提供 GPU on 选项,意思是利用 Host ( 就是执行 Emulator 的PC机) 的 GPU. 当然PC机必须把 OpenGL 的驱动装好 在实现上 ...
- 最新广商小助手 项目进展 OpenGL ES 3D在我项目中引用 代码太多只好选重要部分出来
package com.example.home; import java.io.IOException; import java.io.InputStream; import javax.micro ...
- OpenGL实现3D自由变形
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...
- cartographer 3D scan matching 理解
cartographer 3D scan matching没有论文和其它资料,因此尝试通过源码理解其处理方法,理解不当之处还请指正. 目录: 0.2D 匹配方法简介 1.real time corre ...
随机推荐
- 重复加载同一个jqgrid
重复加载同一个jqgrid时需要先清除原先的数据,再进行加载新的数据: 清除时使用方法:jQuery.jgrid.gridUnload('jqGridId'); 同时还有一个GridDestroy的方 ...
- 使用stylelint对CSS/Sass做代码审查
对样式审查?很少人会这么做吧,但实际上开发者应该有这样的态度,尤其是不同团队多人开发时,这一点尤为重要. 在本文中,我将陈述两点:一是为什么我们需要对样式进行审查,二是如何将审查工具融合到整体的构建流 ...
- BZOJ 4742: [Usaco2016 Dec]Team Building
4742: [Usaco2016 Dec]Team Building Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 21 Solved: 16[Su ...
- wireshark lua脚本
1.目的:解析rssp2协议 2.如何使用wireshark lua插件 将编写的(假设为rssp2.lua)lua文本,放入wireshark 安装目录下,放哪里都行只要dofile添加了路径. ...
- BootLoader(2440)核心初始化代码
1.gboot.lds OUTPUT_ARCH(arm)ENTRY(_start)SECTIONS { . = 0x30008000; //起始地址 . = ALIGN(4);// ...
- php实现数据粘性例子
php实现数据粘性例子 在填表单的时候,有时候会出现表单未填完就提交的情况,这时候若是想要回到原来的表单页面,一般之前填的内容都会消失掉. 故使用PHP实现回到原来表单但是填写数据不消失,代码
- maven报brors occurred during the build
原因分析: 此问题一般发生在eclipse保存文件并自动部署时候.本人在写项目的时候,还没等部署好,关闭了了eclipse,结果出现了这种情况.有一种产生此错误的原因是因为此项目不不是由eclipse ...
- Java网络编程--简单聊天程序
背景 毕业设计前的练手,学校小比赛中的一个题目. 开发环境 Java(eclipse)+Mysql 简介 使用Java+Mysql开发以个简单的聊天工具,在本次项目中实现了: 1. 用户登录(客户端至 ...
- 原生JS会跳动的电子表
一个会跳动的电子表,源码--time.html 图片--img github地址:https://github.com/1056237661/practiceCode <!DOCTYPE h ...
- 【转载】--仅用 []()+! 就足以实现几乎任意Javascript代码
最近在做CTF的题目,遇见了一个都是[]()+!这样的文件,于是百度了一下,发现这个博客对这个有解释. G Reader里Dexter同学的分享,来自sla.ckers.org的又一神作 点我测试 G ...