Как правильно связать программу opengl под debian?

Чтобы научиться делать opengl, у меня есть минималистская система сборки и игрушечная программа. Мой makefile выдает следующие команды:

g++ -O1 -std=c++17 -Wall -Wextra -pedantic   -c -o main.o main.cpp
g++   main.o  -lglfw -lGLEW -lGL -o main

В этом каталоге у меня есть одинокий файл main.cpp. Вопреки многим вопросам здесь, на SO, это действительно работает. Он компилируется и компонуется без проблем. Однако, когда я запускаю программу, она просто отображает цвет фона, который я пишу с помощью glClear, но не мой тестовый треугольник.

Я проверил, что это не проблема с моим кодом, потому что, если я скомпилирую его с помощью (довольно сложной и раздутой) системы сборки, которую я нашел в учебнике, она, кажется, работает. Однако я хочу понять, как на самом деле создать программу opengl самостоятельно. Я подозреваю, что мне не хватает какой-то библиотеки или чего-то еще, но я ожидаю, что программа вылетит, а не просто (почти) ничего не сделает.

У меня самый новый дебиан.

Как это может быть, что он отлично компилируется и компонуется, но работает некорректно?


Отказ от ответственности: я абсолютно новичок в opengl и оконных приложениях. Я не уверен на 100 %, зачем мне нужен блеск.

Код:

#include <iostream>

#include <GL/glew.h>

#include <GLFW/glfw3.h>

bool install_shader(GLuint where, std::string const& code, GLuint* attach_to) {
  GLuint shader_id = glCreateShader(where);
  {
    auto const p = code.c_str();
    glShaderSource(shader_id,1,&p,nullptr); // nullptr indicates null-termination here
  }
  glCompileShader(shader_id);
  GLint out;
  glGetShaderiv(shader_id, GL_COMPILE_STATUS, &out);
  if (out == GL_FALSE) {
    // something went wrong with the shader compilation
    glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &out);
    if (out == 0) {
      std::cout << "Unknown Error during shader compilation\n";
    } else {
      std::string error;
      error.resize(out-1);
      glGetShaderInfoLog(shader_id, out, nullptr, error.data());
      std::cout << "Shader Compilation failed with error: " << error;
    }
    return false;
  } else {
    std::cout << "shader(" << int(shader_id) << "@" << int(where) << ") compiled fine\n";

    if (attach_to) {
      glAttachShader(*attach_to, shader_id);
    }

    // XXX THE SHADERS SHOULD BE DELETED AFTER LINKING !!!!
    // glDeleteShader(shader_id)

    return true;
  }
}

bool install_program(GLuint program_id) {
  glLinkProgram(program_id);
  GLint out;
  glGetProgramiv(program_id, GL_LINK_STATUS, &out);
  if (out == GL_FALSE) {
    // something went wrong with the program linking
    glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &out);
    if (out == 0) {
      std::cout << "Unknown link-Error in shader program\n";
    } else {
      std::string error;
      error.resize(out-1);
      glGetProgramInfoLog(program_id, out, nullptr, error.data());
      std::cout << "Program linking failed with error: " << error;
    }
    return false;
  } else {
    std::cout << "program(" << int(program_id) << ") compiled fine\n";
    return true;
  }
}

int main() {
  if (glfwInit()) {
    std::cout <<  "Initialisation fine\n";
  } else {
    std::cout <<  "Initialisation failed\n";
    return 1;
  }

  glfwWindowHint(GLFW_SAMPLES, 4); // 4x antialiasing
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want OpenGL 3.3
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

  if (GLFWwindow* window = glfwCreateWindow(800,600,"testwindow",nullptr,nullptr)) {
    glfwMakeContextCurrent(window);

    if (glewInit() == GLEW_OK) {
      std::cout << "GLEW also fine\n";
    } else {
      std::cout << "GLEW says nope!\n";
      goto die;
      return 2;
    }

    //std::cout << "Ensure we can capture the escape key being pressed below...\n";
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
    /*business*/
    // (1) specify the triangle to draw
    // An array of 3 vectors which represents 3 vertices
    GLfloat g_vertex_buffer_data[] = {
       -0.4f, -0.8f, 0.f,
        0.4f, -0.8f, 0.f,
        0.0f,  0.8f, 0.f,
    };
    GLuint vertexbuffer;
    glGenBuffers(1, &vertexbuffer);

    GLuint program_id = glCreateProgram();

    // (3) vertex shader
    std::string vshader_code = "#version 330 core\n"
      "layout (location = 0) in vec3 aPos;\n"
      "\n"
      "void main()\n"
      "{\n"
      "    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
      "}\n";

    if (!install_shader(GL_VERTEX_SHADER,vshader_code,&program_id)) {
      goto die;
    }

    std::string fshader_code = "#version 330 core\n"
      "out vec3 color;\n"
      "\n"
      "void main()\n"
      "{\n"
      "    color = vec3(0.9,0.8,0.1);\n"
      "}\n";

    if (!install_shader(GL_FRAGMENT_SHADER,fshader_code,&program_id)) {
      goto die;
    }

    if (!install_program(program_id)) {
      goto die;
    }

    glClearColor(0.3, 0.5, 0.5, 1.0);

    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

    do {
      glClear(GL_COLOR_BUFFER_BIT);

      glUseProgram(program_id);


      glEnableVertexAttribArray(0); // this 0 also refes to 'layout (location = 0)' in the vertex shader

      glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
      glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

      glVertexAttribPointer(
        0,        // this refes to 'layout (location = 0)' in the vertex shader
        3,        // each vertex has 3 coordinates (2d data is also possible)
        GL_FLOAT, // coordinates are of type GLfloat (GL_FLOAT)
        GL_FALSE, // we don't need the input data to be nomalised,
        0, //&g_vertex_buffer_data[3] - &g_vertex_buffer_data[0], // stride
        nullptr        // offset ptr
      );

      // Draw the triangle !
      glDrawArrays(GL_TRIANGLES, 0, 3);
      glDisableVertexAttribArray(0);

      glfwSwapBuffers(window);

      glfwPollEvents();
    } // Check if the ESC key was pressed or the window was closed
    while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS && glfwWindowShouldClose(window) == 0 );

  } else {
    std::cout << "Could not create window\n";
  }

  die:
  glfwTerminate();
  std::cout <<  "Terminated\n";
}

