因为OpenGL只是一个标准/规范,具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。所以任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。取得地址的方法因平台而异,在Windows上会是类似这样:
// 定义函数原型
typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*);
// 找到正确的函数并赋值给函数指针
GL_GENBUFFERS glGenBuffers = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");
// 现在函数可以被正常调用了
GLuint buffer;
glGenBuffers(1, &buffer);
你可以看到代码非常复杂,而且很繁琐,我们需要对每个可能使用的函数都要重复这个过程。幸运的是,有些库能简化此过程,其中GLAD是目前最新,也是最流行的库。另外还可以选择glew。
先创建一个临时窗口,初始下glew,wgl***ARB类似的函数都需要glew初始化才能调用,用OGL3.3及以上的版本就需要wgl***ARB这样的函数去初始化opengl一些东西。类似如下:
bool OpenGLObject::InitGlew(CWnd *window)
{
//创建临时窗口,只是为了初始化glew库
PIXELFORMATDESCRIPTOR pfd;
CWnd* tempWnd = new CWnd();
tempWnd->Create(NULL, NULL, WS_CHILD | WS_VISIBLE, CRect(0, 0, 20, 20), window, NULL);
HDC temHdc = tempWnd->GetDC()->GetSafeHdc();
//HDC temHdc = m_WindowDC;
//尝试设置像素格式
if (!SetPixelFormat(temHdc, 1, &pfd))//每个窗口只能设置一次
return false;
HGLRC temphRC = wglCreateContext(temHdc);//创建一个临时的环境为了初始化glew,初始化后才能够使用wglChoosePixelFormatARB,wglCreateContextAttribsARB函数
wglMakeCurrent(temHdc, temphRC);//只有设置当前opengl环境后才能够初始化glew库
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
MessageBox(NULL, L"GLEW初始化失败!", L"提示", MB_ICONERROR);
return false;
}
if (!WGLEW_ARB_create_context || !WGLEW_ARB_pixel_format)
{
return false;
}
wglMakeCurrent(NULL, NULL);
wglDeleteContext(temphRC);
tempWnd->DestroyWindow();//销毁临时窗口
delete tempWnd;
return true;
}
windows上OpenGL上下文就是HGLRC,对应m_hRc
bool OpenGLObject::SetupPixelFormat()
{
PIXELFORMATDESCRIPTOR pfd;
int nPixelFormat = -1;
int nPixCount = 0;
float fPixAttribs[] = { 0, 0 };
int iPixAttribs[] = {
WGL_SUPPORT_OPENGL_ARB, 1,//支持OPENGL渲染
WGL_DRAW_TO_WINDOW_ARB, 1,//像素格式可以运行到窗口
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,//让HW加速
WGL_COLOR_BITS_ARB, 24,//R,G,B每种颜色8bit
WGL_DEPTH_BITS_ARB, 24,//24位深度
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,//双缓冲上下文
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,//pf should be RGBA type
WGL_STENCIL_BITS_ARB, 8,//模版缓冲区8bit
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,//开启多重采样
WGL_SAMPLES_ARB, 4,//多重采样样本为4
0
};
//新的查询像素格式的函数
wglChoosePixelFormatARB(m_WindowDC, iPixAttribs, fPixAttribs, 1, &nPixelFormat, (UINT*)&nPixCount);
//多重采样时,如果硬件不支持就使用下面的代码关闭多重采样
if (nPixelFormat == -1)
{
//try again without MSAA
iPixAttribs[18] = 1;
wglChoosePixelFormatARB(m_WindowDC, iPixAttribs, fPixAttribs, 1, &nPixelFormat, (UINT*)&nPixCount);
}
//获取一个像素格式,现在设置它为当前的上下文
if (!SetPixelFormat(m_WindowDC, nPixelFormat, &pfd))
{
MessageBox(NULL, L"设置像素格式失败!", L"提示", MB_ICONERROR);
return false;
}
GLint attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,//主版本3
WGL_CONTEXT_MINOR_VERSION_ARB, 3,//次版本3
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,//要求返回兼容模式环境,如果不指定或指定为WGL_CONTEXT_CORE_PROFILE_BIT_ARB会返回只包含核心功能的环境
0
};
m_hRc = wglCreateContextAttribsARB(m_WindowDC, 0, attribs);
if (m_hRc == NULL)
{
MessageBox(NULL, L"不能创建OGL3.3 context\n尝试创建3.2?", L"提示", MB_OK);
attribs[3] = 2;
m_hRc = wglCreateContextAttribsARB(m_WindowDC, 0, attribs);
if (m_hRc == NULL)
{
MessageBox(NULL, L"opengl3.2 create failed!", L"提示", MB_OK);
return false;
}
}
wglMakeCurrent(NULL, NULL);
return true;
}
wglMakeCurrent(HDC,HGLRC);
绑定后调用的GL函数才会作用与当前HDC。