我正在尝试使用OpenGL创建我自己设计的3D环境模拟器,但是我的设计要求我能够直接控制运行时循环。
我正在尝试学习如何在OpenGL中实现VBO,但我很难找到足够的资源。根据我的发现,几乎每个人都以某种方式使用FreeGLUT或GLEW或GLFW或GL3.h来解决一些特定于平台的问题。
虽然这些选项可能很棒,但我根本不想将其他人的包用于此代码。我想弄清楚如何使用大多数机器上开箱即用的标准的vanilla OpenGL来使用VBO。我可以使用一些帮助来寻找OpenGL中不使用任何外部软件包的VBO教程。
......在有人建议之前,关于VBO的NeHe第45课使用的折旧代码(glaux)甚至不再编译,所以这不是一个选择。 :\
编辑:也许我不清楚,或者我被控制,无论如何......问题的关键在于找到示例,教程或其他资源来教授OpenGL中的VBO 不要依赖这些额外的包装。认真的人,告诉我“放弃”或“只使用GLEW”......这没有帮助。
答案 0 :(得分:7)
当你使用GLUT或GLFW时,使用VBO的方式并没有什么不同,如果你手动加载函数指针并不重要(为什么你会首先这样做?那是受虐狂,是的,它可以完成......但你为什么要这么做?)或使用像GLEW这样的包装。
我认为你需要你的概念正确GLUT,GLFW和GLEW实际上是什么。
GLUT(旧的,免费的......和Open ...)和GLFW是框架:它们创建一个窗口,为连接OpenGL上下文做准备,创建一个OpenGL上下文,然后给予控制权(或多或少)。 GLUT接管事件循环。 GLFW要求您实现事件循环。您当然可以自己完成整个设置。但是如果你这样做,那么当涉及到底层操作和图形系统的低级API时,你需要坚定地站稳脚跟。更好地使用框架。你不仅限于GLUT或GLFW,还有其他:Qt,GTK +,SDL,SMFL,FLTK,wxWidgets,Fox Toolkit等等。
GLEW是一个扩展和函数指针加载器库,负责(真正)令人毛骨悚然的细节。 opengl32.dll或libGL.so只公开了OpenGL的更高版本提供的所有函数的一小部分。必须在运行时动态加载所有高级内容。这就是GLEW的用途,因为手动操作真的很糟糕。
没有 它们与VBO完全有关!如果你这么想,你就会有一些严重的误解。
那么如何手动加载扩展。嗯,这真的很讨厌。首先,您必须在源代码中定义所需的函数指针。为了帮助您,ARB发布了一个名为glext.h
的头文件。在这里你可以找到像
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer);
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, GLvoid* *params);
那些为函数指针提供类型。因此,您将glext.h
包含在扩展加载程序模块中
#include "myextensionloader.h"
然后,对于您打算使用的每个函数,您都可以定义一个函数指针变量。但重要的是:这些变量是程序的一部分,因此必须将它们限制在自己的命名空间中;你不能只使用可能存在的名称。因此,它们不会被称为gl…
,而是PROGRAMNAME_PRIVATE_gl…
。
PFNBINDBUFFERPROC myprogram_PRIVATE_glBindBuffer;
PFNDELETEBUFFERPROC myprogram_PRIVATE_glDeleteBuffer;
......等等。你真的想用OpenGL的后续版本的大量函数来做到这一点吗?
实际上,对于Windows的情况来说,这甚至都不完全正确,因为OpenGL函数指针与OpenGL上下文活动相关联。不同线程中的多个OpenGL上下文同时处于活动状态=>灾难的秘诀。那些函数指针变量实际上属于线程本地存储。
然后添加一个函数指针加载器,对于每个需要加载它的OpenGL函数
void load_extension_function_pointers()
{
#ifdef USE_WGL
#define GETGLPROCADDRESS(name) (wglGetProcAddress(name))
#else ifdef USE_GLX
#define GETGLPROCADDRESS(name) (glXGetProcAddress(name))
#endif
#define LOAD_POINTER(name) myprogram_PRIVATE_##name = GETGLPROCADDRESS(#name)
LOAD_POINTER(glBindBuffer)
LOAD_POINTER(glDeleteBuffer)
}
无论如何,现在您想将其提供给其他模块。所以你有一个头文件myextensionloader.h
,你用它来包含glext.h
,提供你的函数指针变量作为externs到你的程序的其余部分,并有一大堆C预处理器宏,可以为你的私有命名函数名称为OpenGL函数名称:
#pragma once
#ifndef MYEXTENSIONLOADER_H
#define MYEXTENSIONLOADER_H
#include <GL/gl.h>
#include <GL/glext.h>
extern PFNBINDBUFFERPROC myprogram_PRIVATE_glBindBuffer;
#define glBindBuffer myprogram_PRIVATE_glBindBuffer;
extern PFNDELETEBUFFERPROC myprogram_PRIVATE_glDeleteBuffer;
#define glDeleteBuffer myprogram_PRIVATE_glDeleteBuffer;
这个方案仍然缺少一些细节:在Windows中,函数指针实际上与活动的OpenGL上下文相关联,因此从技术上讲,您需要以某种方式覆盖wglMakeCurrent以在切换上下文时更新所有函数指针。并且由于不同的OpenGL上下文可以在不同的线程中处于活动状态,因此这些函数指针实际上应该在线程本地存储(TLS)中才是正确的。 GLX更加理智,但你可能仍然在不同GPU供应商的多台X服务器上运行,所以你要应对不同的libGL.so和libGLX.so以及那些可能会让你感到困扰的东西。
你真的想要自己照顾这一切吗?如果您是该代码的唯一用户,为什么?真的是为什么?写这一切的过程非常繁琐,以至于GLEW的维护者从未写过任何实际的GLEW库。他们所做的是编写一个代码生成器,自动从规范文件中生成库源代码。