QT.docx
- 文档编号:9014877
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:14
- 大小:26.11KB
QT.docx
《QT.docx》由会员分享,可在线阅读,更多相关《QT.docx(14页珍藏版)》请在冰豆网上搜索。
QT
QT+openGL
写这篇Blog,是为了记录自己学习的过程。
Tobeaseniorsoftwareengineer,也许途中会留下很多辛勤的汗水与美好的回忆。
也许,每个人都有自己心目中软件工程师的样子。
我想把这些故事好好地写下来,与大家一起分享。
我并不那么专业,所以这篇Blog可能看起来更像是一篇学习心得。
我会按不同的主题写成不同的章节。
如果某一章涉及到的知识你不感兴趣,你可以直接跳过。
也希望这篇Blog对你有所帮助。
------bychuckGao
Chapter1openGL
我决定第一章从最近的工作写起。
openGL是什么?
我不太能回答这个问题,不过我知道我打算用它来做些什么。
我想要做一个简单的导航系统,而我打算用openGL绘制我的三维导航。
其实,这对于我来说事件十分复杂的工作,而且我给自己定下的目标仅仅是能够简单的实现几个场景切换就可以了。
为此,我上网搜索了openGL的教程。
因为我打算用QT开发,所以最后找到了qiliang前辈(技术上的)的一篇QTopenGL教程。
如果大家对此更感兴趣,可以登陆他的网页学习这篇教程:
1.糖果盒子
首先是场景的建立。
我打算将我所有的场景都放在一个正方体(或长方体)里,并把它想象成一个糖果盒子,把正对自己的面删去。
这样,看起来就仿佛是一个舞台,并且有立体的感觉。
对于怎样构造这样的一个舞台,
我将主要步骤例举出来,并在随后补充相关的知识:
①画一个立方体,并不要画正面。
这就好像你推开门走进一间房,我们现在删掉门,让房间里的布局映入我们眼帘;
②为我们的立方体的每个面(除开正面)设置贴图,让它看起来像真实的立体场景。
这里,应该是5个面;
③将贴图设置成半透明的,这样看起来更加生动。
④在必要的时候旋转场景
我们先对第一步进行详细说明。
画立方体的基础是你要知道如何画一个面。
怎样画一个面是有顺序要求的,而画一个立方体的各个面则没有,你可以先画其中任意的一面。
对于画一个面则需要遵循下面的步骤:
先右上坐标,然后左上,然后左下,最后右下。
即按逆时针的方向画。
至于为什么要这样我不太清楚,但只需知道就行。
我们按上面的要求将立方体的五个面都画好。
代码部分很简单,你可以看qiliang的QTopenGL教程,这里不再贴出。
第二步是为每个面设置贴图。
贴图只是一个形象的称呼,专业的术语叫做纹理贴图(texture).纹理贴图的设置需要在画一个面之前。
如果你想每个面的贴图不同,那么你需要在绘制每个面之前绑定你的纹理贴图。
这里,我给出一些简单的代码来说明。
而使用的纹理贴图需要先设置好图片路径及参数。
代码,为绘制的面选择纹理贴图:
glBindTexture(GL_TEXTURE_2D,texture[0]);
glBegin(GL_QUADS);
glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0, 1.0);
glTexCoord2f(1.0,0.0);glVertex3f( 1.0,-1.0, 1.0);
glTexCoord2f(1.0,1.0);glVertex3f( 1.0, 1.0, 1.0);
glTexCoord2f(0.0,1.0);glVertex3f(-1.0, 1.0, 1.0);
glEnd();
glBindTexture(GL_TEXTURE_2D,texture[1]);
glBegin(GL_QUADS);
glTexCoord2f(1.0,0.0);glVertex3f(-1.0,-1.0,-1.0);
glTexCoord2f(1.0,1.0);glVertex3f(-1.0, 1.0,-1.0);
glTexCoord2f(0.0,1.0);glVertex3f( 1.0, 1.0,-1.0);
glTexCoord2f(0.0,0.0);glVertex3f( 1.0,-1.0,-1.0);
glEnd();
这里的texture[0]和texture[1]就是我们使用的纹理贴图,而纹理贴图的设置部分如下:
QImageimg1,img2,;
QImagebuf;
if(!
buf.load("./1.bmp"))
{
qWarning("Couldnotreadimagefile,usingsingle-colorinstead.");
QImagedummy(128,128,QImage:
:
Format_RGB32);
//dummy.fill(Qt:
:
green.rgb());
buf=dummy;
}
img1=QGLWidget:
:
convertToGLFormat(buf);
if(!
buf.load("./2.bmp"))
{
qWarning("Couldnotreadimagefile,usingsingle-colorinstead.");
QImagedummy(128,128,QImage:
:
Format_RGB32);
//dummy.fill(Qt:
:
green.rgb());
buf=dummy;
}
img2=QGLWidget:
:
convertToGLFormat(buf);
//1
glBindTexture(GL_TEXTURE_2D,texture[0]);
glTexImage2D(GL_TEXTURE_2D,0,3,img1.width(),img1.height(),0,
GL_RGBA,GL_UNSIGNED_BYTE,img1.bits());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
//2
glBindTexture(GL_TEXTURE_2D,texture[1]);
glTexImage2D(GL_TEXTURE_2D,0,3,img2.width(),img2.height(),0,
GL_RGBA,GL_UNSIGNED_BYTE,img2.bits());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
这一步完成后,我们就得到了一个拥有五个面的盒子。
现在要做的就是使它看起来更加立体。
这里,我们用到openGL融合的一些知识。
其实很简单,如果你不深入下去,那么只用简单的添加几行代码到initializeGL()里面,它们是:
glColor4f(1.0,1.0,1.0,0.5);
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
并使效果enable:
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
好了,完成上面的步骤后,我们的场景已经建立好了。
我们在设置场景的同时也会考虑能否旋转或者移动它以达到某些效果。
所以最后,我们需要为之做一些铺垫,使我们的场景可以按我们的意愿旋转、移动。
这里我们用到glRotatef(xRot, 1.0, 0.0, 0.0)这个函数。
第一个参数代表旋转地角度,后面三个参数是我们的对象相对于xyz轴旋转的位置.假设我们现在要旋转我们的场景,那么我们可以定义一个xRot变量,在最开始将其赋值为0.0,并在每次画图后xRot+=2之后updateGL()一下。
这样我们将会得到一个绕x轴正向1.0位置旋转地对象。
你可以任意改变绕xyz轴旋转的位置,但不要超出你场景的大小。
而移动场景则用glTranslatef( 0.0, 0.0,zoom)函数,三个参数分别是xyz轴,zoom为正值表示沿z轴正方向移动,负值表示沿负方向(即屏幕后方)移动。
到此为止,我们的场景就ok了。
如果有什么疑问可以给我留言或邮件。
当然,建议大家先去阅读下相关的教程再来看我这个实际的项目。
在下一节中,我们将研究物体在场景中移动的问题。
2.单薄的小人
上一节中我们展示了如何创建一个看似立体的场景,当然,我们只是靠一些纹理贴图让它看起来立体,而不是真正的去构建三维模型。
在这一节中,我们要在前面的场景中加入一些元素。
我们把这些加入的元素想象成人、汽车或者是其他的一些东西。
而我们的目标是让它们看起来是在场景中移动。
还记得上一节中我们只绘制了5个面吗,现在,我们把第六个面加进来,但是并不将它作为我们立方体的正面。
你可以把第六个面想象成皮影戏中的小人,而我们要完成的工作仅仅是让这个单薄的小人在我们的场景中移动。
当然,这看起来十分的简单,当我做完时我也感觉到完成这项工作其实对于学习openGL没有什么实质性的进展,所以我决定在下一节中与大家分享构建多场景,并且加入立体元素的内容。
而这一节,也希望你能耐心的看下去,你将从中学会怎样移动一个对象和openGL中的坐标系统。
场景或对象的移动与旋转是在一开始遍完成的。
这么说的原因在于,每当我们移动或旋转一个对象时,其实我们在进行updateGL(),即重绘我们的场景和对象。
想象一下,我们是如何画一个面的,当时我们用的是确定的一些坐标。
这么说我想你应该能反应过来,对象和场景的移动其实就是用到为变量的坐标,我们对这些变量进行操作,然后update一下。
对于坐标系统D.MichaelTraub:
提供了对Xvector,Yvector和Zvector的上述解释。
为了更好的理解X,Y和Z的旋转,我们看一些例子...
X轴-您正在使用一台台锯。
锯片中心的轴从左至右摆放(就像OpenGL中的X轴)。
尖利的锯齿绕着X轴狂转,看起来要么向上转,要么向下转。
取决于锯片开始转时的方向。
这与我们在OpenGL中绕着X轴旋转什么的情形是一样的。
(CKer注:
这会儿您要把脸蛋凑向显示器的话,保准被锯开了花^-^。
)
Y轴-假设您正处于一个巨大的龙卷风中心,龙卷风的中心从地面指向天空(就像OpenGL中的Y轴)。
垃圾和碎片围着Y轴从左向右或是从右向左狂转不止。
这与我们在OpenGL中绕着Y轴旋转什么的情形是一样的。
Z轴-您从正前方看着一台风扇。
风扇的中心正好朝着您(就像OpenGL中的Z轴)。
风扇的叶片绕着Z轴顺时针或逆时针狂转。
这与我们在OpenGL中绕着Z轴旋转什么的情形是一样的。
当你完全明白以上所说的知识时,你会自己编写代码让我们单薄的小人在场景中移动了。
不过,是不是觉得一点都不兴奋,你会说,为什么我们不构造一个立体的小人呢...
下面我为下一节做些铺垫,我们将会探讨:
①构建一个三维场景,且里面的物体也是立体的,我们的小人也是立体的。
如果我们构造的物体看起来都差不多,我们会考虑使用displayList显示列表
②构建其他的三维场景,并且我们可以在需要的时候切换这些场景。
这里,我们学会构建一些类,用它来储存我们的场景信息
③从文件读入我们的三维场景,当然,文件中保存了我们所需要的所有信息
④一些基本图形的绘制简介,包括圆柱、贝塞尔曲面(这些都仅作介绍)
3.看起来不那么像的汽车
让我们回顾下上一节,我们构建了一个单薄的小人,可它看起来并不让我们满意。
这一节中,我们将介绍:
①构建一个三维场景,且里面的物体也是立体的,我们的小人也是立体的。
如果我们构造的物体看起来都差不
多,我们会考虑使用displayList显示列表
②构建其他的三维场景,并且我们可以在需要的时候切换这些场景。
这里,我们学会构建一些类,用它来储存
我们的场景信息
③从文件读入我们的三维场景,当然,文件中保存了我们所需要的所有信息
④一些基本图形的绘制简介,包括圆柱、贝塞尔曲面(这些都仅作介绍)
首先让我们闭上眼睛想象一下,我们将要构建的是一个真实的立体场景,而不是前两节中所说的看似立体的
场景。
这里,我们将构造很多立体的图形,比如,正方体、长方体、圆柱体或是一些复杂图形。
当然,简单的
画出这些对象并不是我们的目的。
我们将:
①构造一个scene类,它就是我们前两节中构建的糖果盒子。
这次,我们将把许多立体元素加入进来,这就需
要我们再构造一些正方体(长方体)类、圆柱体类,和一些复杂图形类。
完成之后我们将在scene类中用到它们
。
那,让我们思考一下,我们的scene类需要包含些什么东西。
//H文件
classscene:
publicQGLWidget
{
Q_OBJECT
public:
scene(QWidget*parent=0);
~scene();
voiddraw(intid); //调用它,我们将开始paint我们的对象
curbe*myCurbe; /*创建一个curbe类型的指针,我们用它来画我们所有的立方体。
当然curbe类需要
我们去定义*/
intR; //对象旋转地角度
GLuinttexture[3]; //所需的纹理。
因为我不会美工也懒得找图,所以只用了3个简单的纹理图
protected:
voidloadGLTextures();
private:
intsceneID; //我们所绘场景的ID号。
想象我们需要很多不同的场景,并且通过场景ID进行切换
intobjNum; //场景中对象的数量
charsort; //表明我们所画对象的类型,curbe,圆柱或者贝塞尔曲面等
charc; //用来从文件中读入字符的变量
stringmapLine;//文件中的每一行我们存贮在mapLine中
inti;
stringtemp;
voiddrawCurbe();//画立方体时调用
voidpaintMainScene();//创建主场景,就是我们“舞台”的背景
voidcreatObj(); //创建对象,它会去读取文件
voidrecordCurbeData(); //读取到得文件,我们用它来初始化我们的数据
voidinitializeGL();
voidpaintGL();
};
②然后我们在cpp文件中进行我们的文件读取以及绘图等操作
首先,我们绘图的入口在这里
voidscene:
:
draw(intid)
{
sceneID=id;
paintGL();
}
紧接着我们的paintGL函数是这样的:
voidscene:
:
paintGL()
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
paintMainScene();
creatObj(); //then,recorddata
loadGLTextures();
R+=5;
}其中,我们先画我们的场景背景(就是我们前面画的糖果盒子),然后用creatObj()建立其他三维对象。
creatObj完成我们对文件的读写。
文件中按一定格式存储着我们所画对象的信息。
我们在creatObj()和
recordCurbeData()等函数中去完成文件解析。
ps:
最开始我只实现了从文件中读取立方体的数据然后将它们画到我们的场景中。
而且由于能力有限,我只能
将数据存储在txt文件中。
希望对这方面有研究的朋友能告诉我更好的文件存储方法。
存储的数据是这样的:
1 //场景ID
2 //场景中对象数量
0,120,120,120,-120,0,-120,-120,0,0,0,0,0,0,0,-120n
对象类型(0代表curbe),接着三个是长宽高,再接着的是对象底面左上、左下、右下、右上的三维坐标。
最后
以n结束
0,120,120,60,-120,0,0,-120,0,60,-60,0,60,-60,0,0n
上面,我构造了两个立方体,我们可以再构造其他类型的对象,这里不再详细介绍。
③上面的操作我没有用displayList显示列表,因为显示列表用于创建许多一样的对象,而上面的操作我画了
很多大小不一的对象。
再次回顾下我们的目标,一个三维导航系统(虽然说我只能做的很简陋),上面的步骤我
们用来构建静态的东西,然后我们用显示列表画我们的汽车。
当然,这里需要用到我们的贝塞尔曲面,因为我
不知道怎样画汽车,不过我发现贝塞尔曲面和它很像。
关于显示列表,简单的说就是它把我们要画的对象预先画在内存中,以便在实际画的时候能够加快效率。
下面
我们创建一个贝塞尔曲面的显示列表,具体的做法请参考Nehe的中文版教程第28讲。
我这里只列出相关的函数
④initBezier() //用来初始化贝塞尔曲面的16个控制点(4X4)
LoadCarTexture(); //载入纹理
mybezier.dlBPatch=genBezier(mybezier,divs); //创建显示列表
glCallList(mybezier.dlBPatch); //调用显示列表,绘制贝塞尔曲面
其中genBezier()生成贝塞尔曲面的显示列表,它是这个样子的:
GLuintopenGLCar:
:
genBezier(BEZIER_PATCHpatch,intdivs){
int u=0,v;
float py,px,pyold;
GLuint drawlist=glGenLists
(1); //创建显示列表
POINT_3D temp[4];
POINT_3D *last=(POINT_3D*)malloc(sizeof(POINT_3D)*(divs+1)); //根据每一条曲
线的细分数,分配相应的内存
if(patch.dlBPatch!
=NULL) //如果显示列表存在则删除
glDeleteLists(patch.dlBPatch,1);
temp[0]=patch.anchors[0][3]; //获得u方向的四个控制点
temp[1]=patch.anchors[1][3];
temp[2]=patch.anchors[2][3];
temp[3]=patch.anchors[3][3];
for(v=0;v<=divs;v++){ //根据细分数,创建各个分割点额参数
px=((float)v)/((float)divs);
//使用Bernstein函数求的分割点的坐标
last[v]=Bernstein(px,temp);
}
glNewList(drawlist,GL_COMPILE); //创建一个新的显示列表
//glBindTexture(GL_TEXTURE_2D,patch.texture); //邦定纹理
for(u=1;u<=divs;u++){
py =((float)u)/((float)divs); //计算v方向上的细分点的参数
pyold=((float)u-1.0f)/((float)divs); //上一个v方向上的细分点的参数
temp[0]=Bernstein(py,patch.anchors[0]);//计算每个细分点v方向上贝塞尔曲面的控制点
temp[1]=Bernstein(py,patch.anchors[1]);
temp[2]=Bernstein(py,patch.anchors[2]);
temp[3]=Bernstein(py,patch.anchors[3]);
glBegin(GL_TRIANGLE_STRIP); //开始绘制三角形带
for(v=0;v<=divs;v++){
px=((float)v)/((float)divs); //沿着u轴方向顺序绘制
glTexCoord2f(pyold,px); //设置纹理坐标
glVertex3d(last[v].x,last[v].y,last[v].z); //绘制一个顶点
last[v]=Bernstein(px,temp); //创建下一个顶点
glTexCoord2f(py,px); //设置纹理
glVertex3d(last[v].x,last[v].y,last[v].z); //绘制新的顶点
}
glEnd(); //结束三角形带的绘制
}
glEglCallLi
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- QT