gl3w解析

我在学习《OpenGL编程指南》第9版,第一个例子程序,是画2个三角形triangs,里面用到了gl3w。这里就来解析一下,这个gl3w。

gl3w 简介

gl3w是用来加载OpenGL核心模式(core profile)的。所谓核心模式,就是OpenGL编写shader程序,需要使用的那些函数, gl3w就是用来加载这些函数的。可以理解成gl3w就是用来加载OpenGL 3.0以后的函数的。

gl3w 组成

gl3w,就2个文件组成,一个gl3w.h和一个gl3w.c文件。实际使用的时候,可以把gl3w.c编译到应用程序中,也可以自己单独将gl3w封装成库,供应用程序使用。

gl3w工作方式

gl3w,就3个API接口,gl3wInit, gl3wIsSupported,gl3wGetProcAddress。我们使用的时候,一般直接调用gl3wInit就行了。

int gl3wInit(void);
int gl3wIsSupported(int major, int minor);
GL3WglProc gl3wGetProcAddress(const char *proc);

gl3wInit源码解析

int gl3wInit(void)
{
	open_libgl();
	load_procs();
	close_libgl();
	return parse_version();
}

gl3wInit,主要分成4个步骤:

  1. open_libgl, 打开libgl
  2. load_procs, 加载opengl函数
  3. close_libgl,关闭libgl
  4. parse_version, 解析版本

open_libgl

先来看open_libgl。

static void *libgl;
static void open_libgl(void)
{
	libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_GLOBAL);
}

open_libgl,主要是通过dlopen,来打开libGL.so.1。

load_procs

再看load_procs函数。

static void load_procs(void)
{
	gl3wCullFace = (PFNGLCULLFACEPROC) get_proc("glCullFace");
	gl3wFrontFace = (PFNGLFRONTFACEPROC) get_proc("glFrontFace");
	gl3wHint = (PFNGLHINTPROC) get_proc("glHint");
	//...
}

load_procs函数,通过get_proc,获取opengl的函数地址,上面列出了获取glCullFace函数,glFrontFace, glHint函数地址,后面还有很多,总之就是把所有的opengl函数地址都获取到。
来看看是如何获取opengl函数地址的,也就是get_proc函数。

static GL3WglProc get_proc(const char *proc)
{
	GL3WglProc res;

	res = (GL3WglProc) glXGetProcAddress((const GLubyte *) proc);
	if (!res)
		res = (GL3WglProc) dlsym(libgl, proc);
	return res;
}

这个,先通过glXGetProcAddress,来获取函数地址,加入没有获取到,再通过dlsym去libGL.so.1里面找函数地址。这里,假设const char* proc = “glEnable”,那么就会先通过glXGetProcAddress去获取glEnable的函数地址,要是没找到,再去libGL.so.1里面,去找glEnable的函数地址。

找到的函数地址,存放在了gl3wCullFace这个函数指针中。gl3wCullFace的定义如下:

PFNGLCULLFACEPROC gl3wCullFace;
PFNGLFRONTFACEPROC gl3wFrontFace;
PFNGLHINTPROC gl3wHint;

而类型PFNGLCULLFACEPROC的定义如下

typedef void (APIENTRYP PFNGLCULLFACEPROC) (GLenum mode);
typedef void (APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode);
typedef void (APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode);

也就是说,执行完load_procs之后,opengl的函数地址,全都保存在了gl3w.c对应的全局函数指针
gl3wCullFace, gl3wFrontFace, gl3wHint等里面。

close_libgl

static void close_libgl(void)
{
	dlclose(libgl);
}

close_libgl的代码很简单,通过dlopen关闭了libgl,这个libgl就是我们在open_libgl里面打开的动态库的句柄。

parse_version

最后再看parse_version。

static int parse_version(void)
{
	if (!glGetIntegerv)
		return -1;

	glGetIntegerv(GL_MAJOR_VERSION, &version.major);
	glGetIntegerv(GL_MINOR_VERSION, &version.minor);

	if (version.major < 3)
		return -1;
	return 0;
}

这里,调用glGetIntegerv获取opengl的主版本GL_MAJOR_VERSION, opengl副版本GL_MINOR_VERSION,然后判断主版本version.major是否小于3,加入小于3的话,返回-1,表示gl3wInit失败了。否则就返回0,表示成功。

gl3w.h解析

那么这样,只是把opengl的函数地址,获取到,保存在一堆的函数指针里面,我们实际使用的时候,怎么调用的呢?
这个,其实是在gl3w.h里面。
我们应用程序使用gl3wInit的时候,需要包含头文件 GL3/gl3w.h头文件。在这个头文件中,通过宏定义,把opengl函数,替换成了我们刚刚的函数指针。这个就是gl3w的工作方式。

#define glCullFace              gl3wCullFace
#define glFrontFace             gl3wFrontFace
#define glHint          gl3wHint

源码总结

通过上述源码分析,就知道,在Linux系统上,gl3wInit,就是先从libGLX.so里面,通过glXGetProcAddress获取glX支持的opengl函数地址,假如没找到函数地址的,再从libGL.so.1里面获取函数地址。

获取到的函数地址,保存在 gl3wCullFace等这样的函数指针中。
当用户#include <GL3/gl3w.h>时,会把glCullFace这个opengl函数的调用,替换成对gl3wCullFace的调用,这样实际就调用了gl3wCullFace指针指向的函数地址,这个函数地址,要么是从glX里面通过glXGetProcAddress获得的,要么是从libGL.so1里面,通过dlsym找到的。

假如opengl版本低于3.0的话,gl3wInit就是返回-1。

这样,gl3w,就可以整合glX和gl的opengl函数,优先使用glX的函数,glX没有实现的函数,就使用GL的函数。从而完成了glX和GL库的功能整合,最后对用户提供一个统一的gl接口。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值