`
gongzhq
  • 浏览: 22721 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

第九课:移动图像

阅读更多



 欢迎进入第九课。到现在为止,您应该很好的理解OpenGL了。您已经学会了设置一个OpenGL窗口的每个细节。学会在旋转的物体上贴图并打上光线以及融合(透明)处理。这一课应该算是第一课中级教程。您将学到如下的知识:在三维场景中移动位图,并去除位图上的黑色象素(使用融合)。接着为黑白纹理上色,最后您将学会创建丰富的色彩,并把上过不同色彩的纹理相互融合,得到简单的动画效果。

我们要在上一课的代码上进行改动就可以了。

我们将增加一些变量,稍后我们对这些变量进行解释。
NeHeWidget类

(由nehewidget.h展开。)

const GLuint num = 50;

这个常量num存储的是我们绘制的星星的总数。

typedef struct
{
  int r, g, b;
  GLfloat dist;
  GLfloat angle;
}stars;

这个结构是用来存储星星的数据的,r、g、b存储的是星星的颜色,dist是星星距离中心的距离,angle是星星现在所在的角度。

protected:

  void initializeGL();
  void paintGL();
  void resizeGL( int width, int height );

  void keyPressEvent( QKeyEvent *e );
  void loadGLTextures();
  void timerEvent( QTimerEvent * );

增加了一个timeEvent( QTimerEvent * )函数,这个函数可以实现整个窗口部件的一些定时操作。

protected:

  bool fullscreen;

  GLfloat xRot, yRot, zRot;
  GLfloat zoom;
  GLfloat tilt;
  GLfloat spin;
  GLuint loop;
  GLuint texture[1];

  bool twinkle;

  stars star[num];
};

zoom是星星距离观察者的距离,tilt是星星的倾角,spin是闪烁星星的自转,loop是用来绘制所有50个星星的全局变量,texture[1]是用来存储纹理的,twinkle是用来表示星星是否闪烁,star[num]是用来存储50个星星的数据。

(由nehewidget.cpp展开。)

NeHeWidget::NeHeWidget( QWidget* parent, bool fs )
    : QGLWidget( parent )
{
  xRot = yRot = zRot = 0.0;
  zoom = -15.0;
  tilt = 90.0;
  spin = 0.0;
  loop = 0;

  twinkle = false;

  fullscreen = fs;
  setGeometry( 0, 0, 640, 480 );
  setWindowTitle(tr( "NeHe's Animated Blended Textures Tutorial") );

  if ( fullscreen )
    showFullScreen();

  startTimer( 5 );

startTimer( 5 )就是每5毫秒执行一次timerEvent()函数做定时操作。

}

我们需要在构造函数中给各个变量赋初值。

void NeHeWidget::loadGLTextures()
{
  QImage tex, buf;
  if ( !buf.load( "./ui/Star.bmp" ) )
  {
    qWarning( "Could not read image file, using single-color instead." );
    QImage dummy( 128, 128, QImage::Format_RGB32 );
dummy.fill( QColor(Qt::green).rgb() );
buf = dummy;
  }
  tex = QGLWidget::convertToGLFormat( buf );
 
  glGenTextures( 1, &texture[0] );

  glBindTexture( GL_TEXTURE_2D, texture[0] );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
  glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,
      GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
}

loadGLTextures()函数就是用来载入纹理的。

void NeHeWidget::initializeGL()
{
  loadGLTextures();
  glEnable( GL_TEXTURE_2D );
  glShadeModel( GL_SMOOTH );
  glClearColor( 0.0, 0.0, 0.0, 0.5 );
  glClearDepth( 1.0 );
  glEnable( GL_DEPTH_TEST );
  glDepthFunc( GL_LEQUAL );
  glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
  glBlendFunc( GL_SRC_ALPHA, GL_ONE );
  glEnable( GL_BLEND );

上述设置前面的各章都提到过了,就不再讲了。

  for ( loop = 0; loop < num; loop++ )
  {
    star[loop].angle = 0.0;
    star[loop].dist = ( float(loop)/num ) * 5.0;
    star[loop].r = rand() % 256;
    star[loop].g = rand() % 256;
    star[loop].b = rand() % 256;
  }

设置了每颗星星的起始角度、距离和颜色。您会注意到修改结构的属性有多容易。全部50颗星星都会被循环设置。要改变star[1]的角度我们所要做的只是star[1].angle={某个数值};就这么简单!

第loop颗星星离中心的距离是将loop的值除以星星的总颗数,然后乘上5.0。基本上这样使得后一颗星星比前一颗星星离中心更远一点。这样当loop 为50时(最后一颗星星),loop除以num正好是1.0。之所以要乘以5.0是因为1.0*5.0就是5.0。5.0已经很接近屏幕边缘。我不想星星飞出屏幕,5.0是最好的选择了。当然如果如果您将场景设置的更深入屏幕里面的话,也许可以使用大于5.0的数值,但星星看起来就更小一些(都是透视的缘故)。

您还会注意到每颗星星的颜色都是从0~255之间的一个随机数。也许您会奇怪为何这里的颜色得取值范围不是OpenGL通常的0.0~1.0之间。这里我们使用的颜色设置函数是glColor4ub,而不是以前的glColor4f。ub意味着参数是unsigned byte型的。一个byte的取值范围是0~255。这里使用byte值取随机整数似乎要比取一个浮点的随机数更容易一些。

}

void NeHeWidget::paintGL()
{
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

清楚屏幕及深度缓存。

  glBindTexture( GL_TEXTURE_2D, texture[0] );

选择纹理。

  for ( loop = 0; loop < num; loop++ )
  {

这段程序我们来循环绘制所有的星星。

    glLoadIdentity();

绘制每颗星星之前,重置模型观察矩阵。

    glTranslatef( 0.0, 0.0, zoom );

深入屏幕里面(使用“zoom”的值)。

    glRotatef( tilt, 1.0, 0.0, 0.0 );

倾斜视角(使用“tilt”的值)。

    glRotatef( star[loop].angle, 0.0, 1.0, 0.0 );

接下来的代码,我们来移动星星。星星开始时位于屏幕的中心。我们要做的第一件事是把场景沿Y轴旋转。如果我们旋转90度的话,X轴不再是自左至右的了,他将由里向外穿出屏幕。为了让大家更清楚些,举个例子。假想您站在房子中间。再设想您左侧的墙上写着-x,前面的墙上写着-z,右面墙上就是+x咯,您身后的墙上则是+z。加入整个房子向右转90度,但您没有动,那么前面的墙上将是-x而不再是-z了。所有其他的墙也都跟着移动。-z出现在右侧,+z出现在左侧,+x出现在您背后。神经错乱了吧?通过旋转场景,我们改变了x和z平面的方向。

    glTranslatef( star[loop].dist, 0.0, 0.0 );

这代码沿x轴移动一个正值。通常x轴上的正值代表移向了屏幕的右侧(也就是通常的x轴的正向),但这里由于我们绕y轴旋转了坐标系,x轴的正向可以是任意方向。如果我们转180度的话,屏幕的左右侧就镜像反向了。因此,当我们沿x轴正向移动时,可能向左,向右,向前或向后。

    glRotatef( -star[loop].angle, 0.0, 1.0, 0.0 );
    glRotatef( -tilt, 1.0, 0.0, 0.0 );

星星实际上是一个平面的纹理。现在您在屏幕中心画了个平面的四边形然后贴上纹理,这看起来很不错。一切都如您所想的那样。但是当您当您沿着y轴转上个90 度的话,纹理在屏幕上就只剩右侧和左侧的两条边朝着您。看起来就是一条细线。这不是我们所想要的。我们希望星星永远正面朝着我们,而不管屏幕如何旋转或倾斜。

我们通过在绘制星星之前,抵消对星星所作的任何旋转来实现这个愿望。您可以采用逆序来抵消旋转。当我们倾斜屏幕时,我们实际上以当前角度旋转了星星。通过逆序,我们又以当前角度“反旋转”星星。也就是以当前角度的负值来旋转星星。就是说,如果我们将星星旋转了10度的话,又将其旋转-10度来使星星在那个轴上重新面对屏幕。上面的第一行抵消了沿y轴的旋转。然后,我们还需要抵消掉沿x轴的屏幕倾斜。要做到这一点,我们只需要将屏幕再旋转-tilt倾角。在抵消掉x和y轴的旋转后,星星又完全面对着我们了。

    if ( twinkle )
    {
      glColor4ub( star[(num-loop)-1].r,
         star[(num-loop)-1].g,
         star[(num-loop)-1].b, 255 );
      glBegin( GL_QUADS );
        glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0, 0.0 );
        glTexCoord2f( 1.0, 0.0 ); glVertex3f( 1.0, -1.0, 0.0 );
        glTexCoord2f( 1.0, 1.0 ); glVertex3f( 1.0, 1.0, 0.0 );
        glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, 1.0, 0.0 );
      glEnd();
    }

如果twinkle为真,我们在屏幕上先画一次不旋转的星星:将星星总数(num)减去当前的星星数(loop)再减去1,来提取每颗星星的不同颜色(这么做是因为循环范围从0到num-1)。举例来说,结果为10的时候,我们就使用10号星星的颜色。这样相邻星星的颜色总是不同的。这不是个好法子,但很有效。最后一个值是alpha通道分量。这个值越小,这颗星星就越暗。

由于启用了twinkle,每颗星星最后会被绘制两遍。程序运行起来会慢一些,这要看您的机器性能如何了。但两遍绘制的星星颜色相互融合,会产生很棒的效果。同时由于第一遍的星星没有旋转,启用twinkle后的星星看起来有一种动画效果。(如果您这里看不懂得话,就自己去看程序的运行效果吧。)

值得注意的是给纹理上色是件很容易的事。尽管纹理本身是黑白的,纹理将变成我们在绘制它之前选定的任意颜色。此外,同样值得注意的是我们在这里使用的颜色值是byte型的,而不是通常的浮点数。甚至alpha通道分量也是如此。

    glRotatef( spin, 0.0, 0.0, 1.0 );
    glColor4ub( star[loop].r, star[loop].g, star[loop].b, 255 );
    glBegin( GL_QUADS );
      glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0, 0.0 );
      glTexCoord2f( 1.0, 0.0 ); glVertex3f( 1.0, -1.0, 0.0 );
      glTexCoord2f( 1.0, 1.0 ); glVertex3f( 1.0, 1.0, 0.0 );
      glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, 1.0, 0.0 );
    glEnd();

上面是绘制第二遍的星星。唯一和前面的代码不同的是这一遍的星星肯定会被绘制,并且这次的星星绕着z轴旋转。

    spin += 0.01;
    star[loop].angle += float(loop)/num;
    star[loop].dist -= 0.01;

以上的代码代表星星的运动。我们增加spin的值来旋转所有的星星(公转)。然后,将每颗星星的自转角度增加loop/num。这使离中心更远的星星转的更快。最后减少每颗星星离屏幕中心的距离。这样看起来,星星们好像被不断地吸入屏幕的中心。

    if ( star[loop].dist < 0.0 )
    {
      star[loop].dist += 5.0;
      star[loop].r = rand() % 256;
      star[loop].g = rand() % 256;
      star[loop].b = rand() % 256;
    }
  }

检查星星是否已经碰到了屏幕中心。当星星碰到屏幕中心时,我们为它赋一个新颜色,然后往外移5个单位,这颗星星将踏上它回归屏幕中心的旅程。

}

void NeHeWidget::timerEvent(QTimerEvent*)
{
  updateGL();
}

这里就是定时操作函数timerEvent(),执行的操作就是updateGL(),就是刷新窗口了,其实它也会调用paintGL(),所以就实现了每5毫秒刷新一次的动画效果。

void NeHeWidget::keyPressEvent( QKeyEvent *e )
{
  switch ( e->key() )
  {
  case Qt::Key_T:
    twinkle = !twinkle;
    updateGL();
    break;

按下了T键,就可以切换是否闪烁。

  case Qt::Key_Up:
    tilt -= 0.5;
    updateGL();
    break;
  case Qt::Key_Down:
    tilt += 0.5;
    updateGL();
    break;
  case Qt::Key_PageUp:
    zoom -= 0.2;
    updateGL();
    break;
  case Qt::Key_PageDown:
    zoom += 0.2;
    updateGL();
    break;
  case Qt::Key_F2:
    fullscreen = !fullscreen;
    if ( fullscreen )
    {
      showFullScreen();
    }
    else
    {
      showNormal();
      setGeometry( 0, 0, 640, 480 );
    }
    update();
    break;
  case Qt::Key_Escape:
    close();
  }
}

这一课我尽我所能来解释如何加载一个灰阶位图纹理,(使用融合)去掉它的背景色后,再给它上色,最后让它在三维场景中移动。我已经向您展示了如何创建漂亮的颜色与动画效果。实现原理是在原始位图上再重叠一份位图拷贝。到现在为止,只要您很好的理解了我所教您的一切,您应该已经能够毫无问题的制作您自己的 3D Demo了。所有的基础知识都已包括在内!

  • 大小: 131.4 KB
0
0
分享到:
评论

相关推荐

    Nehe教程第9课移动图像程序及资源

    Nehe教程第9课移动图像,在3D空间中移动物体。

    NeHe_OpenGL_第九课 3D空间中移动图像

    windows+glut+win32开发环境 对应博客文章:http://blog.csdn.net/guoming0000/article/details/10125237 或者:http://52coding.com/nehe-3d-space-picture-moving

    methods_class:“学习神经回路的方法”课程

    Methods_class 研究神经回路的方法 现在该课程结束了。 记录了全部11堂讲座并可供观看。 所有课程材料(包括指定的阅读材料和讲座幻灯片)都在“代码”标签下,或者在移动设备上的... 第9课:病变,药理学和基因操作

    图形图像处理实用教程

    第9章 中文CorelDRAW 9.1 CorelDRAW概述 9.1.1 CorelDRAW的功能与特点 9.1.2 CorelDRAW的运行环境与启动 9.1.3 中文CorelDRAW 10的工作界面 9.1.4 中文CorelDRAW 10的标准工具栏 9.1.5 中文CorelDRAW 10的常用工具箱...

    Visual C++数字图像处理开发入门与编程实践2-8章源代码

    第九章 "MagicHouse框架"目录: 含有MagicHouse的原始框架,该框架是在GrahpShower的基础上完成的。 &lt;br&gt;"MagicHouse"目录: 在MagicHouse原始框架下添加了“点运算”的功能。 GrayOperator.h和...

    Visual C++数字图像处理开发入门与编程实践第10章

    第九章 "MagicHouse框架"目录: 含有MagicHouse的原始框架,该框架是在GrahpShower的基础上完成的。 &lt;br&gt;"MagicHouse"目录: 在MagicHouse原始框架下添加了“点运算”的功能。 GrayOperator.h和...

    《Visual C++数字图像处理开发入门与编程实践》源码

    第9章 图像的点运算 325 9.1 灰度直方图 326 9.1.1 灰度直方图 326 9.1.2 基本原理 328 9.1.3 编程实现 328 9.2 灰度线性变换 338 9.2.1 基本原理 338 9.2.2 编程实现 341 9.3 灰度非线性变换 344 9.3.1 灰度对数...

    【matlab代码】-【图像去噪】二维统计滤波算法.zip

    移动模板,重复步骤1,直到模板不能再继续移动。 在3x3模板中,当指定k=1时为最小值滤波,指定k=5时为中值滤波,指定k=9时为最大值滤波。 此外,该程序还要求实现以下功能 1. 显示原图像、移除像素后的图像和...

    DOTNET移动通信程序设计

    Mobile Internet Toolkits是微软为移动设备的网页程序设计领域开发的工具包。...第9章介绍了一些高级程序,如ADO .NET、Email、图像、XML和XML Web Service的编写方法。第10章介绍网页的安全性。最后是附录。

    3D4U和PSDTO3D立体图像制作教程

    1、焦点:焦点是一幅图像的核心,焦点的设置决不能出错,软件虽然会给出一个焦点层,但是不一定是你图像的主景层,你要重新修正,如一幅婚纱画,画中的人物自然是整幅画的灵魂,也就是焦点,他在第几个图层,就设...

    Adobe Illustrator CS4 官方简体中文帮助文档.rar

    第 9 章: 导入、导出和存储 导入文件 227导入位图图像.231导入 Adobe PDF 文件232使用 ADOBE ILLUSTRATOR CS4 vi目录导入 EPS、DCS 和 AutoCAD 文件 233从 Photoshop 导入图稿 234存储图稿 235导出图稿 241创建...

    游戏编程入门莫里森(电子书+光盘资料)Part 1

    第9章 使用子画面动画移动对象 第10章 管理子画面 第11章 示例游戏:Henway 第4部分 使用声音和音乐 第12章 播放数字声音效果 第13章 播放MIDI音乐 第14章 示例游戏:Battle Office 第5部分 高级动画 第15章...

    .NET移动通信程序设计.rar

    第 9 章高级程序........ 119 9.1 ADO.NET 119 9.1.1 ADO.NET 程序... 120 9.1.2 OleDB 程序... 122 9.1.3 DataSet 程序.. 124 9.2 Email........ 126 9.3 图像......... 127 9.4 XML ........ 133 9.5 XML ...

    MFC数字图像处理(BMP格式读取 保存 DFT FFT 直方图 色调均化 缩放 模糊 锐化 滤镜 形态学处理 曲线 裁剪 灰度图 彩色图 自动阈值)

    在对话框中会看到一个9*9的结构元素方阵,可以通过使用鼠标左键点击来改变结构元素的形状,双击鼠标为还原结构元素。 设定好结构元素后可以选择操作的四种方式,选择后便会得到处理后的图像了,十分方便。 当然,...

    SEO 检查器 - 9步完成SEO审核

    SEO 审核对于提高网站在搜索引擎结果中的性能和可见度至关重要。这种系统评估涉及审查各种...第 6 步:审核移动 SEO 步骤 7. SEO 关键字审核 步骤 8. 使用 Lighthouse 审核页面加载速度 步骤 9. 社交媒体 SEO 优化审核

    图像管理大师(Viewlet) v3.0.0.722.zip

    在图片打开页中, 比如可以导航到下一个、上一个、第一个、最后一个图片以及其他图像处理命令, 大大提高了鼠标利用率. 您还可以在选项对话框里面修改和添加其他你想要的动作.     8. Windows 文件管理器 ...

Global site tag (gtag.js) - Google Analytics