文章目录
简介
本文将介绍三维图形渲染框架OSG(Open Scene Graph)是如何维护OpenGL渲染状态的?通过阅读本文,您将深刻理解OSG场景渲染原理中OpenGL渲染状态管理这一块的技术。
正文
阅读本文的技术前提
OSG框架作为一个开源的三维图形渲染框架,在框架的最底层和其它的三维图形渲染框架一样,也是使用了OpenGL等低级图形渲染引擎的功能。正因为如此,如果想很好的理解本文所描述的概念和源码,可能需要读者提前掌握一定的图形学理论基础知识和一定的OpenGL编程技能。本文作者假定阅读本文的读者朋友们已经满足这一个基础条件,所以本文将不会细化阐释图形学技术概念和OpenGL API。
比如为了节省篇幅和集中关注点,本文将不会介绍Windows窗口编程的一些基础技术概念和基础数据类型,比如窗口句柄(HWND)、设备上下文句柄(HDC)等。本文也不会介绍OpenGL API和GLSL这种着色器编程语言。
为了提升读者朋友对OSG框架源码的理解的效率,也为了方便讲解和节省篇幅,本文在引用OSG框架的源码时,可能会对原始源码做一些必要的删减操作和调整操作,使得所展示的源码看起来更为清晰明了。
OSG如何初始化OpenGL上下文?
OSG中的OpenGL上下文包装类
OpenGL上下文实际上是OpenGL渲染上下文。OpenGL渲染上下文可以理解为一个OpenGL使用的有限状态机,执行各种OpenGL API时修改这个状态机中的状态。
The venerable Red Book says that “OpenGL is a state machine”.
备注:
(a)这句话来自OSG源码中State文件的源码注释。Red Book指的是<<The OpenGL® Programming Guide 9th Edition>>,对应的中文版<<OpenGL编程指南(第9版)>>已经由中国机械出版社出版。
(b)如果想获取Red Book的示例源码,可以访问网站:
[OpenGL Red Book](The OpenGL® Programming Guide 9th Edition)
OpenGL API提供了OpenGL上下文对象对应的句柄类型(HGLRC)。在OSG中为各种操作系统提供了独立的OpenGLContext类型用于表达OpenGL上下文这个概念相关的各种状态。OSG框架之所以会有不同版本的OpenGL上下文的包装类数据类型,是因为这个上下文是跟具体操作系统有关的。OpenGL API中尽管提供了上下文句柄这个类型,但是并没有提供一套统一的创建OpenGL上下文对象的API来抹平不同操作系统之间这个创建操作的差异,因此各种使用OpenGL作为底层渲染引擎的开源图形学框架都必须自行实现OpenGL跨平台的特性。
Windows版本实现
OSG框架中的OpenGL上下文包装类随操作系统不同而有所不同,Windows版本中的包装类是OpenGLContext类型,Linux X11版本中的包装类是GraphicsHandleX11类型。由于作者精力和技术水平有限,本文暂时只介绍OSG Windows版本实现。
OSG的源码中提供了对应的Windows版本的OpenGLContext类型来表达OpenGL上下文这个技术概念。在Windows系统中,OpenGL上下文又是依赖于Windows系统中的窗口句柄(HWND)、设备上下文句柄(HDC)等数据类型的。因此OpenGLContext类型实际上就是将这些句柄都包装了到一个C++类型了。Windows中提供了创建OpenGL上下文所需用到的Windows API。
class OpenGLContext
{
public:
//构造OSG的OpenGL上下文包装类对象
OpenGLContext();
OpenGLContext(HWND hwnd, HDC hdc, HGLRC hglrc);
~OpenGLContext();
//设定上下文包装对象中OS相关的一些句柄
void set(HWND hwnd, HDC hdc, HGLRC hglrc);
void clear();
HDC deviceContext();
//使得这个C++对象所管理的上下文句柄对应的上下文对象作为OpenGL当前上下文对象
bool makeCurrent(HDC restoreOnHdc, bool restorePreviousOnExit);
protected:
HDC _previousHdc; // previously HDC to restore rendering context on
HGLRC _previousHglrc; // previously current rendering context
HWND _hwnd; // 窗口句柄
HDC _hdc; // 设备上下文句柄
HGLRC _hglrc; // OpenGL渲染上下文句柄(简称:OpenGL上下文)
bool _restorePreviousOnExit; // 标记:是否在退出时恢复前一个上下文
};
Windows应用程序中初始化OpenGL上下文的基本步骤是这样的:
- 创建窗口
- 初始化像素格式
- 创建OpenGL上下文
- 选择像素格式
- 设置像素格式
- 启用OpenGL当前上下文
完成上述操作在OSG框架中对应的源码如下所示:
源码文件:GraphicsWindowWin32.cpp
//获取到OpenGL上下文
bool Win32WindowingSystem::getSampleOpenGLContext( OpenGLContext& context, HDC windowHDC, int windowOriginX, int windowOriginY )
{
//清除各种句柄属性
context.clear();
//注册Windows窗口类别
registerWindowClasses();
//创建Windows窗口
HWND hwnd = ::CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
osgGraphicsWindowWithoutCursorClass.c_str(),
NULL,
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED,
windowOriginX,
windowOriginY,
1,
1,
NULL,
NULL,
::GetModuleHandle(NULL),
NULL);
//思考:
//问题:为什么这里窗口大小可以设定为1X1这么小的窗口呢?
//答案:现在这个窗口的作用只是用来获取窗口对应的设备上下文句柄(HDC)。
//窗口大小跟后续创建OpenGL上下文没关系。
//像素格式描述符:这个类型成员变量比较多,稍后给出了参考资料
//这里简略的介绍一下什么是像素格式?
//像素格式描述图形设备中是如何存储一个像素的颜色数据及其相关信息的
PIXELFORMATDESCRIPTOR pixelFormat =
{
sizeof(PIXELFORMATDESCRIPTOR), //结构体的大小
1, //版本
//标记:使用OpenGL支持在线渲染到窗口或设备表面
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL,
//像素类型:四个颜色分量
PFD_TYPE_RGBA,
24, //颜色:为24bits。
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
24, //深度:为24bits。
0,
0,
//忽略
PFD_MAIN_PLANE,
0,
0, 0, 0
};
//获取窗口设备上下文。
//使用这个设备上下文可以在整个窗口上绘图,包括客户去和非客户区
HDC hdc = ::<