Monday, August 01, 2011

QGLWidget 使用 paintEvent 与 paintGL 的区别

QGLWidget 提供了 OpenGL 的封装,通常我们不必写独立的 paintEvent 而只需要重写 paintGL 事件即可。如果需要利用 QPainter 实现基本的 over painting(在 OpenGL 场景上绘制独立的 2D 图案或者文字),我们就有必要在 paintEvent 里面进行修改而不是改 paintGL。

那么应该如何修改 paintEvent 呢(和修改 paintGL 的区别)?我发现主要有下面几点:
  • paintEvent 里面必须创建 QPainter 对象,不论画或者不画 2D 的东西,否则结果不对;我曾很天真的认为如果我不画 2D 的,就没必要创建 QPainter,这是完全不对的。
  • paintEvent 里面需要完全依照先 glPushMatrix 最后 glPopMatrix 的逻辑来写,还需要手工设置 viewport;之后才能利用 QPainter 画图;如果直接写 paintGL 是可以偷懒的;
  • 我原先因为偷懒所以 selection 部分就没有弄完整的画图动作,这在 paintEvent 实现里面是不行的,必须全部实现 OK;

Saturday, October 23, 2010

OpenGL 的工作流程

上图为 OpenGL Programming Guide 中的图片,特此说明。

我们需要理解 OpenGL 的工作机制。OpenGL本身仅仅支持最原始点、线段和平面的绘制,因此多数情况下我们都是准备好 vertex 数据,这些或者组织成为 display list 提交给显卡的 evaluator,这个 evaluator 将点信息转换成为需要描述的面的信息(如法向量等,这个功能在创建复杂的几何形体如曲面等时会经常用到);之后通过设定的 per-vertex 操作,如进行几何变换,计算纹理位置,光源、材料等等;然后是 assemble,获得显示到窗口的投影坐标。这之后就需要将这些计算出来的数据进行 rasterization(像素化,栅格化),同时将纹理信息加入到这些面上,进一步就可以做 fragment 操作(如根据深度去掉被遮挡的、又或者进行 blending 等,一般每种操作对应某种 buffer),最后将结果放到某个 buffer 中(可能是用于显示的,也可能是用于 swap 的)。而一般外部的位图数据,一般会首先经过处理(如放缩、平移、映射等)形成纹理。

编写 OpenGL 的程序要理解清楚,我们所绘制的对象是一个所谓的 OpenGL context,这个 context 里面包括了所有打开的 OpenGL 使用的 buffer 等对象,我们做图时往往需要指定一个 context,做图的过程就是将这个 context 的状态不停的改变,比如我们希望改变渲染的对象,我们会在 GL_RENDER 下切换到矩阵的 GL_MODELVIEW 里面,而如果只是改变投影我们是在 GL_PROJECTION 里面;如果我们需要选择所画的某些对象,我们会切换到 GL_SELECT 这种模式,并通过 GL_PROJECTION 设定我们观看的区域,然后切换回 GL_MODELVIEW 重新绘制,退回 GL_RENDER 的时候就会获得一个被选择的对象列表。所以我们需要搞清楚有些什么样的状态,对应状态下可以进行一些什么操作。

推荐的文档有两个,一个是 OpenGL 红皮书(红宝书?)即 OpenGL Programming Guide,现在已经到第 7 版了,非常经典的书,现在也开始涉及到 OpenGL 3.0 的东西,最新的 4.1 尚未涉及;4.1 相关的主要是 NVIDIA 在推动;这本书网上有电子版,中文版只能说翻译质量一般;另一个自然就是官方的 spec,网上可以免费下载(见这里)。

另外,我们需要理解清楚下面几个概念。
  • OpenGL 与 WGLAGLGLX 的关系:OpenGL 本质上和窗口系统无关,因此如果需要在某些窗体环境中使用 OpenGL 需要这些 GUI 提供某种 OpenGL 扩展,WGL 是 Windows 的扩展,AGL(另外还有 CGL、Cocoa 的 NSOpenGL 类)是苹果的扩展,而 GLX 是 X11 扩展(并且允许 network-transparent 的 OpenGL 渲染)。
  • OpenGL 与 GLU 和 GLUT 之间的关系:前面说了 OpenGL 本身仅仅提供了非常原始的操作;GLU 另外提供了层次纹理(mipmapping)、简单的矩阵操作(更加方便)、polygon tessellation(将曲面分解成为多边形)、二次曲面以及 NURBS 的支持;GLUT 提供的是接口统一跨平台的 GUI 接口。
后面我们会学习红皮书和 GLU 方面的东西。