在QT和SDL搭建的框架中使用OPENGL在SDL窗口上进行绘图

基于前篇关于QT+SDL的播放器框架。我们得以获取一个SDL的窗口。

又看到SDL上支持了OPENGL,因此,试想,我们是否可以用OPENGL来绘图呢,如果可以,可以通过这个渠道来学习OPENGL相关的知识。

根据原来的程序结构,我们把SDL的初始化放到了MyWidnow::CreateSDLWindow这里面。

现在我们用SDL的线程函数在这里分离出一个线程,来进行OPENGL的绘制工作。

SDL_CreateThread(sdl_main_loop, (void*)ui->SDLWindow);

这样,在sdl_main_loop函数中就可以进行OPENGL的初始化然后事件循环,绘制工作了。

先简单介绍一下流程,先设置了SDL的属性为OPENGL的模式,包括什么位深啊,颜色是几位的之类的。

然后进行opengl的设置,一些属性的设置。

然后进入一个while死循环,做两件事情,绘制屏幕和处理事件。

其中绘制屏幕就是在SDL的窗口上显示一个立方体,染了色的。

事件就是响应两个按键,空格可以暂停立方体的转动,ESC键退出程序。


这里面我们不熟悉的是OPENGL的函数们。

下面列举一下设置OPENGL属性的一些函数。

    /*
        控制两点间其他点颜色的过渡模式
        GL_SMOOTH是平滑过渡,表现出来的是渐变
        GL_FLAT是以指定的抹一点的单一颜色绘制其他所有点
    */
    //glShadeModel( GL_FLAT );
    glShadeModel( GL_SMOOTH );

glCullFace( GL_BACK );
选择物体的前面还是背面可以被隐藏,也就是不显示出来,可以一定程度上提高效率

glFrontFace( GL_CCW );
设置多边形的正面人如何确定,是顺时针还是逆时针转的方向。

glEnable( GL_CULL_FACE );
使能GL的功能

glClearColor( 0.5, 0.5, 0.5, 0.5 );
设置清除颜色,也就是屏幕清空时候用来填充的颜色

glViewport( 0, 0, width, height );
设置视角,一般就是整个屏幕

glMatrixMode( GL_PROJECTION );
设置哪个矩阵是当前使用的矩阵,这个还不知道各个矩阵是干什么用的。

glLoadIdentity( );
原点设置到屏幕中心(0,0,0)

gluPerspective( 60.0, ratio, 1.0, 1024.0 );
设置透视投影矩阵。y轴视域的角度,x轴的w/h比,z轴近截面和远截面

等等~~,还是挺多的。各种属性啊~~计算机图形学~~有木有啊有木有~~

在绘制函数中遇到的还有几个旋转的函数和设置颜色顶点然后显示的函数

glTranslatef(0.0, 0.0, -4.0);
也是矩阵运算,说白了就是将物体沿着(x,y,z)向量移动多少的距离

glRotatef(angle, 1.0, 2.0, 0.0);
物体旋转的轴,(x,y,z)轴,一次旋转angle的角度。转动方向是右手定则,拇指轴向,弯曲四指,弯曲方向就是转动方向。

glBegin(GL_TRIANGLES);
还有一个glEnd用来圈定设置顶点的函数们

glColor4ubv( red );
glVertex3fv( v0 );

这两个猜也有了,设置颜色和顶点的位置,就是把这个点设置成神马颜色的。

SDL_GL_SwapBuffers();
这个是SDL的函数,前面开启了交换缓冲区,这里调用,交换前后缓冲区,图像就显示出来了。


下面上代码。

main.cpp

#include <QtGui/QApplication>
#include "mainwindow.h"

int qMain(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFileDialog>
#include <QMessageBox>
#include <stdio.h>
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include "SDL_GL.h"

#define FILE_NAME_LENGTH    128

extern SDL_Surface *pSDLSurface;

namespace Ui {
    class MyWindow;
}

class MyWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MyWindow(QWidget *parent = 0);
    ~MyWindow();
    /* 获取当前播放还是暂停的状态 */
    int  getPlayState(void);
    /* 设置当前播放还是暂停的状态 */
    void setPlayState(int i);

private:
    Ui::MyWindow *ui;
    /* 表示当前是播放还是暂停的状态的变量 */
    int  iPlayPause;
    /* 表示要播放的文件名称 */
    char caFileName[FILE_NAME_LENGTH];

    /* 保存打开的文件名称(包括路径) */
    void SaveFileName(QString file);
    /* 获取文件名称(包括路径) */
    char *GetFileName(void);

    void CreateSDLWindow(void);

public slots:
    /* 快退 */
    void SlotsBackward(void);
    /* 快进 */
    void SlotsForward(void);
    /* 播放暂停 */
    void SlotsPlayPause(void);
    /* 停止 */
    void SlotsStop(void);
    /* 打开文件选择对话框 */
    void SlotsOpenFile(void);
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mywindow.h"

MyWindow::MyWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MyWindow)
{
    ui->setupUi(this);

    Qt::WindowFlags flags = 0;
    flags |= Qt::WindowMinimizeButtonHint; /* 禁止最大化 */
    setWindowFlags(flags);
    //QSize size = ui->MyScreen->baseSize();
    //setFixedSize(size.width(),size.height());
    setFixedSize(400,450); /* 禁止改变窗口大小 */

    iPlayPause = 0;
    connect(ui->Backward,   SIGNAL(clicked()), this, SLOT(SlotsBackward()));
    connect(ui->Forward,    SIGNAL(clicked()), this, SLOT(SlotsForward()));
    connect(ui->PlayPause,  SIGNAL(clicked()), this, SLOT(SlotsPlayPause()));
    connect(ui->Stop,       SIGNAL(clicked()), this, SLOT(SlotsStop()));
    connect(ui->OpenFile,   SIGNAL(clicked()), this, SLOT(SlotsOpenFile()));

    CreateSDLWindow();
}

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

