Qt 中的 3D 绘图

来源 http://hi.baidu.com/dem_simulation/item/a66e688c81b172da5e0ec1cd


  • OpenGL 是渲染 3D 图形的标准 API 函数库。

  • 在 Qt 应用程序中,可以使用 QtOpenGL 这个模块来实现 3D 图像的绘制。

  • QtOpenGL 模块对系统上的 OpenGL 库进行了封装,这个模块中提供一个 QGLWidget 类,任何由这个类派生的 widget 都能使用 OpenGL 命令绘制自己。对于许多的 3D 应用程序,这个类提供的功能已经相当足够了。

  • 同时,在 QGLWidget 中使用 QPainter,这样做很方便,一方面可以发挥 OpenGL 在三维渲染方面的强大性能,同时还可以具备 QPainter 提供的高效的 2D 图形 API。

 

1.   OpenGL 绘图

 

(1)将 3D 应用程序链接到 QtOpenGL 模块上,并加入 OpenGL 库,所以需要在 .pro 文件中加入:

 

   QT += opengl

 

(2)由 QGLWidget 派生一个类;

 

(3)重载并实现 QGLWidget 中的 initializeGL( ) ,resizeGL( ),paintGL( ) 这三个虚函数;

 

2.   函数实现

 

(1)构造函数

 

Tetrahedron::Tetrahedron(QWidget *parent)
    : QGLWidget(parent)
{
    setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
    ……
}

 

  • 在 3D 应用程序的构造函数中,使用 QGLWidget : : setFormat( ) 来指定 OpenGL 的 DC(display context)。其中,通常需要设置画面的双缓冲和深度缓存:

 

  • setFormat( QGLFormat( QGL : : DoubleBuffer | QGL : : DepthBuffer ) ) ;

 

(2)重载 initializeGL 函数

 

  • 函数的调用过程是:

  • initializeGL;                          // 只被调用一次

  • resizeGL;

  • paintGL;

  • 任何引起窗口重绘的地方:paintGL;

  • 任何引起尺寸变化的地方:resizeGL;

 

void Tetrahedron::initializeGL()
{
    qglClearColor(Qt::black);
    glShadeModel(GL_FLAT);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
}

 

  • 在 initializeGL 中,OpenGL 中的 glClearColor 函数被封装到:

 

  • qglClearColor( Color ) 

 

  • 如果使用这个函数,就可以调用 Qt 中预定义的颜色标识如: Qt : : yellow 等等;

  • 如果使用 glClearColor( ) 只只能使用 RGB 值的方式。

  • initializeGL 这个函数在调用 paintGL 之前首先被调用,并且只调用一次,这是初始化 OpenGL 的 RC (rendering context)、显示列表和其它初始化的地方,

 

(3)重载 resizeGL 函数

 

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);
}

 

  • 在 paintGL 被调用之前,在 initializeGL 被调用之后,resizeGL 也会被调用一次;同时在 widget 被 resized 之后,这个函数也会被调用。

  • 在这个函数中主要设置 OpenGL 的视口,视景体等物件。

 

(4)重载 paintGL 函数

 

void Tetrahedron::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    draw();
}

 

  • paintGL 则是在 widget 需要重绘时被调用,这个有点像是 QWidget : : paintEvent( ) 函数;

  • QGLWidget : : updateGL( ) 是虚 slot,调用时将直接引发 glDraw( ) 函数来更新 widget;

  • QGLWidget : : glDraw( ) 函数执行虚函数 paintGL( ) ;

  • QGLWidget : : paintGL( ) 函数是虚函数,如果 widget 需要重绘时,这个虚函数就会被调用;

  • 在绘制 opengl 三维图形时,使用 opengl 命令前不必使用 makeCurrent( ) 函数,因为这个函数已加入到了 paintGL( ) 函数中去了。

  • 其中,使用 glEnable( GL_MULTISAMPLE) 是允许开启“反走样”。

 

3.   三维图形与鼠标交互

 

一般的三维图形与鼠标的交互方法包括:

 

  • 左(右)键点击并移动鼠标时,三维图形绕轴旋转;

 

  • 中间滑轮滚动时,三维图形缩放。

 

这里主要需要重载三个事件:

 

(1)重载 void mousePressEventQMouseEvent* ) 事件

 

void Tetrahedron::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}

 

  • 通常用成员变量 QPoint,保存左键按下的那一点。

 

(2)重载 void mouseMoveEventQMouseEvent* ) 事件

 

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( ) ;                       // 如果是重载 paintEvent,则使用 update( ) ;

}

else if ( event->buttons( ) & Qt : : RightButton )

{

   rotationX+ = 180 * dy ;

   rotationZ+ = 180 * dx ;

   updateGL( ) ;                      // 如果是重载 paintEvent,则使用 update( ) ;

}

lastPos = event->pos( ) ; 

 

  • 主要是将鼠标的移动距离转化成角度变化(成员变量保存),同时更新 lastPos;

  • 这里的 rotationX,rotationY,rotationZ 分别将在 MODELVIEW 的 glRotatef() 中使用。

 

(3)重载 void wheelEventQWheelEvent * ) 事件

 

double numDegrees = -event->delta( ) / 8.0 ;

double numSteps = numDegrees / 15.0 ;

scaling *= std : : pow( 1.125 , numSteps ) ;

updateGL( ) ;                                               // 如果是重载 paintEvent,则使用 update( ) ;

 

  • 将滑轮滚动转换成放大倍数;

  • event( )->delta 返回滑轮滚动的距离,正值表示向前滑动,负值表示向后滑动;

  • 大多数的鼠标是以滑动 15 度为一步,通常这一步就是 1/8 的 delta( );

  • 这里的 scaling 是一个浮点数,MODELVIEW矩阵中,画图之前进行的矩阵变换中使用:glScalef( scaling, scaling, scaling )。

 

4.   屏幕拾取

 

  • 首先需要鼠标点击的点:event->pos( ) 。然后在 GL_SELECT 模式下渲染 OpenGL 场景,这样就能利用 OpenGL 提供的拾取的能力。

  • 关于 OpenGL 中的拾取模式,详细内容参见:

 

http://blog.csdn.net/zsc09_leaf/article/details/6785472

 

示例代码如下:

 

int Tetrahedron::faceAtPosition(const QPoint &pos)
{
    const int MaxSize = 512;
    GLuint buffer[MaxSize];
    GLint viewport[4];

    makeCurrent();

    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];
}

 


  • 0
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值