QT5 OpenGL (六, 键盘事件, 开关灯,放大缩小综合运用)


概要

多篇讲QT5 opengl的文章,从简单到复杂,几乎每篇都在原来的基友上有所增加新的内容, 感觉越到后面,越容易被opengl强大的功能所震撼, 而这篇文章主要是把前面所讲的一些内容进行综合, 然后再加入新的一些内容的运用。 首先, 加入键盘事件, 这个是学QT的人基本上都会的。 开关灯是这次一些新的内容, 放大缩小,是原来的内容,只是原来没有扩展开来。


实例效果图

为什么每次要先上效果图呢, 因为只有看到不错的效果图后,读者才有更大的兴趣读下去。


立体图放大图

这里写图片描述


立体图缩小图

这里写图片描述


不加矢量开灯图

这里写图片描述


不加矢量关灯图

这里写图片描述


加矢量关灯图1

这里写图片描述


加矢量关灯图2

这里写图片描述


部分代码展示

.h文件

#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H
#include <QtOpenGL>

class OpenglWidget : public QGLWidget
{
public:
    OpenglWidget(QWidget* parent = 0);

protected:
  void initConnection();
  void initializeGL();
  void initWidget();
  void paintGL();
  void resizeGL(int width, int height);

  void loadGLTextures();

  void keyPressEvent( QKeyEvent *e );

private slots:



private:
  GLfloat m_rotateTriangle;
  GLfloat m_rotateRectangle;

  GLfloat m_x;
  GLfloat m_y;
  GLfloat m_z;

  GLuint textur[3];

  GLfloat m_zoom;

  GLfloat m_xSpeed;
  GLfloat m_ySpeed;
  GLfloat m_zSpeed;

  bool m_openLight;

  GLuint m_choiceTexture;

};

#endif // OPENGLWIDGET_H

.cpp文件

#include "openglwidget.h"


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 };

OpenglWidget::OpenglWidget(QWidget* parent)
    :QGLWidget(parent),
      m_rotateTriangle(0),
      m_rotateRectangle(0),
      m_x(0),
      m_y(0),
      m_z(0),
      m_zoom(-6),
      m_xSpeed(10),
      m_ySpeed(10),
      m_zSpeed(10),
      m_choiceTexture(0),
      m_openLight(false)

{
    initWidget();
}

void OpenglWidget::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 );
}

void OpenglWidget::initWidget()
{
    setGeometry( 400, 200, 640, 480 );
    setWindowTitle(tr("opengl demo"));
}

void OpenglWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glTranslatef( -1.5,  0.0, m_zoom );

    glRotatef( m_x,  1.0,  0.0,  0.0 );
    glRotatef( m_y,  0.0,  1.0,  0.0 );
    glRotatef( m_z,  0.0,  0.0,  1.0 );


    glBindTexture( GL_TEXTURE_2D, textur[m_choiceTexture] );

    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();

    glLoadIdentity();

    glTranslatef(  1.5,  0.0, m_zoom );

    glRotatef( m_x,  1.0,  0.0,  0.0 );
    glRotatef( m_y,  0.0,  1.0,  0.0 );
    glRotatef( m_z,  0.0,  0.0,  1.0 );


    glBegin(GL_TRIANGLES);

    //三棱柱四面贴图
    glTexCoord2f( 1, 1 ); glVertex3f( 0, 1, 0 );
    glTexCoord2f( 0, 0 ); glVertex3f(  1, -1, 1 );
    glTexCoord2f( 1, 0 ); glVertex3f(  -1, -1, 1 );

    glTexCoord2f( 1, 1 ); glVertex3f( 0, 1, 0 );
    glTexCoord2f( 0, 0 ); glVertex3f( -1.0,  -1.0, 1.0 );
    glTexCoord2f( 1, 0 ); glVertex3f(  -1.0,  -1.0, -1.0 );


    glTexCoord2f( 1, 1 ); glVertex3f( 0,  1, 0 );
    glTexCoord2f( 0, 0 ); glVertex3f( -1.0,  -1.0,  -1.0 );
    glTexCoord2f( 1, 0 ); glVertex3f(  1.0,  -1.0,  -1.0 );

    glTexCoord2f( 1, 1 ); glVertex3f( 0, 1, 0 );
    glTexCoord2f( 0, 0 ); glVertex3f(  1.0, -1.0, -1.0 );
    glTexCoord2f( 1, 0 ); glVertex3f(  1.0, -1.0,  1.0 );

    //三棱柱底面贴图
    glTexCoord2f( 0, 0 ); glVertex3f(  -1.0, -1.0, -1.0 );
    glTexCoord2f( 1, 0 ); glVertex3f(  1.0, -1.0,  -1.0 );
    glTexCoord2f( 1, 1 ); glVertex3f(  1.0, -1.0, 1.0 );

    glTexCoord2f( 1, 0 ); glVertex3f(  1.0, -1.0,  -1.0 );
    glTexCoord2f( 1, 1 ); glVertex3f(  1.0, -1.0, 1.0 );
    glTexCoord2f( 0, 1 ); glVertex3f(  -1.0, -1.0,  1.0 );

    glTexCoord2f( 1, 1 ); glVertex3f(  1.0, -1.0, 1.0 );
    glTexCoord2f( 0, 1 ); glVertex3f(  -1.0, -1.0,  1.0 );
    glTexCoord2f( 0, 0 ); glVertex3f(  -1.0, -1.0, -1.0 );

    glEnd();


    m_x += m_xSpeed;
    m_y += m_ySpeed;
    m_z += m_zSpeed;
}