/* 快退 */
void MyWindow::SlotsBackward(void)
{
    qDebug("Backward");
}

/* 快进 */
void MyWindow::SlotsForward(void)
{
    qDebug("Forward");
}


/* 第一次点下去从播放变为暂停,以后每次布尔状态切换播放和暂停状态 */
void MyWindow::SlotsPlayPause(void)
{
    if (getPlayState()==0) {
        qDebug("Play: %s", GetFileName());
        setPlayState(1);
    } else {
        qDebug("Pause");
        setPlayState(0);
    }
}

/* 停止当前的播放 */
void MyWindow::SlotsStop(void)
{
    qDebug("Stop");
}

/* 获取当前的播放状态,播放还是暂停 */
int MyWindow::getPlayState(void)
{
    return iPlayPause;
}

/* 设置当前的播放状态,播放还是暂停 */
void MyWindow::setPlayState(int i)
{
    iPlayPause = i;
}

/* 与OPEN按钮关联的动作,显示文件选择对话框,让用户选择想要打开的文件 */
void MyWindow::SlotsOpenFile(void)
{
    qDebug("Open File");
    QFileDialog *fd = new QFileDialog(this);
    fd->setModal(QFileDialog::ExistingFile);    /* 设置模式为存在的文件 */
    fd->setViewMode(QFileDialog::Detail);       /* 设置显示模式为详细 */
    fd->setFilter("Video (*.mpeg *.avi)");      /* 设置过滤器,显示特定后缀名的文件 */
    if (fd->exec() == QDialog::Accepted) {
        QString file = fd->selectedFiles()[0];  /* 取得选择的文件,包括了绝对路径 */
        //qDebug(file.toAscii().data());
        SaveFileName(file);
    }
}

/* 将想要打开的媒体文件的名字保存到一个数组里面,方便使用 */
void MyWindow::SaveFileName(QString file)
{
    memset(caFileName, 0, FILE_NAME_LENGTH);
    strcpy(caFileName, file.toAscii().data());
}

/* 获取想要打开的媒体文件的名字 */
char* MyWindow::GetFileName(void)
{
    return caFileName;
}

/* 把一个QT的窗口交给SDL使用 */
void MyWindow::CreateSDLWindow(void)
{
    QWidget *widget = ui->SDLWindow;

    {
        /* 使用SDL的关键步骤 */
        char winID[32] = {0};
        sprintf(winID, "SDL_WINDOWID=0x%lx", (long unsigned int)widget->winId());
        SDL_putenv(winID);

        /* 初始化SDL */
        if (SDL_Init(SDL_INIT_VIDEO) < 0) {
            fprintf( stderr, "Video initialization failed: %s\n",
                 SDL_GetError( ) );
        }

        /* 开辟一个线程,用来对SDL的窗口进行操作,传递的参数是为了得到窗口的大小 */
        SDL_CreateThread(sdl_main_loop, (void*)ui->SDLWindow);
    }
}

SDL_GL.h

#ifndef SDL_GL_H
#define SDL_GL_H
#include "mainwindow.h"

int sdl_main_loop(void *window);

#endif // SDL_GL_H


SDL_GL.cpp

#include <GL/gl.h>
#include <GL/glu.h>
#include "mainwindow.h"
#include "SDL_GL.h"

static GLboolean should_rotate = GL_TRUE;

static void quit_SDL( int code )
{
    SDL_Quit( );

    exit( code );
}

