使用 OpenGL 和 SFML 显示一个简单的三角形

Displaying a simple triangle using OpenGL and SFML

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

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

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

我的显卡说它支持 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。

VAO

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

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

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

VBO(只是通用缓冲区)

IBO 本身不知道任何这些信息。它只是一个通用缓冲区,它只知道它包含多少数据并且它有一个使用提示(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).