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

第七课:光照和键盘

阅读更多



 这一课我会教您如何使用三种不同的纹理滤波方式。教您如何使用键盘来移动场景中的对象,还会教您在OpenGL场景中应用简单的光照。这一课包含了很多内容,如果您对前面的课程有疑问的话,先回头复习一下。进入后面的代码之前,很好的理解基础知识十分重要。
我们要在第一课的代码上进行改动就可以了。
我们将要增加一个loadGLTextures()函数来处理有关纹理操作的。我们将增加一些变量,稍后我们对这些变量进行解释。
NeHeWidget类
(由nehewidget.h 展开。)
protected:
  void loadGLTextures();
在这个函数中我们会载入指定的图片并生成相应当纹理。
protected:
  bool fullscreen;
  GLfloat xRot, yRot, zRot;
  GLfloat zoom;
  GLfloat xSpeed, ySpeed;
  GLuint texture[3];
  GLuint filter;
  bool light;
};
上面就是添加的三个变量xRot、yRot、zRot来处理立方体在三个方向上的旋转。zoom是场景深入屏幕的距离。xSpeed和ySpeed是立方体在X轴和Y轴上旋转的速度。texture[3]用来存储三个纹理。filter表明的是使用哪个纹理。light是说明现在是否使用光源。
(由nehewidget.cpp 展开。)
GLfloat lightAmbient[4] = { 0.5, 0.5, 0.5, 1.0 };
GLfloat lightDiffuse[4] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat lightPosition[4] = { 0.0, 0.0, 2.0, 1.0 };
这里定义了三个数组,它们描述的是和光源有关的信息。
我们将使用两种不同的光。第一种称为环境光。环境光来自于四面八方。所有场景中的对象都处于环境光的照射中。第二种类型的光源叫做漫射光。漫射光由特定的光源产生,并在您的场景中的对象表面上产生反射。处于漫射光直接照射下的任何对象表面都变得很亮,而几乎未被照射到的区域就显得要暗一些。这样在我们所创建的木板箱的棱边上就会产生的很不错的阴影效果。
创建光源的过程和颜色的创建完全一致。前三个参数分别是RGB三色分量,最后一个是alpha通道参数。
因此,第一行有关lightAmbient的代码使我们得到的是半亮(0.5)的白色环境光。如果没有环境光,未被漫射光照到的地方会变得十分黑暗。
第二行有关lightDiffuse的代码使我们生成最亮的漫射光。所有的参数值都取成最大值1.0。它将照在我们木板箱的前面,看起来挺好。
第三行有关lightPosition的代码使我们保存光源的位置。前三个参数和glTranslate中的一样。依次分别是XYZ轴上的位移。由于我们想要光线直接照射在木箱的正面,所以XY轴上的位移都是0.0。第三个值是Z轴上的位移。为了保证光线总在木箱的前面,所以我们将光源的位置朝着观察者(就是您哪。)挪出屏幕。我们通常将屏幕也就是显示器的屏幕玻璃所处的位置称作Z轴的0.0点。所以Z轴上的位移最后定为2.0。假如您能够看见光源的话,它就浮在您显示器的前方。当然,如果木箱不在显示器的屏幕玻璃后面的话,您也无法看见箱子。最后一个参数取为1.0。这将告诉OpenGL这里指定的坐标就是光源的位置,以后的教程中我会多加解释。
NeHeWidget::NeHeWidget( QWidget* parent, bool fs )
    : QGLWidget( parent )
{
  xRot = yRot = zRot = 0.0;
  zoom = -5.0;
  xSpeed = ySpeed = 0.0;
  filter = 0;
  light = false;
  fullscreen = fs;
  setGeometry( 0, 0, 640, 480 );
  setWindowTitle(tr( "NeHe's Texture, Lighting & Keyboard Tutorial") );
  if ( fullscreen )
    showFullScreen();

   startTimer(5);


}
我们需要在构造函数中给各个变量赋初值。xRot、yRot、zRot是0.0。zoom是-5.0。xSpeed和ySpeed都是0。filter是0。light是false。
void NeHeWidget::loadGLTextures()
{
  QImage tex, buf;
  if ( !buf.load( "./ui/Crate.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( 3, &texture[0] );
这一部分,上一章讲过了。我们这里创建了3个纹理。
  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() );
第六课中我们使用了线性滤波的纹理贴图。这需要机器有相当高的处理能力,但它们看起来很不错。这一课中,我们接着要创建的第一种纹理使用 GL_NEAREST方式。从原理上讲,这种方式没有真正进行滤波。它只占用很小的处理能力,看起来也很差。唯一的好处是这样我们的工程在很快和很慢的机器上都可以正常运行。您会注意到我们在MIN和MAG时都采用了GL_NEAREST,你可以混合使用GL_NEAREST和GL_LINEAR。纹理看起来效果会好些,但我们更关心速度,所以全采用低质量贴图。MIN_FILTER在图像绘制时小于贴图的原始尺寸时采用。MAG_FILTER在图像绘制时大于贴图的原始尺寸时采用。
  glBindTexture( GL_TEXTURE_2D, texture[1] );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,
      GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
这个纹理与第六课的相同,线性滤波。唯一的不同是这次放在了texture[1]中。因为这是第二个纹理。如果放在texture[0]中的话,它将覆盖前面创建的GL_NEAREST纹理。
  glBindTexture( GL_TEXTURE_2D, texture[2] );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
这里是创建纹理的新方法。Mipmapping!您可能会注意到当图像在屏幕上变得很小的时候,很多细节将会丢失。刚才还很不错的图案变得很难看。当您告诉OpenGL创建一个 mipmapped的纹理后,OpenGL将尝试创建不同尺寸的高质量纹理。当您向屏幕绘制一个mipmapped纹理的时候,OpenGL将选择它已经创建的外观最佳的纹理(带有更多细节)来绘制,而不仅仅是缩放原先的图像(这将导致细节丢失)。
我曾经说过有办法可以绕过OpenGL对纹理宽度和高度所加的限制——64、128、256,等等。办法就是gluBuild2DMipmaps。据我的发现,您可以使用任意的位图来创建纹理。OpenGL将自动将它缩放到正常的大小。
因为是第三个纹理,我们将它存到texture[2]。这样本课中的三个纹理全都创建好了。 gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, tex.width(), tex.height(), GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
这一行生成 mipmapped 纹理。我们使用三种颜色(红,绿,蓝)来生成一个2D纹理。tex.width()是位图宽度,tex.height()是位图高度,extureImage[0]->sizeY 是位图高度,GL_RGBA意味着我们依次使用RGBA色彩。GL_UNSIGNED_BYTE意味着纹理数据的单位是字节。tex.bits()指向我们创建纹理所用的位图。
}
loadGLTextures()函数就是用来载入纹理的。
void NeHeWidget::paintGL()
{
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  glLoadIdentity();
  glTranslatef(  0.0,  0.0, zoom );
  glRotatef( xRot,  1.0,  0.0,  0.0 );
  glRotatef( yRot,  0.0,  1.0,  0.0 );
  glBindTexture( GL_TEXTURE_2D, texture[filter] );
根据filter变量来决定使用哪个纹理。
  glBegin( GL_QUADS );
    glNormal3f( 0.0, 0.0, 1.0 );
    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 );
    glNormal3f( 0.0, 0.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 );
    glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0, -1.0 );
    glNormal3f( 0.0, 1.0, 0.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 );
    glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0,  1.0,  1.0 );
    glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0, -1.0 );
    glNormal3f( 0.0, -1.0, 0.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 );
    glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0,  1.0 );
    glNormal3f( 1.0, 0.0, 0.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 );
    glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0,  1.0 );
    glNormal3f( -1.0, 0.0, 0.0 );
    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();