person bitmask    schedule 26.12.2019    source источник
comment
I have verified that it's not a problem with my code, because if I compile it with a (quite complex and bloated) build system I found in a tutorial it seems to work. это не обязательно означает, что код правильный. На самом деле у вас может иметь место какое-то неопределенное поведение, а другая система сборки может иметь другие параметры компиляции или по какой-то другой причине привести к другому расположению памяти, что может привести к тому, что код будет работать правильно, даже если технически это не так. действительный.   -  person t.niese    schedule 26.12.2019
comment
И код был бы полезен, чтобы выяснить, почему он не падает и где можно подключить некоторые проверки ошибок, чтобы отследить проблему.   -  person t.niese    schedule 26.12.2019
comment
@t.niese: я поместил свой код в pastebin. Я скопировал большую часть этого из примеров в учебниках. В основном с opengl-tutorial.org и Learnopengl.com.   -  person bitmask    schedule 26.12.2019
comment
Система сборки, которую я нашел, использует копию glfw и glew, которая скомпилирована и связана с ней. Однако я считаю это довольно некрасивым. Я хотел бы связать библиотеку, которую уже предоставляет моя система. Это не просто так.   -  person bitmask    schedule 26.12.2019
comment
Просто чтобы исключить одну вещь: в обоих случаях вы запускаете программу на одной машине с одним и тем же графическим процессором? Потому что технически данная настройка OpenGL недействительна и должна показывать только треугольник с графическими процессорами Nvidia и их драйверами.   -  person t.niese    schedule 26.12.2019
comment
То же оборудование, та же операционная система, Intel UHD Graphics 620.   -  person bitmask    schedule 26.12.2019
comment
@t.niese: А как насчет того, что данная настройка opengl недействительна?   -  person bitmask    schedule 26.12.2019


Ответы (1)


Вам нужен объект массива вершин, и привязать его для хранения состояний требуются для рисования треугольника:

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// your remaining code
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
// ...

Профиль совместимости OpenGL делает объект VAO 0 объектом по умолчанию. Базовый профиль OpenGL делает объект VAO 0 вообще не объектом. Поэтому, если VAO 0 привязан к основному профилю, вам не следует вызывать какую-либо функцию, которая изменяет состояние VAO. Это включает привязку GL_ELEMENT_ARRAY_BUFFER с помощью glBindBuffer.

В зависимости от профиля и/или драйвера может быть активным VAO по умолчанию, что приведет к рендерингу трейнгла. Но для стандартной настройки жалоб вам необходимо создать VAO для объекта, который вы хотите визуализировать.

Это на самом деле не объясняет, почему он работает с GLFW и GLEW в комплекте, но, возможно, один из них создает VAO по умолчанию, чтобы имитировать поведение профиля совместимости или включение драйвера NVIDIA.

Вы можете проверить, находитесь ли вы в основном профиле или профиле совместимости, используя этот код:

GLint prof;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &prof);
std::cout << (prof&GL_CONTEXT_CORE_PROFILE_BIT) << " " << (prof&GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) << std::endl;
person t.niese    schedule 26.12.2019
comment
Блестяще, это работает. Теперь, чтобы понять VAO для меня. У вас есть идеи, почему код учебника работал без VAO? - person bitmask; 26.12.2019
comment
@bitmask, поэтому вы никогда не должны предполагать, что код правильный и неактуальный;) - person t.niese; 26.12.2019
comment
Да да, я знаю. Виноват :) - person bitmask; 26.12.2019
comment
@bitmask Не вдаваясь в настройки системы сборки, которую вы использовали, а также используя glfw и версию glew, я могу только догадываться, что профиль имеет другое значение по умолчанию. - person t.niese; 26.12.2019
comment
@bitmask Я добавил несколько строк кода, которые вы могли бы включить в свой проект, чтобы проверить, что контекст, созданный с помощью этой системы сборки, работает с профилем совместимости. - person t.niese; 26.12.2019
comment
Выводит 1 0. Я полагаю, совместимость плохая, а ядро ​​​​хорошее? - person bitmask; 26.12.2019
comment
@bitmask выводит 1 0 в среде, где работало без VAO? Это странно. В любом случае, я не думаю, что стоит тратить время на то, чтобы выяснить, почему это работает там, если это работает с правильной настройкой :D. Профиль совместимости Well позволяет использовать старый устаревший код OpenGL, который может потребоваться для некоторых проектов, но из-за этого контекст OpenGL может замедляться, поскольку драйвер должен поддерживать устаревший код. И с ядром вы должны использовать современный API, который драйвер — теоретически — мог бы оптимизировать лучше. - person t.niese; 26.12.2019
comment
Да, так и думал. Я согласен, что не стоит беспокоиться, почему сработала недействительная программа. УБ есть УБ есть УБ. Я СЕЙЧАС прочитал в документе, что современный opengl требует использования VAO. - person bitmask; 26.12.2019