学习Nehe Lesson 10

一、Nehe的内容

我们先来看Nehe教程中的内容。在这一课,个人认为最重要且难理解的内容主要有三个,分别是对面的设置处理、读入并处理文件及数据、设置世界的移动旋转。

1、面的构成

从代码的实现来说,这个问题并不难,重要的是这个想法。从前面的几课中我们可以看出,简单一些的立体图形都可以通过一个个面来构成,因此在我们画立方体的时候是通过面的拼接,同样的,在构建一个3D世界时,我们也可以通过一个个小的面来拼出来。这样就产生了一个想法,我们先构建一个顶点结构,一个顶点有五个数据,X轴,Y轴,Z轴和两个纹理坐标,再构建一个三角形结构,一个三角形有3个顶点,最后为我们的世界构建一个区段结构,“每个3D世界基本上可以看作是sector(区段)的集合。一个sector(区段)可以是一个房间、一个立方体、或者任意一个闭合的区间。”这是Nehe教程中的定义。区段中包含了基本图形即三角形的个数以及指向三角形数组的指针。这样一个3D世界的层次就构建好了。如下。

typedef struct tagVERTEX                        // 创建Vertex顶点结构
{
    float x, y, z;                          // 3D 坐标
    float u, v;                         // 纹理坐标
} VERTEX;                               // 命名为VERTEX

typedef struct tagTRIANGLE                      // 创建Triangle三角形结构
{
    VERTEX vertex[3];                       // VERTEX矢量数组,大小为3
} TRIANGLE;                             // 命名为 TRIANGLE

typedef struct tagSECTOR                        // 创建Sector区段结构
{
    int numtriangles;                       // Sector中的三角形个数
    TRIANGLE* triangle;                     // 指向三角数组的指针
} SECTOR;                               // 命名为SECTOR

但一个3D世界很复杂,即便是简单的3D世界,也要用到十几个甚至更多的面,这意味着我们要打印十几次相同的代码,而它们仅仅是坐标参数略有不同。如果可以把坐标都存到某个数组里,然后用循环逐次读入,这样会轻松一些。但这时就产生了新的问题,一个点的坐标有五个数据,画一个三角形要用3个点,一个正方形要用4个点,而一个简单的3D世界,比如Nehe的这个,要用36个三角形(即108个点),换成正方形则是18个(即72个点),我们可以看得出来,这个数据太大了。因此,我们考虑把数据存到一个文件中,要用的时候逐一读取。这就是接下来的文件内容。

2、文件读取和处理

在这里,我们把数据存在txt文档中。第一行是文档中三角形个数,或者说程序运行时读取的三角形个数,如果你写了一百个三角形的数据,但只用到前30个,就可以把这个数字写成30。接下来就是一组一组的三角形数据了。
首先,要从文件中读取完整的一行数据存到一个数组里,在这里用readstr这个函数,如下。

void readstr(FILE *f,char *string)
{
    do
    {
        fgets(string, 255, f);
    } while ((string[0] == '/') || (string[0] == '\n'));
    return;
}

这个函数有两个参数,第一个是读取的文件,第二个是存数据的数组。fgets函数是从文件中读取完整的一行并存入数组,关于这个函数的具体内容参见百度百科。可以看出,在while循环中,把读到的内容都存到string中,如果遇到注释的标志’/’或换行的标志’\n’,就重新开始循环,即换行时就重新开始存。这样很巧妙地避开了文档中的注释语句和空行。
接下来,就是如何打开并读取文档从而设置世界的问题了。在SetupWorld函数中,用这样一个函数打开了文档,filein = fopen(“world.txt”, “rt”);然后用前面的readstr函数把文档的数据以行为单位提取到数组oneline中,把第一行中的三角形个数存到变量中,再用for循环将剩下的数据存到顶点数组中。for循环有两层,外层是个数循环,即从第一个三角形开始一直到最后一个,内层是顶点循环,即从第一个顶点到第三个定点,这样,每一个定点就以如下的方式存到了前面提到的结构体中。

sector1.triangle[loop].vertex[vert].x = x;
sector1.triangle[loop].vertex[vert].y = y;
sector1.triangle[loop].vertex[vert].z = z;
sector1.triangle[loop].vertex[vert].u = u;
sector1.triangle[loop].vertex[vert].v = v;

最后,读取完数据我们关上这个txt文档, fclose(filein);
接下来要做的就是用这些数据绘制一个个面了。绘制没有什么难度,重要的是旋转。

3、世界移动旋转

在这一课,世界旋转和前进后退和前面变得不一样了,因为3D世界要有一个轻微的摇摆效果,这样是模拟人走在路上时头部的轻微摆动。同时,在这里加入了世界的旋转,即以视点为中心旋转整个世界。我们采用的方法是:根据用户的指令旋转并变换镜头位置;围绕原点,以与镜头相反的旋转方向来旋转世界。(让人产生镜头旋转的错觉);以与镜头平移方式相反的方式来平移世界(让人产生镜头移动的错觉)。

//前进
xpos -= (float)sin(heading*piover180) * 0.05f;
zpos -= (float)cos(heading*piover180) * 0.05f;
if (walkbiasangle >= 359.0f)
{
    walkbiasangle = 0.0f;
}
else
{
    walkbiasangle+= 10;
}
walkbias = (float)sin(walkbiasangle * piover180)/20.0f;
//后退
xpos += (float)sin(heading*piover180) * 0.05f;
zpos += (float)cos(heading*piover180) * 0.05f;
if (walkbiasangle <= 1.0f)
{
    walkbiasangle = 359.0f;
}
else
{
    walkbiasangle-= 10;
}
walkbias = (float)sin(walkbiasangle * piover180)/20.0f;
//左转
heading += 1.0f;    
yrot = heading;
//右转
heading -= 1.0f;
yrot = heading;