void OpenglWidget::resizeGL(int width, int height)
{
    if(0 == height) {
        height = 1;
    }

    glViewport(0, 0, (GLint)width, (GLint)height);

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

  //  gluPerspective(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);

    GLdouble aspectRatio = (GLfloat)width/(GLfloat)height;
    GLdouble zNear = 0.1;
    GLdouble zFar = 100.0;

    GLdouble rFov = 45.0 * 3.14159265 / 180.0;
     glFrustum( -zNear * tan( rFov / 2.0 ) * aspectRatio,
               zNear * tan( rFov / 2.0 ) * aspectRatio,
               -zNear * tan( rFov / 2.0 ),
               zNear * tan( rFov / 2.0 ),
               zNear, zFar );

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();
}


void OpenglWidget::loadGLTextures()
{
  QImage tex;
  QImage buf;

  if ( !buf.load(":/images/dog.png"))
  {
    qWarning( "load image failed!" );
    QImage dummy( 128, 128, QImage::Format_RGB32 );
    dummy.fill( Qt::red);
    buf = dummy;

  }

  tex = QGLWidget::convertToGLFormat( buf );
  glGenTextures( 3, &textur[0] );

  //纹理一
  glBindTexture( GL_TEXTURE_2D, textur[0] );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
  glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,
      GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );


  //纹理二
  glBindTexture( GL_TEXTURE_2D, textur[1] );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,
      GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );


  //纹理三
  glBindTexture( GL_TEXTURE_2D, textur[2] );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST );
  //gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, tex.width(), tex.height(), GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
  glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,
      GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );

}



void OpenglWidget::keyPressEvent( QKeyEvent *e )
{
  switch ( e->key() )
  {
  case Qt::Key_L:
    m_openLight = !m_openLight;
    if ( !m_openLight )
    {
      glDisable( GL_LIGHTING );
    }
    else
    {
      glEnable( GL_LIGHTING );
    }
    updateGL();
    break;

  case Qt::Key_F:
    m_choiceTexture += 1;;
    if ( m_choiceTexture > 2 )
    {
      m_choiceTexture = 0;
    }
    updateGL();
    break;

  case Qt::Key_W:
    m_zoom -= (GLfloat)0.2;
    updateGL();
    break;

  case Qt::Key_S:
    m_zoom += (GLfloat)0.2;
    updateGL();
    break;

  case Qt::Key_Up:
    m_xSpeed -= 1;
    updateGL();
    break;

  case Qt::Key_Down:
    m_xSpeed += 1;
    updateGL();
    break;

  case Qt::Key_Right:
    m_ySpeed += 1;
    updateGL();
    break;

  case Qt::Key_Left:
    m_ySpeed -= 1;
    updateGL();
    break;

  case Qt::Key_A:
    m_zSpeed += 1;
    updateGL();
    break;

  case Qt::Key_D:
    m_zSpeed -= 1;
    updateGL();
    break;


  case Qt::Key_Escape:
    close();
    break;

  }
}

主要内容解析

先易后难吧,从最简单的开始讲:


QT键盘事件

void keyPressEvent( QKeyEvent *e );

其实就是重写父类的键盘事件。 当用户按键时,系统就会自己过滤到这个按键的过程, 然后可以通过返回的事件拿到按下的对应的键,我们可以根据这个键来进行判断,然后再做相应的事件处理。


立体图形的放大和缩小

其实这里的放大缩小的原理是通过立体图形离显示屏幕的离距

glTranslatef( x, y, z );

也就是这个函数。。

它控制了三维空间里面的x, y, z 轴。

x 是相对于我们所创建的屏幕左右的移动, 而y是相对于我们所创建屏幕上下的移动, z就是我们面对屏幕深度的移动, 怎么来判断它的深度呢, 这里就用了一个放大,缩小 来达到这个效果的。

