歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

QT與OpenGL之三維旋轉

效果:

制作短暫的效果。

在這篇文章中,將要展現的效果是在3D場景中移動位圖,得到簡單的動畫效果。便於大家共同學習,給出全部代碼:

在開始前,你應該在pro中添加

LIBS += glut.lib glut32.lib

同時你也應該將這兩個庫加到QT的bin目錄下。

glwidget.h

#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QtOpenGL>
#include <QWidget>

namespace Ui {
class GLWidget;
}

class GLWidget : public QGLWidget
{
    Q_OBJECT

public:
    explicit GLWidget(QGLWidget *parent = 0);
    ~GLWidget();

protected:
    void initializeGL();  //初始化OpenGL窗口部件
    void paintGL();  //繪制整個OpenGL窗口,只要有更新發生,這個函數就會被調用
    void resizeGL(int width, int height); //處理窗口大小變化事件的,參數是新狀態下的寬和高
    void keyPressEvent(QKeyEvent *e);  //鼠標處理函數
    void loadGLTextures();
    void timerEvent(QTimerEvent *);

protected:
    bool fullscreen;  //判斷是否全屏的變量
    bool twinkle;  //閃爍的星星
    bool tp;  //"T" 按下了嗎
    bool blend;
    GLfloat rotate_angle;
    double title;
    double zoom;
    double tilt;
    GLfloat spin;
    GLuint loop;
    GLuint texture[1];

private:
    Ui::GLWidget *ui;
};

#endif // GLWIDGET_H

glwidget.cpp

#include "glwidget.h"
#include "ui_glwidget.h"

#include <QtGui>
#include <QtCore>
#include <QtOpenGL>
#include <GL/GLU.h>

const int num = 50;

typedef struct
{
    int r, g, b;
    GLfloat dist;
    GLfloat angle;
}stars;
stars star[num];

GLfloat light_ambient[4]={0.5, 0.5, 0.5, 1.0};
GLfloat light_diffuse[4]={1.0, 1.0, 1.0, 1.0};
GLfloat light_position[4]={0.0, 0.0, 2.0, 0.0};

GLWidget::GLWidget(QGLWidget *parent) :
    QGLWidget(parent),
    ui(new Ui::GLWidget)
{
    ui->setupUi(this);
    fullscreen = false;
    rotate_angle = 0.0;
    zoom = -15.0;
    title = 90.0;
    spin = (GLfloat)0.1;
    loop = 0;
    twinkle = false;
    blend = false;
    startTimer(5);//開啟5ms定時器
}

//這是對虛函數,這裡是重寫該函數
void GLWidget::initializeGL()
{
    setGeometry(300, 150, 500, 500);//設置窗口初始位置和大小
    loadGLTextures();
    glEnable(GL_TEXTURE_2D);//允許采用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);//源像素因子采用alpha通道值,目標像素因子采用1.0
    glEnable(GL_BLEND);

    /*為num個星星的數組結構體賦初值*/
    for(loop = 0; loop < num; loop++)
        {
            star[loop].angle = 0.0;
            star[loop].dist = (float(loop)/num)*5.0;//星星的離中心的距離越來越遠,最大距離為5,接近屏幕的距離
            star[loop].r = rand()%256;
            star[loop].g = rand()%256;
            star[loop].b = rand()%256;
        }
}

void GLWidget::paintGL()
{
    //glClear()函數在這裡就是對initializeGL()函數中設置的顏色和緩存深度等起作用
    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);//移向屏幕裡面
            glRotatef(title, 1.0, 0.0, 0.0);//沿著x軸旋轉了tilt度
            glRotatef(star[loop].angle, 0.0, 1.0, 0.0);//每個星星沿著y軸旋轉自己的角度
            glTranslatef(star[loop].dist, 0.0, 0.0);
            glRotatef(-star[loop].angle, 0.0, 1.0, 0.0);//將沿著y軸旋轉的角度又轉回去
            glRotatef(-title, 1.0, 0.0, 0.0);//將沿著x軸旋轉過的角度也轉回去
            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();
                }
            /*不閃爍時*/
            glRotatef(spin, 0.0, 0.0, 1.0);//星星沿z軸自轉spin度
            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();

            spin += 0.01f;
            star[loop].angle += float(loop)/num;//角度是在慢慢增大的
            star[loop].dist -= 0.01f;//距離慢慢減小,被吸向了屏幕中心
            if(star[loop].dist < 0)
                {
                    star[loop].dist += 5.0f;
                    star[loop].r = rand()%256;
                    star[loop].g = rand()%256;
                    star[loop].b = rand()%256;
                }
        }
}

