最近我开始在C++中使用OpenGL进行3D渲染,但奇怪的是它似乎只能在一些机器上工作。我正在使用OpenGL 3.0或更高版本(目前设置为3.0),使用顶点数组对象(不确定如何称呼,但是API版本已经移除了glBegin、glEnd等函数)。
在测试其他机器之前,我总是询问它们的GPU驱动程序,大多数支持OpenGL 4.2,如果不支持,我确保它们至少支持3.0。但在某些机器上,我的应用程序只是崩溃了,或者根本没有渲染任何内容,OpenGL没有抛出任何错误(我每帧至少检查一次错误)。它在我的机器上完全正常运行,而且在某些情况下,我的机器甚至比一些无法工作的机器更老。
我开始只使用OpenGL本身和GLEW,但为了方便使用和一些我喜欢的其他功能,我改用SFML和GLEW。然而OpenGL本身的行为并没有改变。我曾经重写过引擎,以封装所有GL调用并纳入顶点数组对象和其他一些功能,在事实上,我可以给你一个应用程序使用的所有API调用列表。显然,在测试应用程序中并不使用全部调用,但这些是引擎中我使用的所有调用(这是能够进行渲染的最低限度,因此引擎甚至没有完成):
- glAttachShader - 将着色器附加到程序对象
- glBindAttribLocation - 绑定通用顶点属性的索引到位置
- glBindBuffer - 绑定缓冲区对象
- glBindVertexArray - 绑定顶点数组对象
- glBufferData - 创建并初始化缓冲区对象的数据存储
- glBufferSubData - 更新部分缓冲区对象的数据存储
- glClear - 填充指定的缓冲区至清除值
- glClearColor - 设置用于清除颜色缓冲区的值
- glClearDepth - 设置用于清除深度缓冲区的值
- glCompileShader - 编译着色器对象
- glCreateProgram - 创建程序对象
- glCreateShader - 创建着色器对象
- glCullFace - 指定多边形正面或背面剔除
- glDeleteBuffers - 删除缓冲区对象
- glDeleteProgram - 删除程序对象
- glDeleteShader - 删除着色器对象
- glDeleteVertexArrays - 删除顶点数组对象
- glDepthFunc - 设置深度比较函数
- glDepthMask - 启用或禁用写入深度缓冲区
- glDepthRange - 指定深度值的映射范围
- glDisableVertexAttribArray - 禁用通用顶点属性数组
- glDrawElements - 用索引数组渲染图元
- glEnable - 启用OpenGL功能
- glEnableVertexAttribArray - 启用通用顶点属性数组
- glFrontFace - 指定多边形正面方向
- glGenBuffers - 创建缓冲区对象名称
- glGenVertexArrays - 创建顶点数组对象名称
- glGetAttribLocation - 返回通用顶点属性变量的位置
- glGetBufferParameteriv - 返回缓冲区对象参数
- glGetBufferSubData - 返回部分缓冲区对象的数据存储
- glGetError - 返回当前的OpenGL错误状态
- glGetIntegerv - 返回整数型或整数型向量参数
- glGetProgramInfoLog - 返回程序对象的信息日志
- glGetProgramiv - 返回程序对象参数
- glGetShaderInfoLog - 返回着色器对象的信息日志
- glGetShaderiv - 返回着色器对象参数
- glGetShaderSource - 返回着色器源代码字符串
- glGetUniformLocation - 返回统一变量的位置
- glIsProgram - 判断是否是程序对象
- glIsShader - 判断是否是着色器对象
- glLinkProgram - 链接程序对象
- glMapBufferRange - 映射缓冲区对象的数据存储,并返回指针
- glPixelStorei - 设置像素存储模式
- glShaderSource - 设置着色器源代码
- glUniform(1i, 1ui, 1f, 2f, 3f, 4f, Matrix3fv, Matrix4fv) - 设置统一变量的值
- glUnmapBuffer - 解除映射缓冲区对象的数据存储
- glUseProgram - 安装程序对象为当前渲染状态
- glVertexAttrib(1i, 1ui, 1f, 2f, 3f, 4f) - 设置通用顶点属性变量的值
- glVertexAttribPointer - 指定通用顶点属性数组的位置
每个测试都是在Windows Vista或7上完成的。我已经为每个API调用添加了一个OpenGL错误检查,并且没有发现任何错误。我无法在自己的机器上重现它,但在其他机器上进行了更多的跟踪后,我发现它直到渲染时才会崩溃。设置正常工作,完美地创建所有缓冲区和对象,但是一旦我尝试渲染网格(VAO),它就会崩溃,而没有任何错误(除了.exe已停止工作)。我怀疑是命令glUseProgram或glDrawElements导致的。
关于示例,除非您想搜索大约10个类,否则我无法给您一个简短的示例。
编辑,呈现对象的少量代码:
Mesh类向对象添加这些结构,以便知道要绘制什么:// Define Geometry (draw command) struct Geometry { // Primitives PrimitiveType primitiveType; // Indices IndexType indexType; unsigned int count; // elements unsigned int offset; // bytes };
顺便说一下,“几何标签”只是一个字符串,可以在其中放置多个绘图调用,以下是一些定义:
// Define a list of primitives typedef std::vector<Geometry> GeometryList; // Define Geometry ordered by tag typedef std::map<const std::string, GeometryList> GeometryMap;
对于每个“draw”调用,它会返回一个字符串,以便网格类可以绑定适当的材质。//----------------------------------------------------------------------- const std::string HardwareObject::nextGeometryTag() { // Loop back GeometryMap::const_iterator end = _geometry.end(); if(_activeGeometry == end) { // Bind and go to begin glBindVertexArray(_GL_VertexArray); _activeGeometry = _geometry.begin(); } // Check if new tag exists else if(++_activeGeometry == end) { // Unbind and return empty tag glBindVertexArray(0); return ""; } return _activeGeometry->first; } //----------------------------------------------------------------------- bool HardwareObject::drawGeometryTag() const { // Validate current tag if(_activeGeometry == _geometry.end()) return false; // Draw each geometry call of tag for(GeometryList::const_iterator it = _activeGeometry->second.begin(); it != _activeGeometry->second.end(); ++it) glDrawElements(it->primitiveType, it->count, it->indexType, (void*)it->offset); // GL Error return !Console::GET().getError("HardwareObject Drawing"); } //----------------------------------------------------------------------- void HardwareObject::resetGeometryTag() { _activeGeometry = _geometry.end(); }
编辑:网格调用以上所有方法来实际渲染对象
lockVertexAttributes() 只是确保所有属性指针绑定到正确的顶点缓冲区。HardwareProgram 的 bind 方法仅检查程序是否已编译并调用 glUseProgram。
//----------------------------------------------------------------------- bool Mesh::render() { // Lock vertex attributes if(!lockVertexAttributes()) return false; // To avoid errors _object.resetGeometryTag(); // Loop while there's a tag for(std::string tag = _object.nextGeometryTag(); tag != ""; tag = _object.nextGeometryTag()) { // Find material MaterialMap::const_iterator it = _materials.find(tag); if(it == _materials.end()) continue; // Bind material (get program directly) const HardwareProgram *prog = it->second->getProgram(); if(!prog) continue; if(!prog->bind()) continue; // Draw tag _object.drawGeometryTag(); } // Ok! return true; }