这里绘制正方体的方法,上一章已经讲解过了。
  xRot += xSpeed;
  yRot += ySpeed;
将xRot和yRot的旋转值分别增加xSpeed和ySpeed个单位。xSpeed和ySpeed的值越大,立方体转得就越快。
}
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 );
  glLightfv( GL_LIGHT1, GL_AMBIENT, lightAmbient );
  glLightfv( GL_LIGHT1, GL_DIFFUSE, lightDiffuse );
  glLightfv( GL_LIGHT1, GL_POSITION, lightPosition );
  glEnable( GL_LIGHT1 );
这里开始设置光源。第一行设置环境光的发光量,光源GL_LIGHT1开始发光。这一课的开始处我们我们将环境光的发光量存放在lightAmbient数组中。现在我们就使用此数组(半亮度环境光)。
接下来我们设置漫射光的发光量。它存放在lightDiffuse数组中(全亮度白光)。
然后设置光源的位置。位置存放在lightPosition 数组中(正好位于木箱前面的中心,X-0.0,Y-0.0,Z方向移向观察者2个单位,位于屏幕外面)。
最后,我们启用一号光源。我们还没有启用GL_LIGHTING,所以您看不见任何光线。记住:只对光源进行设置、定位、甚至启用,光源都不会工作。除非我们启用GL_LIGHTING。
}
void NeHeWidget::keyPressEvent( QKeyEvent *e )
{
  switch ( e->key() )
  {
  case Qt::Key_L:
    light = !light;
    if ( !light )
    {
      glDisable( GL_LIGHTING );
    }
    else
    {
      glEnable( GL_LIGHTING );
    }
    updateGL();
    break;
按下了L键,就可以切换是否打开光源。
  case Qt::Key_F:
    filter += 1;;
    if ( filter > 2 )
    {
      filter = 0;
    }
    updateGL();
    break;
按下了F键,就可以转换一下所使用的纹理(就是变换了纹理滤波方式的纹理)。
  case Qt::Key_PageUp:
    zoom -= 0.2;
    updateGL();
    break;
按下了PageUp键,将木箱移向屏幕内部。
  case Qt::Key_PageDown:
    zoom += 0.2;
    updateGL();
    break;
按下了PageDown键,将木箱移向屏幕外部。
  case Qt::Key_Up:
    xSpeed -= 0.01;
    updateGL();
    break;
按下了Up方向键,减少xSpeed。
  case Qt::Key_Down:
    xSpeed += 0.01;
    updateGL();
    break;
按下了Dowm方向键,增加xSpeed。
  case Qt::Key_Right:
    ySpeed += 0.01;
    updateGL();
    break;
按下了Right方向键,增加ySpeed。
  case Qt::Key_Left:
    ySpeed -= 0.01;
    updateGL();
    break;
按下了Left方向键,减少ySpeed。
  case Qt::Key_F2:
    fullscreen = !fullscreen;
    if ( fullscreen )
    {
      showFullScreen();
    }
    else
    {
      showNormal();
      setGeometry( 0, 0, 640, 480 );
    }
    update();
    break;
  case Qt::Key_Escape:
    close();
  }
}
这一课完了之后,您应该学会创建和使用这三种不同的纹理映射过滤方式。并使用键盘和场景中的对象交互。最后,您应该学会在场景中应用简单的光源,使得场景看起来更逼真

  • 大小: 170.4 KB