//該程序是設置opengl場景透視圖,程序中至少被執行一次(程序啟動時).
void GLWidget::resizeGL(int width, int height)
{
    if(0 == height)
        height = 1;//防止一條邊為0
    glViewport(0, 0, (GLint)width, (GLint)height);//重置當前視口,本身不是重置窗口的,只不過是這裡被Qt給封裝好了
    glMatrixMode(GL_PROJECTION);//選擇投影矩陣
    glLoadIdentity();//重置選擇好的投影矩陣
    gluPerspective(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);//建立透視投影矩陣
    glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glMatrixMode(GL_MODELVIEW);//以下2句和上面出現的解釋一樣
    glLoadIdentity();

}
void GLWidget::keyPressEvent(QKeyEvent *e)
{
    switch(e->key())
    {
        /*PageUp鍵為將木箱移到屏幕內部方向*/
        case Qt::Key_T:
        twinkle = !twinkle;
        updateGL();
        break;
        /*B鍵位選擇是否采用色彩融合*/
        case Qt::Key_B:
            blend = !blend;
            if(blend)
                {
                    glEnable(GL_BLEND);//色彩融合和深度緩存不能同時開啟
                    glDisable(GL_DEPTH_TEST);
                }
            else
                {
                    glDisable(GL_BLEND);
                    glEnable(GL_DEPTH_TEST);
                }
            updateGL();
            break;
        /*PageUp鍵為將木箱移到屏幕內部方向*/
        case Qt::Key_PageUp:
            zoom -= 0.2;
            updateGL();
            break;
        /*PageDown鍵為將木箱移到屏幕外部方向*/
        case Qt::Key_PageDown:
            zoom += 0.2;
            updateGL();
            break;
        /*Up鍵為加快立方體旋轉的速度*/
        case Qt::Key_Up:
            title += 0.5;
            updateGL();
            break;
        /*Down鍵為減慢立方體旋轉的速度*/
        case Qt::Key_Down:
            title -= 0.5;
            updateGL();
            break;
        /*F1鍵為全屏和普通屏顯示切換鍵*/
        case Qt::Key_F1:
            fullscreen = !fullscreen;
            if(fullscreen)
                showFullScreen();
            else
            {
                setGeometry(300, 150, 500, 500);
                showNormal();
            }
            updateGL();
            break;
        /*Ese為退出程序鍵*/
        case Qt::Key_Escape:
            close();
    }
}

/*裝載紋理*/
void GLWidget::loadGLTextures()
{
    QImage tex, buf;
    if(!buf.load(":/images/2.bmp"))//這個時候因為debug沒有在外面,所以圖片文件夾就是本目錄了
  // if(!buf.load("../opengl_qt_nehe_07/crate.bmp"))
    {
        qWarning("Cannot open the image...");
        QImage dummy(128, 128, QImage::Format_RGB32);//當沒找到所需打開的圖片時,創建一副128*128大小,深度為32位的位圖
        dummy.fill(Qt::green);
        buf = dummy;
    }
    tex = convertToGLFormat(buf);//將Qt圖片的格式buf轉換成opengl的圖片格式tex
    glGenTextures(1, &texture[0]);//開辟3個紋理內存,索引指向texture[0]

    /*建立第一個紋理*/
    glBindTexture(GL_TEXTURE_2D, texture[0]);//將創建的紋理內存指向的內容綁定到紋理對象GL_TEXTURE_2D上,經過這句代碼後,以後對
                                            //GL_TEXTURE_2D的操作的任何操作都同時對應與它所綁定的紋理對象
    glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());//開始真正創建紋理數據
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//當所顯示的紋理比加載進來的紋理小時,采用GL_NEAREST的方法來處理
                                                                      //GL_NEAREST方式速度非常快,因為它不是真正的濾波,所以占用內存非常
                                                                      // 小,速度就快了
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//當所顯示的紋理比加載進來的紋理大時,采用GL_NEAREST的方法來處理

}

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

GLWidget::~GLWidget()
{
    delete ui;
}

Copyright © Linux教程網 All Rights Reserved