Saturday, October 16, 2010

billboard 的 OpenGL 实现

原文见这里

基本想法是两个:

要不我把当前的 MODELVIEW 矩阵拿出来,把旋转去掉,这只需要把那个矩阵的 3x3 部分变成单位阵就行了,因为把矩阵理解成为对当前点的变换可以发现我们仅仅需要平移到当前点,然后画需要的 billboard 就好了。

要不我把当前的 MODELVIEW 矩阵拿出来,3x3 那部分里面每行相当于是在 x、y、z 上的投影,因此只需要取前两行作为方向计算该处物体的坐标就行了。

一般 billboard 都是矩形,因此计算量不算很大,后者需要用 CPU 计算所有的 billboard 坐标,如果 billboard 较少比较适合;前者实际上需要将矩阵从显存放到内存,然后写回去,如果 billboard 计算量太小则不太适合。

Monday, October 11, 2010

Qt 的 OpenGL 支持

首先需要知道 OpenGL 是一个渲染 3D 图像的接口,其定义与语言几乎无关。我们这里直接在 C/C++ 里面使用对应的 OpenGL 语言。说到头,OpenGL 只是渲染一个图像,因此很多做图的原语被反复的使用。我们知道的 Mesa 本质上是实现了一个 CPU 版本(以及少量显卡硬件支持)的 OpenGL。为什么需要 OpenGL 呢?如果每个显卡都有自己的一套渲染方案,但提供给用户的 API 不同,则每个图形应用程序都需要针对特定的显卡进行编写。而如果显卡提供了一致的标准,编写程序的时候则会轻松很多。但是这要求显卡提供商实现两部分内容。一部分是驱动程序,被 OS 隐藏起来,用于直接与硬件打交道;另一部分是用来向 OS 提交请求的用户 API 接口。这样显卡提供商一方面需要尽可能的通过自己显卡的硬件环境优化 OpenGL 的渲染实现,也需要通过 marshalling 减少从用户态转换到内核态的次数(避免每次 OpenGL 调用都 involve 一次转换)。这样将一些 rendering 的工作(特别是大量 3D 渲染)从 CPU 转移到 GPU 之后就能加速应用程序。

第二,Qt 对 OpenGL 的支持在于它提供了一个用于显示 OpenGL 渲染结果的 widget,即 QGLWidget,如果我们需要显示 OpenGL 的渲染结果,我们就应该继承 QGLWidget,然后实现它三个方法:initializeGL() 用于初始化 OpenGL,通常设置一些前期的背景色、光照等等;paintGL() 用于渲染整个场景;resizeGL() 用于在 widget 大小变化的时候产生合理的 view。

第三,如果需要设置动画效果,本质上是在 paintGL() 过程中加入某些变化的参数,这样每次调用 paintGL() 的时候产生不同的渲染结果。实现动画需要调用 QTimer 之类的定时器,而这些对象并不能访问前面的这些方法(因为是 protected 的),因此一般调用含有 updateGL() 方法的 slot,将 QTimer 的某些 signal 与之 connect 即可。

第四,如果需要加入鼠标和键盘的操作,应该设置 mousePressEvent()、mouseMoveEvent() 等事件,将需要的方法绑定到对应的事件中。

总而言之,OpenGL 的使用并不神秘,只是我们需要进一步理解其中的一些基本概念。下面是一个简单的 OpenGL 程序,可以看到前面第二点里面所说的三个要素,我们在后面的分析中会逐步加深。下面是继承 QGLWidget 类的声明
#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QGLWidget>
#include <QVector>
#include <gl.h>

class GLWidget : public QGLWidget {
 public:
  GLWidget( QWidget* = NULL, const QGLWidget* = NULL,
            Qt::WindowFlags = Qt::Widget ) ;

 protected:
  void initializeGL();
  void paintGL();
  void resizeGL(int width, int height);

 private:
  QVector<GLuint> stars ;
} ;

#endif
下面是相关实现,
#include "glwidget.h"

GLWidget::GLWidget( QWidget* parent, const QGLWidget* shared,
                    Qt::WindowFlags flags)
  : QGLWidget( parent, shared, flags ), stars(1) {
}

void
GLWidget::initializeGL() {
  QGLWidget::initializeGL() ;
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glClearDepth( 1.0 ) ;
}

void
GLWidget::paintGL() {
  glClearColor( 0.0, 0.0, 0.0, 0.0 ) ;
  glClear (GL_COLOR_BUFFER_BIT);

  glLoadIdentity() ;
  glColor3f (1.0, 1.0, 1.0);
  glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
  glBegin(GL_POLYGON);
    glVertex3f( 0.25, 0.25, .0 ) ; 
    glVertex3f( 0.75, 0.25, .0 ) ; 
    glVertex3f( 0.75, 0.75, .0 ) ; 
    glVertex3f( 0.25, 0.75, .0 ) ;
  glEnd();
  glFlush();
}

void
GLWidget::resizeGL(int w, int h ) {
  glViewport( 0, 0, w, h ) ;
  glMatrixMode( GL_PROJECTION ) ;
  glLoadIdentity() ;
}
我想后面我们应该会更关注 OpenGL 本身,而不仅仅在 Qt 上。