/* 设置OPENGL的相关属性 */
static void setup_opengl( int width, int height )
{
    /* 比率,宽度除以高度 */
    float ratio = (float) width / (float) height;

    /*
        控制两点间其他点颜色的过渡模式
        GL_SMOOTH是平滑过渡,表现出来的是渐变
        GL_FLAT是以指定的抹一点的单一颜色绘制其他所有点
    */
    //glShadeModel( GL_FLAT );
    glShadeModel( GL_SMOOTH );

    glCullFace( GL_BACK );
    //glCullFace(GL_FRONT);
    //glCullFace(GL_FRONT_AND_BACK);
    glFrontFace( GL_CCW );
    glEnable( GL_CULL_FACE );

    glClearColor( 0.5, 0.5, 0.5, 0.5 );

    glViewport( 0, 0, width, height );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );

    gluPerspective( 60.0, ratio, 1.0, 1024.0 );
}

static void handle_key_down( SDL_keysym* keysym )
{
    switch( keysym->sym ) {
    case SDLK_ESCAPE:
        quit_SDL( 0 );
        break;
    case SDLK_SPACE:
        should_rotate = !should_rotate;
        break;
    default:
        break;
    }

}

static void process_events( void )
{
    SDL_Event event;

    while( SDL_PollEvent( &event ) ) {

        switch( event.type ) {
        case SDL_KEYDOWN:
            handle_key_down( &event.key.keysym );
            break;
        case SDL_QUIT:
            quit_SDL( 0 );
            break;
        }

    }

}

static void draw_scren(void)
{
    static float angle = 0.0f;

    static GLfloat v0[] = {-1.0f, -1.0f,  1.0f};
    static GLfloat v1[] = { 1.0f, -1.0f,  1.0f};
    static GLfloat v2[] = { 1.0f,  1.0f,  1.0f};
    static GLfloat v3[] = {-1.0f,  1.0f,  1.0f};
    static GLfloat v4[] = {-1.0f, -1.0f, -1.0f};
    static GLfloat v5[] = { 1.0f, -1.0f, -1.0f};
    static GLfloat v6[] = { 1.0f,  1.0f, -1.0f};
    static GLfloat v7[] = {-1.0f,  1.0f, -1.0f};
    static GLubyte red[]    = {255, 0, 0, 255};
    static GLubyte green[]  = {0, 255, 0, 255};
    static GLubyte blue[]   = {0, 0, 255, 255};
    static GLubyte white[]  = {255, 255, 255, 255};
    static GLubyte yellow[] = {0, 255, 255, 255};
    static GLubyte black[]  = {0, 0, 0, 255};
    static GLubyte orange[] = {255, 255, 0, 255};
    static GLubyte purple[] = {255, 0, 255, 0};

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslatef(0.0, 0.0, -4.0);

    glRotatef(angle, 1.0, 2.0, 0.0);

    if (should_rotate) {
        if (++angle > 360.0f) {
            angle = 0.0f;
        }
    }

    /* 立方体有6个面,下面的绘制把每个面分成两个三角形来渲染的,所以做了12组操作 */
    glBegin(GL_TRIANGLES);
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( blue );
    glVertex3fv( v2 );

    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( white );
    glVertex3fv( v3 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( orange );
    glVertex3fv( v6 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( blue );
    glVertex3fv( v2 );

    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( purple );
    glVertex3fv( v7 );

    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( purple );
    glVertex3fv( v7 );
    glColor4ubv( orange );
    glVertex3fv( v6 );

    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( white );
    glVertex3fv( v3 );

    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( purple );
    glVertex3fv( v7 );

    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( orange );
    glVertex3fv( v6 );

    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( purple );
    glVertex3fv( v7 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( black );
    glVertex3fv( v5 );

    glEnd();

    SDL_GL_SwapBuffers();
}


/* SDL线程里面的主循环 */
int sdl_main_loop(void *window)
{
    const SDL_VideoInfo *info = NULL;
    int width   = 0;
    int height  = 0;
    int bpp     = 0;
    int flags   = 0;
    QWidget *screen = (QWidget*)window;

    info = SDL_GetVideoInfo( ); /* 获取视频硬件的相关信息,主要为了获得bpp */

    if( !info ) {
        fprintf( stderr, "Video query failed: %s\n",
             SDL_GetError( ) );
        quit_SDL( 1 );
    }

    width   = screen->width();
    height  = screen->height();
    width   = 400;
    height  = 400;
    bpp = info->vfmt->BitsPerPixel;

    /* 设置属性,RGB的大小和位深,开启双缓冲 */
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    /* 设置视频属性, 这是为了创建一个OPENGL渲染的上下文 */
    flags = SDL_OPENGL;

    if( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
        fprintf( stderr, "Video mode set failed: %s\n",
             SDL_GetError( ) );
        quit_SDL( 1 );
    }
    setup_opengl( width, height );

    while( 1 ) {
        process_events();
        draw_scren();
        SDL_Delay(5);
    }
    return 0;
}

OK,最后的结果是,一个彩色的立方体在窗口里旋转。


阅读更多
个人分类: QT
上一篇在QT搭建的播放器外壳中嵌入SDL的窗口
下一篇OPENGL中简单点的理解以及关于照相机的简单理解
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