1. 程式人生 > >QT5 OpenGL (六, 鍵盤事件, 開關燈,放大縮小綜合運用)

QT5 OpenGL (六, 鍵盤事件, 開關燈,放大縮小綜合運用)

gdi 按鍵 直接 2.0 中學 void lint ota 不為

    • 概要
    • 實例效果圖
      • 立體圖放大圖
      • 立體圖縮小圖
      • 不加矢量開燈圖
      • 不加矢量關燈圖
      • 加矢量關燈圖1
      • 加矢量關燈圖2
    • 部分代碼展示
    • 主要內容解析
      • QT鍵盤事件
      • 立體圖形的放大和縮小
      • 上下左右鍵以及A鍵D爭鍵控制x y z 軸旋轉速度的快慢
    • 開燈關燈以及矢量的實現原理


概要

多篇講QT5 opengl的文章,從簡單到復雜,差點兒每篇都在原來的基友上有所增加新的內容, 感覺越到後面,越easy被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的方面, 正負。表示方向相反。

假設不加這個關燈後。就全暗了,加了之後就是確定光照在這個矢量方向上暗。

不知不覺寫了之麽多, 可能裏面有一些理解有誤的地方,希望大家多多指正。

QT5 OpenGL (六, 鍵盤事件, 開關燈,放大縮小綜合運用)