【问题标题】:Displaying a simple triangle using OpenGL and SFML使用 OpenGL 和 SFML 显示一个简单的三角形
【发布时间】:2015-10-19 22:22:13
【问题描述】:

我正在使用 OpenGL 和 SFML。我是这两个方面的初学者。我正在阅读一些教程,并尝试自己调整一下。但我就是无法让下面的代码工作。它应该显示一个白色三角形,但它只显示黑屏。它不打印任何错误消息。

有几行注释掉了,它们将使用顶点缓冲区对象而不是当前使用的顶点数组。但这两个都不起作用。

我想我一定错过了一些非常简单的东西......但不知道是什么。

我的卡说它支持 OpenGL3.1,但这会是问题的根源吗?当我在 Qt 中尝试使用 OpenGL 的类似示例时,它运行良好。

#include <SFML/Window.hpp>
#include <GL/glew.h>
#include <iostream>

bool createShader(GLuint &shaderID, GLenum shaderType, const char* shaderSource)
{
    shaderID = glCreateShader(shaderType);
    glShaderSource(shaderID, 1, &shaderSource, 0);
    glCompileShader(shaderID);

    GLint compileStatus;
    glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compileStatus);
    if (compileStatus == GL_TRUE)
        return true;

    // log error
    GLint infoLogLength;
    glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
    GLchar* buffer = new GLchar[infoLogLength];
    glGetShaderInfoLog(shaderID, infoLogLength, &infoLogLength, buffer);
    std::cerr << buffer << std::endl;
    delete[] buffer;
    return false;
}

bool createShaderProgram(GLuint &programID)
{
    const char *vertexShaderSource =
        "attribute vec2 position;\n"
        "void main() {\n"
        "   gl_Position = vec4(position, 0.0, 0.0);\n"
        "}\n";

    const char *fragmentShaderSource =
        "void main() {\n"
        "   gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
        "}\n";

    GLuint vertexShaderID;
    if (! createShader(vertexShaderID, GL_VERTEX_SHADER, vertexShaderSource))
        return false;

    GLuint fragmentShaderID;
    if (! createShader(fragmentShaderID, GL_FRAGMENT_SHADER, fragmentShaderSource))
        return false;

    programID = glCreateProgram();
    glAttachShader(programID, vertexShaderID);
    glAttachShader(programID, fragmentShaderID);
    glLinkProgram(programID);

    GLint linkStatus;
    glGetProgramiv(programID, GL_LINK_STATUS, &linkStatus);
    if (linkStatus == GL_TRUE)
        return true;

    // log error
    GLint infoLogLength;
    glGetShaderiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength);
    GLchar* buffer = new GLchar[infoLogLength];
    glGetShaderInfoLog(programID, infoLogLength, &infoLogLength, buffer);
    std::cerr << buffer << std::endl;
    delete[] buffer;
    return false;
}

int main()
{
    sf::Window window(sf::VideoMode(800, 600), "OpenGL", sf::Style::Default, sf::ContextSettings(32));
    window.setVerticalSyncEnabled(true);

    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        std::cerr << "Error: " << glewGetErrorString(err) << std::endl;
        exit(1);
    }
    std::cout << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << std::endl;

    GLuint programID;
    if (! createShaderProgram(programID))
        exit(1);

    glUseProgram(programID);

    static const GLfloat vertices[] =
    {
        0.0f, 1.0f,
        -1.0f, -1.0f,
        1.0f, -1.0f
    };
    //GLuint vertexBufferID;
    //glGenBuffers(1, &vertexBufferID);
    //glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
    //glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    GLint posAttrID = glGetAttribLocation(programID, "position");

    glVertexAttribPointer(posAttrID, 2, GL_FLOAT, GL_FALSE, 0, vertices); // 0);
    glEnableVertexAttribArray(posAttrID);

    bool running = true;
    while (running)
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                running = false;
            }
            else if (event.type == sf::Event::Resized)
            {
                glViewport(0, 0, event.size.width, event.size.height);
            }
        }

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glDrawArrays(GL_TRIANGLES, 0, 3);

        window.display();
    }

    // release resources...

    return 0;
}

更新:

下面的一些人建议我必须使用 VBO 和 VAO。真的吗?或者它只是可选的以获得更好的性能?我不能将数据从普通 RAM 发送到 glVertexAttribPointer() 吗?

我还在支持 OpenGL4.4 的台式机上尝试了上面的代码(之前我只尝试了支持 OpenGL3.1 的集成显卡的懒惰笔记本电脑),结果是:整个屏幕都是白色的。之前是黑色的。为什么?老实说,我不明白。

