OpenGL非常机器特定。

7

最近我开始在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;
    }
    

2
我怀疑如果没有一个可重现的简短示例,这个问题可能非常困难,甚至是不可能回答的。 - R. Martinho Fernandes
每个测试都在Windows Vista或7上完成。我为每个API调用添加了一个OpenGL错误检查,但似乎没有捕获任何错误。我无法在自己的机器上复制它,但在其他机器上进行了更多跟踪后,发现它直到渲染时才会崩溃。设置正常工作,完美地创建了所有缓冲区和对象,但是一旦尝试渲染网格(VAO),它就会崩溃而没有任何错误(除了.exe已停止工作)。我怀疑命令glUseProgram或glDrawElements。 - Invalid
你是否在VAO中“封装”了你的GL_ELEMENT_ARRAY_BUFFER绑定? - rotoglup
@rotoglup,看到问题了,已经添加了所有的渲染代码,如果你需要其他任何东西,就大声喊出来。 - Invalid
最后一件事,你的问题标题与你的问题关系不大。你可能会发现,为了吸引更多的注意力,下一个问题的标题更具体、更紧密地与你所提出的问题相关会更有帮助。 - rotoglup
显示剩余3条评论
1个回答

5
我猜你是通过VAO绑定了GL_ELEMENT_ARRAY_BUFFER。
我曾经在几年前的某些版本的NVidia驱动程序中遇到过这个问题,会导致应用程序崩溃。但我没有记录哪些版本有这个问题。
当时我的解决方法是依靠VAO设置GL_ARRAY_BUFFER绑定和顶点属性指针,然后在glBindVertexArray之后显式重新定义GL_ELEMENT_ARRAY_BUFFER绑定。
我认为这个问题现在已经得到解决,但是一些老的驱动程序可能仍然存在这个问题。
你能看出哪些平台的配置会出现这个问题吗?

那真的解决了问题,感谢你的帮助!到目前为止,我只有在一台机器上测试过它,而在这台机器上之前它并没有工作,正如你所说,它使用了Nvidia显卡,只要我拿到其他机器,我就会在那里尝试。确切来说:http://www.geforce.com/hardware/notebook-gpus/geforce-gt-540m/specifications - Invalid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接