讲到这里,压制不住内心的想法,要进行一下扩展

glVertex3f(x,y,z) 与 glTranslatef( x, y, z );的区别, 前者是在一个立体图形固定在某个位置后,以它为坐标原点, 所构成的一个三维空间。

而后者是以我们所创建的整个屏幕为一个三个空间,屏幕的中文为三维空间的坐标原点。

这里又想到了glLoadIdentity(); 这个,如果还创建第二个立体图形时,不加这一个,第二个立体图形是随上一个在一个三维空间里面, 加了之后,就如同分开了两个三维空间,各自做各自的。

发现扯远了一点, 还是回到立体图形的放大和缩小问题上来,它就是直接通过控制,glTranslatef(x, y, z)的z 轴来进行放大缩小的

提得一提的是, 这里的z轴,跟我们高中学习的z轴好像有点不要样, 在高中,z轴向屏幕的深层方向应该是正方向, 反之为反方向, 而在opengl中,屏幕的深层方向为反方向, 反之为正方向, 对此我也表示,发明这些opengl的难道学数学时,在他们国家讲课的内容不一样??


上下左右键,以及A键D争键控制x, y, z 轴旋转速度的快慢。

    glRotatef( m_x,  1.0,  0.0,  0.0 );
    glRotatef( m_y,  0.0,  1.0,  0.0 );
    glRotatef( m_z,  0.0,  0.0,  1.0 );



    m_x += m_xSpeed;
    m_y += m_ySpeed;
    m_z += m_zSpeed;


  case Qt::Key_Right:
    m_ySpeed += 1;
    updateGL();
    break;

  case Qt::Key_Left:
    m_ySpeed -= 1;
    updateGL();
    break;

  case Qt::Key_A:
    m_zSpeed += 1;
    updateGL();
    break;

  case Qt::Key_D:
    m_zSpeed -= 1;
    updateGL();
    break;

  case Qt::Key_Up:
    m_xSpeed -= 1;
    updateGL();
    break;

  case Qt::Key_Down:
    m_xSpeed += 1;
    updateGL();
    break;

这上面是其中对应相关联地方的代码。其实理解并不难, 关键是掌握上几节所讲的内容。

主要是通过调用下面这个函数来进行控制:
WINGDIAPI void APIENTRY glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);

angle 表示旋转的角度, x, y, z 分别表示围绕着那个方向来旋转。

当我们的angle 逐渐变大时,它的速度就变得越来越快。

我们项目里面用的m_x , m_y, m_z ,它们都只对应自己的轴,其它轴的值为零,这样的好处,是让我们更清楚地看到朝的某一个正方向的运动轨迹, 否则多个方向旋转就显得很乱。


然后到了讲有些生疏的开关灯了

开灯关灯以及矢量的实现原理

首先需要讲的是灯光效果三个很重要的元素:
环境光, 漫射光,以及 光源位置。

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 };

它们都用一个浮点的数组保存四个值,而这些数组,最后给系统来识别, 环境光, 漫射光 是光就是强度, 前三个参数的值,值从0-1 表示由弱到强, 三个参数表示RGB三色分量,最后一个是alpha通道参数

而光源位置前三个参数,表示三维空间的x,y, z轴。最后一个表示指定的位置就是光源位置,这种解释感觉有点不靠谱,然而,我也不知道怎么说。

环境光,我们可以理解为我们的物体被四面八方的光源所包围,而漫射光理解为漫反射之内的吧, 就是不是镜子的平面反射。

    glLightfv( GL_LIGHT1, GL_AMBIENT, lightAmbient );
    glLightfv( GL_LIGHT1, GL_DIFFUSE, lightDiffuse );
    glLightfv( GL_LIGHT1, GL_POSITION, lightPosition );

    glEnable( GL_LIGHT1 );




  case Qt::Key_L:
    m_openLight = !m_openLight;
    if ( !m_openLight )
    {
      glDisable( GL_LIGHTING );
    }
    else
    {
      glEnable( GL_LIGHTING );
    }
    updateGL();
    break;

这里通过绑定
我们设置的值,并开启灯光的使用权限 。

值得一提得是在贴图 的过程中, 我们有用到如下所示的接口。
glNormal3f( 0.0, 0.0, 1.0 );

这就是光源的矢量, 表示光的照射方面。

就是在x, y, z三个值中, 当两个为0时, 就是朝另外一个不为0的方面, 正负,表示方向相反。

如果不加这个关灯后,就全暗了,加了之后就是确定光照在这个矢量方向上暗。

不知不觉写了之么多, 可能里面有一些理解有误的地方,希望大家多多指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小K小Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值