任务之余抽点时间出来写写博文,希望对新接触的朋友有帮助。今天在这里和大家一起学习一下实现图形
OpenGL是绘制三维图形的标准API。Qt应用程序可以应用QtOpenGL模块绘制三维图形,该模块依赖于系统的OpenGL库。Qt OpenGL模块供给QGLWidget类,可以通过对它子类化,并应用OpenGL命令开发出自己的窗口部件。对许多三维应用程序来讲,这就足够了。
这节假设大家都已经学过OpenGL,这样我们就无后顾之忧了。
在Qt中绘制OpenGL通常需要做以下任务:1)、必须子类化QGLWidget;2)、实现几个虚函数:void initiallizeGL()
void resizeGL(), void paintGL(), 这些都是在QGLWidget中实现的,还有一些和用户交互的虚函数,诸如void mouseMoveEvent()之类的,想必大家都比较熟了,这些虚函数是在Widget中实现的。
下面我们介绍一个例子。先给出该程序的效果:
菜单栏里的第一项可以实现一个自定义巨细的抓图,即由用户自己决定抓图的巨细,抓图会显示在右侧的方框里,注意这里只能设置图形的巨细小雨当前图形的尺寸, 如果大于当前图形尺寸,则钳位到当前图形尺寸。效果看起来应该是这样:
菜单栏第二项也是一个抓图功能,它返回一个当前图形尺寸的图形,并填充到右侧。
第三项即清除右侧图形。
这个代码由以下部件形成:
一个QMainWindow,我们通过子类化这个类来实现自己想要的一些功能。
一个QWidget,我们把它作为中心窗口,在其上添加自己想要的一些子部件。
两个QScrollBar,用来盛载一个QGLWidget和一个QLabel。
一个QGLWidget,我们通过子类化它并把它加进一个QScrollBar来实现三维绘图,即上图所示的左边窗口。
一个QLabel,同样,我们把这个QLabel加进一个QScrollBar来接收抓图后的显示效果。
三个QSlider,我们通过这三个滑动条控制所绘制的四面体沿x,y,z轴动弹,同样鼠标拖动这个四面体也可以转变滑动条的值。
以上是全部程序的框架。
以下是代码的实现部分。
MainWindow 类定义了我们全部程序的框架:
//mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QAction; class QLabel; class QMenu; class QSlider; class QScrollArea; class GLWidget; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void renderIntoPixmap(); void grabFrameBuffer(); void clearPixmap(); void about(); private: void createMenus(); void createActions(); QSlider *createSlider(const char *changedSignal, const char *setterSlot); void setPixmap(const QPixmap &pixmap); QSize getSize(); QWidget *centralWidget; QScrollArea *glWidgetArea; QScrollArea *pixmapLabelArea; GLWidget *glWidget; QLabel *pixmapLabel; QSlider *xSlider; QSlider *ySlider; QSlider *zSlider; QMenu *fileMenu; QMenu *helpMenu; QAction *renderIntoPixmapAction; QAction *grabFrameBufferAction; QAction *clearPixmapAction; QAction *exitAction; QAction *aboutAction; QAction *aboutQtAction; }; #endif // MAINWINDOW_H
以下是程序的实现部分:
//mainwindow.cpp #include <QtOpenGL> #include <QAction> #include <QLabel> #include <QMenu> #include <QSlider> #include <QScrollArea> #include <QMenuBar> #include <QApplication> #include "mainwindow.h" #include "glwidget.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { centralWidget = new QWidget; setCentralWidget(centralWidget); glWidget = new GLWidget; pixmapLabel = new QLabel; glWidgetArea = new QScrollArea; glWidgetArea->setWidget(glWidget); //glWidgetArea->viewport()->setBackgroundRole(QPalette::Dark); glWidgetArea->setWidgetResizable(true); glWidgetArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); glWidgetArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); glWidgetArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); glWidgetArea->setMinimumSize(50, 50); pixmapLabelArea = new QScrollArea; pixmapLabelArea->setWidget(pixmapLabel); pixmapLabelArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); pixmapLabelArea->setMinimumSize(50, 50); //在结构一个QSlider时将QGLWidget的信号和槽传给这个函数的形参,这样就可以在QMainWindow中 //控制OpenGL的动作了,而让GLWidget类只实现绘图任务。 xSlider = createSlider(SIGNAL(xRotationChanged(int)), SLOT(setXRotation(int))); ySlider = createSlider(SIGNAL(yRotationChanged(int)), SLOT(setYRotation(int))); zSlider = createSlider(SIGNAL(zRotationChanged(int)), SLOT(setZRotation(int))); /* xSlider = new QSlider(Qt::Horizontal); ySlider = new QSlider(Qt::Horizontal); zSlider = new QSlider(Qt::Horizontal); */ QGridLayout *centralLayout = new QGridLayout; centralLayout->addWidget(glWidgetArea, 0, 0); centralLayout->addWidget(pixmapLabelArea, 0, 1); centralLayout->addWidget(xSlider, 1, 0, 1, 2); centralLayout->addWidget(ySlider, 2, 0, 1, 2); centralLayout->addWidget(zSlider, 3, 0, 1, 2); centralWidget->setLayout(centralLayout); createActions(); createMenus(); xSlider->setValue(15 * 16); ySlider->setValue(345 * 16); zSlider->setValue(0 * 16); setWindowTitle(tr("Grabeer")); resize(480, 360); } void MainWindow::setPixmap(const QPixmap &pixmap) { pixmapLabel->setPixmap(pixmap); QSize size = pixmap.size(); if (size - QSize(1, 0) == pixmapLabelArea->maximumViewportSize()) size -= QSize(1, 0); pixmapLabel->resize(size); } QSize MainWindow::getSize() { bool ok; QString text = QInputDialog::getText(this, tr("Grabber"), tr("Enter Pixmap Size:"), QLineEdit::Normal, tr("%1 x %2").arg(glWidget->width()) .arg(glWidget->height()), &ok); if (!ok) return QSize(); QRegExp regExp(tr("([0-9]+) *x *([0-9]+)")); if (regExp.exactMatch(text)) { int width = regExp.cap(1).toInt(); int height = regExp.cap(2).toInt(); if (width > 0 && width < 2048 && height > 0 && height < 2048) return QSize(width, height); } return glWidget->size(); } void MainWindow::renderIntoPixmap() { QSize size = getSize(); if (size.isValid()) { QPixmap pixmap = glWidget->renderPixmap(size.width(), size.height()); setPixmap(pixmap); } } void MainWindow::grabFrameBuffer() { //QGLWidget有一个返回其帧缓冲区的QImage图片的函数 QImage image = glWidget->grabFrameBuffer(); //QPixmap的fromImage函数把一个QImage转换成QPixmap setPixmap(QPixmap::fromImage(image)); } void MainWindow::clearPixmap() { setPixmap(QPixmap()); //给它传一个空的对象 } void MainWindow::about() { QMessageBox::about(this, tr("About Grabber"), tr("The <b>Grabber</b> example demonstrates two approaches for " "rendering OpenGL into a Qt pixmap.")); } QSlider *MainWindow::createSlider(const char *changedSignal, const char *setterSlot) { QSlider *slider = new QSlider(Qt::Horizontal); slider->setRange(0, 16 * 360); slider->setSingleStep(16); slider->setPageStep(15 * 16); slider->setTickInterval(15 * 16); slider->setTickPosition(QSlider::TicksRight); //这种经典的用法一定要当心,报错:glWidget的槽函数在传进来的时候已经被强制转换成SLOT了, //所以setterSlot不必SLOT修饰;同样,changedSignal也不能再拿SIGNAL修饰 connect(slider, SIGNAL(valueChanged(int)), glWidget, setterSlot); connect(glWidget, changedSignal, slider, SLOT(setValue(int))); return slider; } void MainWindow::createActions() { renderIntoPixmapAction = new QAction(tr("&Render into Pixmap..."), this); renderIntoPixmapAction->setShortcut(tr("Ctrl+R")); renderIntoPixmapAction->setToolTip(tr("yes, triggerd it")); connect(renderIntoPixmapAction, SIGNAL(triggered()), this, SLOT(renderIntoPixmap())); grabFrameBufferAction = new QAction(tr("&Grab Frame Buffer"), this); grabFrameBufferAction->setShortcut(tr("Ctrl+G")); connect(grabFrameBufferAction, SIGNAL(triggered()), this, SLOT(grabFrameBuffer())); clearPixmapAction = new QAction(tr("&Clear Pixmap"), this); clearPixmapAction->setShortcut(tr("Ctrl+L")); connect(clearPixmapAction, SIGNAL(triggered()), this, SLOT(clearPixmap())); exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcuts(QKeySequence::Quit); connect(exitAction, SIGNAL(triggered()), this, SLOT(close())); aboutAction = new QAction(tr("&About"), this); connect(aboutAction, SIGNAL(triggered()), this, SLOT(about())); aboutQtAction = new QAction(tr("About &Qt"), this); connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); } void MainWindow::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(renderIntoPixmapAction); fileMenu->addAction(grabFrameBufferAction); fileMenu->addAction(clearPixmapAction); fileMenu->addSeparator(); fileMenu->addAction(exitAction); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAction); helpMenu->addAction(aboutQtAction); } MainWindow::~MainWindow() { }
GLWidget类是全部绘图的部分,在这部分虽然用了一点Qt的东西,但我们不是非要这样应用,我们可以用全部纯净的OpengGL库来实现我们的绘制。
//glwidget.h #ifndef GLWIDGET_H #define GLWIDGET_H #include <QGLWidget> class GLWidget : public QGLWidget { Q_OBJECT public: explicit GLWidget(QWidget *parent = 0); int xRotation() const { return xRot; } int yRotation() const { return yRot; } int zRotation() const { return zRot; } signals: void xRotationChanged(int angle); void yRotationChanged(int angle); void zRotationChanged(int angle); public slots: void setXRotation(int angle); void setYRotation(int angle); void setZRotation(int angle); protected: void initializeGL(); void paintGL(); void resizeGL(int w, int h); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); private slots: void alwaysRotate(); void drawTriangle(); private: void normalizeAngle(int *angle); int xRot; int yRot; int zRot; QColor faceColors[4]; QPoint lastPos; }; #endif // GLWIDGET_H
以下是代码实现部分:
//glwidget.cpp #include <QtGui> #include <QtOpenGL> #include <math.h> #include "glwidget.h" GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent) { xRot = 0; yRot = 0; zRot = 0; faceColors[0] = Qt::red; faceColors[1] = Qt::green; faceColors[2] = Qt::blue; faceColors[3] = Qt::yellow; QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(alwaysRotate())); timer->start(70); } void GLWidget::initializeGL() { glClearColor(0.0, 0.2, 0.3, 1.0); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH); } void GLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); drawTriangle(); glPopMatrix(); } void GLWidget::resizeGL(int w, int h) { int side = qMin(w, h); glViewport((width() - side) / 2, (height() - side) / 2, side, side); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.2, 1.2, -1.2, 1.2, 5.0, 60.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -40.0); } void GLWidget::mousePressEvent(QMouseEvent *event) { lastPos = event->pos(); } void GLWidget::mouseMoveEvent(QMouseEvent *event) { int dx = event->x() - lastPos.x(); int dy = event->y() - lastPos.y(); if (event->buttons() & Qt::LeftButton) { (xRot + 4 * dx); setYRotation(yRot + 4 * dy); } else if (event->buttons() & Qt::RightButton) { (xRot + 4 * dy); setZRotation(zRot + 4 * dx); } lastPos = event->pos(); } void GLWidget::drawTriangle() { static const GLfloat P1[3] = { 0.0, -1.0, +2.0 }; static const GLfloat P2[3] = { +1.73205081, -1.0, -1.0 }; static const GLfloat P3[3] = { -1.73205081, -1.0, -1.0 }; static const GLfloat P4[3] = { 0.0, +2.0, 0.0 }; static const GLfloat * const coords[4][3] = { { P1, P2, P3 }, { P1, P3, P4 }, { P1, P4, P2 }, { P2, P4, P3 } }; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -10.0); glRotatef(xRot, 1.0, 0.0, 0.0); glRotatef(yRot, 0.0, 1.0, 0.0); glRotatef(zRot, 0.0, 0.0, 1.0); for (int i = 0; i != 4; ++i) { //glLoadName(i); glBegin(GL_TRIANGLES); qglColor(faceColors[i]); for (int j = 0; j < 3; ++j) { glVertex3f(coords[i][j][0], coords[i][j][1], coords[i][j][2]); } glEnd(); } } void GLWidget::normalizeAngle(int *angle) { while (*angle < 0) angle += 360 * 16; while (*angle > 360 *16) angle -= 360 *16; } void GLWidget::setXRotation(int angle) { normalizeAngle(&angle); if ( angle != xRot ) { xRot = angle; emit xRotationChanged(angle); updateGL(); } } void GLWidget::setYRotation(int angle) { normalizeAngle(&angle); if ( angle != yRot ) { yRot = angle; emit yRotationChanged(angle); updateGL(); } } void GLWidget::setZRotation(int angle) { normalizeAngle(&angle); if ( angle != zRot ) { zRot = angle; emit zRotationChanged(angle); updateGL(); } } void GLWidget::alwaysRotate() { zRot += 2; emit zRotationChanged(zRot); updateGL(); }
至此,我们就实现了全部应用程序的编写,继续努力。
文章结束给大家分享下程序员的一些笑话语录:
程序员打油诗
写字楼里写字间,写字间里程序员;
程序人员写程序,又拿程序换酒钱。
酒醒只在网上坐,酒醉还来网下眠;
酒醉酒醒日复日,网上网下年复年。
但愿老死电脑间,不愿鞠躬老板前;
奔驰宝马贵者趣,公交自行程序员。
别人笑我忒疯癫,我笑自己命太贱;
不见满街漂亮妹,哪个归得程序员。