在这一块,Nehe的教程讲的很清楚。
值得注意的一点是,我们在处理图像坐标和坐标之间的关系以及一些旋转等问题时,sin和cos这两个函数往往十分有用,比如说让物体绕某点圆周运动,如果用圆的方程那个开根号的方式来写y关于x的变化,会发现圆周运动速度变化很明显,而如果用sin和cos把x和y写成关于一个角度变量的函数,就可以得到匀速圆周运动,并且避免了对x和y正负的判断等问题。

二、我的增加与修改

Nehe的内容是一个简单3D世界的基础,在这个基础上我做了一些很小的改进,也算是对这一课的复习。

1、和第九课的结合

第九课移动图像是一个旋转的星星,把这两课的内容结合在一起,主要就是把InitGL和DrawGLScene这两个函数的内容结合起来,然后在主函数的按键控制部分加入需要的内容。要注意的是,第九课的混色要关闭深度测试,而第十课的混色要打开深度测试,而且第九课的混色是一直开着,而第十课的混色是按B键后打开。关于这个的设定可以看 http://blog.youkuaiyun.com/u014420201/article/details/44064737 。其他的就没什么了。

2、三角形到正方形的转变

大多数时候,我们会用三角形来构成3D世界,这主要是因为由三个给定顶点构成的三角形一定共面,而四个顶点得到的四边形却不一定共面,不共面的四边形会导致纹理渲染出错。但在简单的3D世界里,或者说很明显都是平面的世界里,我们可以构建四边形结构体,把后面涉及三角形的地方都改成四边形,同时把坐标从三角形改成四边形。这个改动并不是很难,要注意的主要就是纹理坐标的顺序,以及代码中改动的地方不要有遗漏。我把要改的列出来,如下。
a.创建结构时,要把顶点数从3改成4,同时下面的区段结构中的变量名为了方便也最好改了;
b.SetupWorld()函数中的for循环,顶点循环的那层也要从3改成4;
c.DrawGLScene()函数中的for循环,同样要加一个顶点的设置,并且要注意改了的变量名。

3、自己设置坐标

在熟练地实现了教程中的3D世界后,我们会想要自己设计一个世界,虽然复杂的世界比如高山、城市、迷宫等等都不可能这么容易地就设计出来,但设计一个简单的只是有几面围墙几块地板的世界还是可以的。在这里要注意的是坐标的顺序,最好都按照顺时针或都逆时针。只要把World.txt文档中的坐标数据改了就够了,整个程序完全不用修改。

4、键盘控制的增加

最后,我增加了一些键盘控制,首先是前进后退用W和S控制,左右旋转视角用A和D来控制,向上向下看用M和N控制,左右移动和上下移动用四个方向键来控制,这样就可以实现在整个3D世界的漫游。
在这里还可以做进一步的改进,就是加入鼠标控制,鼠标直接控制视角的旋转移动等,这个问题可以以后探索。

最后,这四个改动后的代码在: http://download.youkuaiyun.com/detail/u014420201/8540581

NeHe OpenGL教程(中英文版附带VC++源码)中英文系列 Lesson 01-lesson 02 创建一个OpenGL窗口: 如何创建三角形和四边形 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53679 Lesson 03-lesson 04 添加颜色 旋转 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53682 Lesson 05-lesson 06 3D空间 纹理映射 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53706 Lesson 07-lesson 08 光照和键盘控制 混合 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53709 Lesson 09-lesson 10 3D空间中移动图像 加载3D世界,并在其中漫游 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53822 Lesson 11-lesson 12 飘动的旗帜 显示列表 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53823 Lesson 13-lesson 14 图像字体 图形字体 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53880 Lesson 15-lesson 16 图形字体的纹理映射 雾 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=53881 Lesson 17-lesson 18 2D 图像文字 二次几何体 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54042 Lesson 19-lesson 20 粒子系统 蒙板 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54072 Lesson 21-lesson 22 线,反走样,计时,正投影和简单的声音 凹凸映射,多重纹理扩展 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54074 Lesson 23-lesson 24 球面映射 扩展,剪裁和TGA图像文件的加载 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54176 Lesson 25-lesson 26 变形和从文件中加载3D物体 剪裁平面,蒙板缓存和反射 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54177 Lesson 27-lesson 28 影子 贝塞尔曲面 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54180 Lesson 29-lesson 30 Blitter 函数 碰撞检测 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54186 Lesson 31-lesson 32 模型加载 拾取, Alpha混合, Alpha测试, 排序 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54209 Lesson 33-lesson 34 加载压缩和未压缩的TGA文件 从高度图生成地形 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54271 Lesson 35-lesson 36 在OpenGL中播放AVI 放射模糊和渲染到纹理 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54289 Lesson 37-lesson 38 卡通映射 从资源文件中载入图像 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54290 Lesson 39-lesson 40 物理模拟简介 绳子的模拟 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54706 Lesson 41-lesson 42 体积雾气 多重视口 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54707 Lesson 43-lesson 44 在OpenGL中使用FreeType库 3D 光晕 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54710 Lesson 45-lesson 46 顶点缓存 全屏反走样 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54729 Lesson 47-lesson 48 CG 顶点脚本 轨迹球实现的鼠标旋转 http://ieee.org.cn/dispbbs.asp?boardID=61&ID=54730
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值