转自【翻译】NeHe OpenGL 教程

前言

声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。

NeHe OpenGL第十一课:飘动的旗帜

飘动的旗帜:

这一课从第六课的代码开始,创建一个飘动的旗帜。我相信在这课结束的时候,你可以掌握纹理映射和混合操作。

大家好!对那些想知道我在这里作了些什么的朋友,您可以先按文章的末尾所列出的链接,下载我那毫无意义的演示(Demo)看看先!我是bosco,我将尽我所能教您来实现一个以正弦波方式运动的图象。这一课基于NeHe的教程第六课,当然您至少也应该学会了一至六课的知识。您需要下载源码压缩包,并将压缩包内带的data目录连其下的位图一起释放至您的代码目录下。或者使用您自己的位图,当然它的尺寸必须适合OpenGL纹理的要求。

在我们开始之前,先打开Visual C++(译者:我可是用的C++ Builder…)并在其他的#inlude之后,添加如下的代码。这将引入我们在程序中将要用到的复杂(译者:复杂吗?)数学函数sine和cosine。

 

#include <math.h>      // 引入数学函数库中的Sin

我们将使用points数组来存放网格各顶点独立的x,y,z坐标。这里网格由45×45点形成,换句话说也就是由44格×44格的小方格子依次组成了。wiggle_count用来指定纹理波浪的运动速度。每3帧一次看起来很不错,变量hold将存放一个用来对旗形波浪进行光滑的浮点数。这几行添加在程序头部,位于最后一行#include之后、GLuint
texture[1]之前的位置。

float points[ 45 ][ 45 ][3];     // Points网格顶点数组

int wiggle_count = 0;      // 指定旗形波浪的运动速度

GLfloat hold;       // 临时变量

然后下移至LoadGLTextures()子过程。本课中使用的纹理文件名是Tim.bmp。找到LoadBMP("Data/NeHe.bmp")这一句,并用LoadBMP ("Data/Tim.bmp")替换它。

if (TextureImage[0]=LoadBMP("Data/Tim.bmp"))  // 载入位图

接着在InitGL()函数的尾部return TRUE之前,添加如下的代码。

glPolygonMode( GL_BACK, GL_FILL );   // 后表面完全填充

 glPolygonMode( GL_FRONT, GL_LINE );   // 前表面使用线条绘制

上面的代码指定使用完全填充模式来填充多边形区域的背面(译者:或者叫做后表面吧)。相反,多边形的正面(译者:前表面)则使用轮廓线填充了。这些方式完全取决于您的个人喜好。并且与多边形的方位或者顶点的方向有关。详情请参考红宝书(Red
Book)。这里我顺便推销一本推动我学习OpenGL的好书 — Addison-Wesley出版的《Programmer's Guide to
OpenGL》。个人以为这是学习OpenGL的无价之宝。

接着上面的代码并在return TRUE这一句之前,添加如下的几行。

// 沿X平面循环

 for(int x=0; x<45; x++)

 {

  // 沿Y平面循环

  for(int y=0; y<45; y++)

  {

   // 向表面添加波浪效果

   points[x][y][0]=float((x/5.0f)-4.5f);

   points[x][y][1]=float((y/5.0f)-4.5f);

   points[x][y][2]=float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f));

  }

 }

这里感谢Graham Gibbons关于使用整数循环变量消除波浪间的脉冲锯齿的建议。

上面的两个循环初始化网格上的点。使用整数循环可以消除由于浮点运算取整造成的脉冲锯齿的出现。我们将x和y变量都除以5,再减去4.5。这样使得我们的波浪可以“居中”(译者:这样计算所得结果将落在区间[-4.5,4.5]之间)。

  

点[x][y][2]最后的值就是一个sine函数计算的结果。Sin()函数需要一个弧度参变量。将float_x乘以40.0f,得到角度值。然后除以360.0f再乘以PI,乘以2,就转换为弧度了。

接着我将彻底重写DrawGLScene函数。

int DrawGLScene(GLvoid)      // 绘制我们的GL场景

{

 int x, y;      // 循环变量

 float float_x, float_y, float_xb, float_yb;  // 用来将旗形的波浪分割成很小的四边形

我们使用不同的变量来控制循环。下面的代码中大多数变量除了用来控制循环和存储临时变量之外并没有什么别的用处。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓冲

 glLoadIdentity();     // 重置当前的模型观察矩阵

glTranslatef(0.0f,0.0f,-12.0f);    // 移入屏幕12个单位

glRotatef(xrot,1.0f,0.0f,0.0f);    // 绕 X 轴旋转

 glRotatef(yrot,0.0f,1.0f,0.0f);    // 绕 Y 轴旋转

 glRotatef(zrot,0.0f,0.0f,1.0f);    // 绕 Z 轴旋转

glBindTexture(GL_TEXTURE_2D, texture[0]);  // 选择纹理

正如您所见,上面的代码和第六课的很类似,唯一的区别就是我将场景挪的离镜头更远了一些。

glBegin(GL_QUADS);     // 四边形绘制开始

 for( x = 0; x < 44; x++ )    // 沿 X 平面 0-44 循环(45点)

 {

  for( y = 0; y < 44; y++ )   // 沿 Y 平面 0-44 循环(45点)

  {

接着开始使用循环进行多边形绘制。这里使用整型可以避免我以前所用的int()强制类型转换。

float_x = float(x)/44.0f;  // 生成X浮点值

   float_y = float(y)/44.0f;  // 生成Y浮点值

   float_xb = float(x+1)/44.0f;  // X浮点值+0.0227f

   float_yb = float(y+1)/44.0f;  // Y浮点值+0.0227f

上面我们使用4个变量来存放纹理坐标。每个多边形(网格之间的四边形)分别映射了纹理的1/44×1/44部分。循环首先确定左下顶点的值,然后我们据此得到其他三点的值。

glTexCoord2f( float_x, float_y); // 第一个纹理坐标 (左下角)

   glVertex3f( points[x][y][0], points[x][y][1], points[x][y][2] );

glTexCoord2f( float_x, float_yb ); // 第二个纹理坐标 (左上角)

   glVertex3f( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2] );

glTexCoord2f( float_xb, float_yb ); // 第三个纹理坐标 (右上角)

   glVertex3f( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2] );

glTexCoord2f( float_xb, float_y ); // 第四个纹理坐标 (右下角)

   glVertex3f( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2] );

  }

 }

 glEnd();      // 四边形绘制结束