【问题讨论】:

  • VAO 不是 VBO的替代品。在现代 GL 中你需要both
  • 我没有以任何方式使用或提到过 VAO,我知道它是完全不同的东西(上下文处理、绑定、不绑定的东西等)。而且我坚信如果我可以手动绑定/取消绑定东西,我根本不需要使用 VAO。
  • 现代 GL 需要 VAO。但无论如何,顶点数组也不是 VBO 的替代品,您需要将顶点数组放入 VBO。现在不清楚你是否打算使用现代 GL,我也不知道 SFML 默认会创建什么上下文。
  • 我不明白。您是否建议如果我也使用 VAO,那么它可以工作吗?好吧,我对此不确定,因为我看过即使没有 VAO 也能工作的教程。还是您的意思是我的 SFML 使用了甚至不支持 VBO 的“旧”上下文?
  • VAO 是一个 OpenGL 对象,它存储属性的格式以及存储数据的缓冲区。因此,VAO 仅引用缓冲区对象。缓冲区对象具有包含数据的数据存储,但它们对该数据一无所知。所以你需要两者,因为它们具有非常不同的功能。是的,旧技术不需要您使用 VAO 或缓冲区(如 glBegin()/glEnd())

标签: c++ opengl sfml


【解决方案1】:

您的代码缺少用于存储顶点属性数据的缓冲区(只需取消注释缓冲区部分)和 VAO。

VAO

VAO 是一个对象,它存储每个属性的格式(数据类型、大小等)和一些信息,告诉 OpenGL 在哪里可以找到每个顶点的数据(在哪个缓冲区中)。它还将偏移量存储到缓冲区和步幅中。它本身不存储顶点属性数据。

您可以拥有多个 VAO(使用 glCreateVertexArrays() 创建它们),并且您必须先绑定一个 (glBindVertexArray()) 才能使用它。您使用 glDrawArrays() 或 glDrawElements()(或其任何变体)进行的任何渲染以及对 glVertexAttribPointer() 等的任何调用都使用当前绑定的 VAO。

如果您有多个具有不同格式的顶点属性的网格,这将很有用。您可以通过一个函数调用轻松更改所有状态。但是,切换 VAO 对性能的影响很大,因此请尝试通过以相同的属性格式存储网格来尽量减少这种情况。

VBO(只是通用缓冲区)

VBO 本身不知道任何此类信息。它只是一个通用缓冲区,它只知道它包含多少数据并且它有一个使用提示(STATIC_DRAW 等)。

总而言之,您的代码只需要进行两项更改: 在调用 glVertexAttribPointer() 之前取消对缓冲区部分的注释并创建和绑定 VAO。请注意,您使用 glBufferData() 提供的信息不是 VAO 状态的一部分。

编辑1: 在您的顶点着色器中,您通过gl_Position 输出vec4(position, 0.0, 0.0),其中w 分量为0。w 应始终为1。你应该把它改成vec4(position, 0.0, 1.0)

【讨论】:

  • 感谢解释,看来我误解了VAO的概念。根据一些教程,我的印象是,如果要存储/恢复缓冲区的绑定/解除绑定状态,则只需要绑定 VAO。但是在我深入研究之后,似乎我只能在称为“核心配置文件”OpenGL的东西中使用直接访问内存数组(如我的代码),即没有VBO和VAO(我认为这意味着相同) “旧”OpenGL)。那正确吗?今晚我将尝试取消 VBO 的注释并添加 VAO,让您知道它是如何工作的。
  • 我取消了 VBO 和 // 0); 的注释。然后我添加了glCreateVertexArrays(1, &amp;vao); glBindVertexArray(vao); glEnableClientState(GL_VERTEX_ARRAY);... 但没有任何变化。我忘了什么吗?您能否建议如何将 VAO 合并到这个简单的示例中?
  • “直接访问内存数组”是指 glVertexAttribPointer() 用于提供指向客户端内存的指针,而不仅仅是缓冲区的偏移量吗?在这种情况下,是的,该功能仅在旧版本的 ogl 中执行此操作。核心配置文件不是旧的 OpenGL 版本。恰恰相反。这意味着对于给定的 ogl 版本,您的上下文将仅公开“最新”或已弃用但未删除的函数。兼容性配置文件还将公开所有已删除的功能(旧的 ogl)。提示,请查看:opengl.org/wiki
  • 我明白了,我对核心配置文件的错误。我现在在 SFML 文档中找到了一段:“SFML 支持 3.0 以上的 OpenGL 版本(只要你的图形驱动程序可以处理它们),但你现在不能设置标志。这意味着你不能创建调试或前向兼容的上下文;事实上,SFML 自动创建带有“兼容性”标志的上下文,因为它在内部使用了不推荐使用的函数。这应该很快得到改进,然后标志将在公共 API 中公开。“似乎这可能是原因。 SFML 似乎没有使用核心配置文件创建上下文。
  • glEnableClientState() 是来自 OpenGL 2 的一个非常古老的函数。你不应该使用它(是的,我知道 GL_VERTEX_ARRAY 听起来与这一切有关,但它没有: p)。我不知道这是否会造成麻烦,但我建议您删除它。足够高版本的兼容性上下文应该可以正常工作。核心只是意味着旧的东西被删除,兼容性意味着你可以同时使用新的东西和旧的东西。
猜你喜欢
  • 2018-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
相关资源
最近更新 更多