1
0
分享到:
评论

相关推荐

    Nehe第7课光照和键盘控制

    Nehe第7课光照和键盘控制.教您如何使用三种不同的纹理滤波方式。教您如何使用键盘来移动场景中的对象,还会教您在OpenGL场景中应用简单的光照。

    OpenGL第七和第八讲关于键盘和光照的程序实现

    在VS2005上实现了NEHE OpenGL教程的第七课和第八课。主要实现的是键盘和光照。有需要的话可以下载看看。

    OPenGL编程书籍

    这一课是基于第7课的代码的,你将学会三种不同的雾的计算方法,以及怎样设置雾的颜色和雾的范围。 17.2D 图像文字 在这一课中,你将学会如何使用四边形纹理贴图把文字显示在屏幕上。你将学会如何把256个不同的文字...

    Nehe的OpenGL教程电子书

    这一课是基于第7课的代码的,你将学会三种不同的雾的计算方法,以及怎样设置雾的颜色和雾的范围。 17.2D 图像文字 在这一课中,你将学会如何使用四边形纹理贴图把文字显示在屏幕上。你将学会如何把256个不同的...

    智能照明控制模块操作说明.pdf

    七、光控设置 ( 光照设置 ) 光照度: 18 对象: 01 路 状态: 通:10 断: 20 -5- 详图 九、外形尺寸: 型号规格 4路智能照明时控模块 8路智能照明时控模块 12路智能照明时控模块 L W W1 W2 H 90 162 216 104 104 104...

    directX实验四报告.doc

    directX实验四报告全文共13页,当前为第7页。 directX实验四报告全文共13页,当前为第8页。 directX实验四报告全文共13页,当前为第9页。 directX实验四报告全文共13页,当前为第10页。 directX实验四报告全文共13...

    精通DirectX.3D图形与动画程序设计.pdf

    第7章 使用文件模型 7.1 三维模型基础 7.2 模型文件格式转换 7.2.1 ds max制作的模型转换为.x文件模型 7.2.2 maya制作的模型转换为.x文件模型 7.3 在direct3d程序中载入模型 7.3.1 网格模型接口id3dxmesh 7.3.2 载入...

    《精通direct3d图形及动画程序设计》附书源代码

    第7章 使用文件模型 1、Mesh 演示.X文件模型的使用,包括从.X文件中加载模型生成网格模型对象,渲染网格模型。 2、StateControlUseMatrix 演示使用矩阵旋转网格模型。程序运行时按下“D”和“A”键可以使飞机模型绕...

    OpenSceneGraph三维渲染引擎编程指南.pdf

    第7章 场景图形的工作机制 213 7.1 内存管理 214 7.1.1 Referenced类 214 7.1.2 ref_ptr模板类 214 7.1.3 智能指针 215 7.2 访问器机制 215 7.2.1 访问器设计模式 215 7.2.2 osg::NodeVisitor类 216 ...

    OpenSceneGraph三维渲染引擎编程指南

    第7章 场景图形的工作机制 213 7.1 内存管理 214 7.1.1 Referenced类 214 7.1.2 ref_ptr模板类 214 7.1.3 智能指针 215 7.2 访问器机制 215 7.2.1 访问器设计模式 215 7.2.2 osg::NodeVisitor类 216 7.2.3 ...

    WebGL编程指南压缩包

    第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察...

    WebGL编程指南

    第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察...

    webgl编程指南及源码1/2

    第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察...

    webgl编程指南及源码2/2

    第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察...

    计算机图形学基础-pdf.zip

    第7章三维变换及三维观察 7.1三维变换的基本概念 7.1.1几何变换 7.1.2三维齐次坐标变换矩阵 7.1.3平面几何投影 7.2三维几何变换 7.2.1三维基本几何变换 7.2.2三维复合变换 7.3三维投影变换 7.3.1...

    游戏编程指南 doc

    第七章 我没有想好名字 99 7.1 读取键盘数据 99 7.2 读取鼠标数据 100 7.3 恢复和关闭DirectInput 101 7.3.1 恢复DirectInput设备 101 7.3.2 关闭DirectInput 101 7.4 初始化和关闭DirectX Audio 101 7.4.1 初始...

    基于OpenGL的Android+3D游戏开发技术详解与典型案例(源码)

     第7章 海阔凭鱼跃,天高任鸟飞—3D基本形状的构建在本章中介绍了圆柱体、圆锥体、圆环、抛物面、双曲面和螺旋面在OpenGL ES中的渲染方法。这些基本形状在3D世界中应用广泛,在构造一些复杂物体时,经常会运用这些...

    游戏编程指南

    第七章 我没有想好名字... 102 7.1 读取键盘数据... 102 7.2 读取鼠标数据... 103 7.3 恢复和关闭DirectInput 104 7.3.1 恢复DirectInput设备... 104 7.3.2 关闭DirectInput 104 7.4 初始化和关闭DirectX Audio. 104...

Global site tag (gtag.js) - Google Analytics