OpenGL图象

发布: 2008-08-12 23:49

       OpenGL是一种渲染2D和3D图象的标准API。Qt程序可以使用QtOpenGL模块绘制3D图象,这依赖于系统的OpenGL库。这一节假设你已经熟悉了OpenGL。如果OpenGL对你来说是新的,一个开始学习的好地方是http://www.opengl.org/。
       在Qt程序中使用OpenGL绘图是非常直接的:我们必须继承QGLWidget,重新实现几个虚函数,并把程序与QtOpenGL和OpenGL库链接。因为QGLWidget继承自QWidget,我们已经知道的大多数依然可用。主要的不同是我们使用标准的OpenGL函数执行绘图而不是用QPainter。
       为了展示这是如何工作的,我们预览一下图8.17中展示的四面体程序的代码。该程序展示了一个3D的四面体,或者叫四边骰子,它的每一面都使用不同的颜色绘制。用户可以通过按下鼠标并拖动来旋转该四面体。用户也可以通过双击它并从弹出的QcolorDialog选择一种颜色设置其中一面的颜色。
图8.17 四面体程序


[code type="cpp-qt"]
class Tetrahedron : public QGLWidget
{
Q_OBJECT
public:
Tetrahedron(QWidget *parent = 0);
protected:
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
private:
void draw();
int faceAtPosition(const QPoint &pos);
GLfloat rotationX;
GLfloat rotationY;
GLfloat rotationZ;
QColor faceColors[4];
QPoint lastPos;
};
[/code]

       该Tetrahedron类继承自QGLWidget。initializeGL(), resizeGL(), 和 paintGL()函数都是重新实现了QGLWidget中的。鼠标事件处理器像通过一样是重新实现了QWidget中的虚函数。

[code type="cpp-qt"]
Tetrahedron::Tetrahedron(QWidget *parent)
: QGLWidget(parent)
{
setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
rotationX = -21.0;
rotationY = -57.0;
rotationZ = 0.0;
faceColors[0] = Qt::red;
faceColors[1] = Qt::green;
faceColors[2] = Qt::blue;
faceColors[3] = Qt::yellow;
}
[/code]

       在构造函数中,我们调用QGLWidget::setFormat()来指定OpenGL显示上下文,并初始化该类的私有变量。

[code type="cpp-qt"]
void Tetrahedron::initializeGL()
{
qglClearColor(Qt::black);
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
[/code]

       initializeGL()函数仅在paintGL()调用前调用了一次。这是我们可以设置渲染上下文,定义显示列表,和执行其他初始化的地方。
       所有代码都是标准的OpenGL,除了QGLWidget的qglClearColor()函数之外。如果我们希望坚持使用OpenGL,我们可以在RGBA模式下调用glClearColor(),在颜色索引模式调用glClearIndex()。

[code type="cpp-qt"]
void Tetrahedron::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat x = GLfloat(width) / height;
glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
glMatrixMode(GL_MODELVIEW);
}
[/code]

       resizeGL()函数在paintGL()首次调用前,在initializeGL()被调用之后被调用。它还在物件在任何调整尺寸的时候被调用。这是我们可以设置OpenGL视口、投影以及任何依赖于物件尺寸的其他设置的地方。

[code type="cpp-qt"]
void Tetrahedron::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw();
}
[/code]

       paintGL()函数在任何物件需要重绘的时候调用。这与QWidget::paintEvent()很相似,但与QPainter函数相反,我们使用OpenGL函数。实现的绘图是在私有函数draw()中执行的。

[code type="cpp-qt"]
void Tetrahedron::draw()
{
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(rotationX, 1.0, 0.0, 0.0);
glRotatef(rotationY, 0.0, 1.0, 0.0);
glRotatef(rotationZ, 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();
}
}
[/code]

       在draw()中,我们绘制该四面体,利用x, y, 和 z旋转和存储在faceColors数组中的颜色。所有都是标准OpenGL,除了qglColor()调用之外。我们本应该使用glColor3d() 或者 glIndex()中的一个,具体用哪个依赖于当前的模式。

[code type="cpp-qt"]
void Tetrahedron::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void Tetrahedron::mouseMoveEvent(QMouseEvent *event)
{
GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
if (event->buttons() & Qt::LeftButton) {
rotationX += 180 * dy;
rotationY += 180 * dx;
updateGL();
} else if (event->buttons() & Qt::RightButton) {
rotationX += 180 * dy;
rotationZ += 180 * dx;
updateGL();
}
lastPos = event->pos();
}
[/code]

       mousePressEvent() 和 mouseMoveEvent()函数也是重新实现自QWidget以允许用户通过点击和拖动旋转该视图。鼠标左键允许用户沿x和y轴旋转,鼠标右键允许用户沿x和z轴旋转。
       在修改了rotationX、rotationY 或者rotationZ变量后,我们调用updateGL()重绘该场景。

[code type="cpp-qt"]
void Tetrahedron::mouseDoubleClickEvent(QMouseEvent *event)
{
int face = faceAtPosition(event->pos());
if (face != -1) {
QColor color = QColorDialog::getColor(faceColors[face], this);
if (color.isValid()) {
faceColors[face] = color;
updateGL();
}
}
}
[/code]

       mouseDoubleClickEvent()重新实现自QWidget以允许用户通过双击它设置四面体各面的颜色。我们调用私有函数faceAtPosition()来决定哪一面,如果有的话,位于鼠标的下面。如果一个面被双击,我们调用QColorDialog::getColor()为该面获取一个新的颜色。然后我们用新的颜色值更新faceColors数组,再调用updateGL()来重绘该场景。

[code type="cpp-qt"]
int Tetrahedron::faceAtPosition(const QPoint &pos)
{
const int MaxSize = 512;
GLuint buffer[MaxSize];
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(MaxSize, buffer);
glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(GLdouble(pos.x()), GLdouble(viewport[3] - pos.y()),
5.0, 5.0, viewport);
GLfloat x = GLfloat(width()) / height();
glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
draw();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
if (!glRenderMode(GL_RENDER))
return -1;
return buffer[3];
}
[/code]

       faceAtPosition()函数物件上特定点所在面的序号,或者如果该点没有面则返回-1。决定这个值的代码在OpenGL中有点小复杂。本质上来说,我们所做的只是在GL_SELECT模式下渲染该场景以充分利用OpenGL的挑选功能,然后就可从OpenGL的命中记录中获取面的序号(它的“名字”)。

       下面是main.cpp:

[code type="cpp-qt"]
#include
#include
#include "tetrahedron.h"
using namespace std;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!QGLFormat::hasOpenGL()) {
cerr << "This system has no OpenGL support" << endl;
return 1;
}
Tetrahedron tetrahedron;
tetrahedron.setWindowTitle(QObject::tr("Tetrahedron"));
tetrahedron.resize(300, 300);
tetrahedron.show();
return app.exec();
}
[/code]

       如果用户的系统不支持OpenGL,我们向控制台打印一个错误消息并立即返回。
       要把该程序与QtOpenGL模块和系统的OpenGL库链接,.pro文件中需要下面的一条:
QT += opengl

       该四面体程序就完成了。为了更多关于QtOpenGL模块的信息,请看手册中QGLWidget, QGLFormat, QGLContext, QGLColormap, 和 QGLPixelBuffer的文档。




原文: http://qtchina.tk/?q=node/242

Powered by zexport