上面几行使用glTexCoord2f()和glVertex3f()载入数据。提醒一点:四边形是逆时针绘制的。这就是说,您开始所见到的表面是背面。后表面完全填充了,前表面由线条组成。

  

如果您按顺时针顺序绘制的话,您初始时见到的可能是前表面。也就是说您将看到网格型的纹理效果而不是完全填充的。

if( wiggle_count == 2 )     // 用来降低波浪速度(每隔2帧一次)

 {

每绘制两次场景,循环一次sine值,以产生运动效果。

for( y = 0; y < 45; y++ )   // 沿Y平面循环

  {

   hold=points[0][y][2];   // 存储当前左侧波浪值

   for( x = 0; x < 44; x++)  // 沿X平面循环

   {

    // 当前波浪值等于其右侧的波浪值

    points[x][y][2] = points[x+1][y][2];

   }

   points[44][y][2]=hold;   // 刚才的值成为最左侧的波浪值

  }

  wiggle_count = 0;    // 计数器清零

 }

 wiggle_count++;      // 计数器加一

上面所作的事情是先存储每一行的第一个值,然后将波浪左移一下,是图象产生波浪。存储的数值挪到末端以产生一个永无尽头的波浪纹理效果。然后重置计数器wiggle_count以保持动画的进行。

  

上面的代码由NeHe(2000年2月)修改过,以消除波浪间出现的细小锯齿。

xrot+=0.3f;      // X 轴旋转

 yrot+=0.2f;      // Y 轴旋转

 zrot+=0.4f;      // Z 轴旋转

return TRUE;      // 返回

}

原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=11

NeHe OpenGL教程 第十一课:飘动的旗帜的更多相关文章

  1. NeHe OpenGL教程 第二十一课:线的游戏

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. NeHe OpenGL教程 第二十七课:影子

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. NeHe OpenGL教程 第二十三课:球面映射

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. NeHe OpenGL教程 第二十课:蒙板

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. NeHe OpenGL教程 第十七课:2D图像文字

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. NeHe OpenGL教程 第十三课:图像字体

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. NeHe OpenGL教程 第十课:3D世界

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. NeHe OpenGL教程 第八课:混合

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  9. NeHe OpenGL教程 第七课:光照和键盘

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

随机推荐

  1. CSS3教程:pointer-events属性值详解 阻止穿透点击

    转:http://www.poluoluo.com/jzxy/201109/142876.html 在某个项目中,很多元素需要定位在一个地图层上面,这里就要用到很多绝对定位或者相对定位的元素,但是这样 ...

  2. PPT图片快速编辑技巧

    修改*.ppt或*.pptx的后缀名为zip 例如:demo.pptx修改为demo.zip 修改为, 使用压缩软件打开此压缩包, 一般图片资源都会存放在ppt/media下 找到你所要修改的图片,然 ...

  3. Linux驱动设计——内存与IO访问

    名词解释 内存空间与IO空间 内存空间是计算机系统里面非系统内存区域的地址空间,现在的通用X86体系提供32位地址,寻址4G字节的内存空间,但一般的计算机只安装256M字节或者更少的内存,剩下的高位内 ...

  4. 黑马程序员——JAVA基础之多线程的安全问题

    ------- android培训.java培训.期待与您交流! ---------- 导致多线程出现问题的一个特殊的状态:就绪.具备了执行资格,但是还没有获取资源. 导致安全问题的出现的原因: 1. ...

  5. C++命名空间<转>

    熟练掌握C/C++语言,熟悉Windows开发平台,能熟练运用MFC自主编开发出一些应用程序: 熟练掌握SQL语句,对数据库有很好的认识,能熟练使用SQL Server2000软件: 熟练掌握JAVA ...

  6. Python 基本语法1

    Python 基础语法(一) Python的特点 1. 简单 Python是一种代表简单思想的语言. 2. 易学 Python有极其简单的语法. 3. 免费.开源 Python是FLOSS(自由/开放 ...

  7. 前端面试库_JS部分_02

    今天小编做了一个伟大的决定-----把我的初恋追回来.她在我心中一直是美好的,以前也人渣过,脑袋发热过,到了现在才真的是想有一个人陪伴着我,我与她约定晚些相见,我相信这个时间不会很久,虽然三年没有联系 ...

  8. 安装pgadmin3

    在centos/redhat/系统是x86_64 1.下载适合自己系统的合适的版本http://dl.fedoraproject.org/pub/epel/6/x86_64/ ( yum instal ...

  9. 【转】Python numpy库的nonzero函数用法

    当使用布尔数组直接作为下标对象或者元组下标对象中有布尔数组时,都相当于用nonzero()将布尔数组转换成一组整数数组,然后使用整数数组进行下标运算. nonzeros(a) 返回数组a中值不为零的元 ...

  10. ABBYY FineReader 12扫描界面介绍

    ABBYY FineReader 12OCR图文识别软件自身拥有着自己的扫描界面,一般在默认情况下,ABBYY FineReader 使用其自身的扫描界面.本文就解析了ABBYY FineReader ...