Я обнаружил некоторые ошибки в размещенном коде. В драматической манере я покажу виновника последним.
NULL используется как целое число
Например,
glBindTexture(GL_TEXTURE_2D, NULL); // Incorrect
Функция glBindTexture принимает целочисленный параметр, а не указатель. Вот правильная версия:
glBindTexture(GL_TEXTURE_2D, 0); // Correct
Это также относится к glBindBuffer
и glBindVertexArray
.
Нулевой конструктор определен явно
Ключевое слово explicit
влияет только на унарные конструкторы (конструкторы, которые принимают один параметр). Это не влияет на конструкторы с любым другим количеством параметров.
explicit Background() noexcept; // "explicit" does not do anything.
Background() noexcept; // Exact same declaration as above.
Конструктор неправильно определен как noexcept
Ключевое слово noexcept
означает «эта функция никогда не вызовет исключение». Однако он содержит следующий код, который может выдать:
new Shader("image")
Согласно стандарту, это может выдать std::bad_alloc
. Поэтому аннотация noexcept
неверна. См. Может ли оператор C ++ `new` когда-либо генерировать исключение в реальной жизни?
На более практическом замечании конструктор читает изображение с диска. Скорее всего, это не получится, и исключение - разумный способ справиться с этим.
Ключевое слово noexcept
здесь не особенно полезно. Возможно, компилятор может сгенерировать немного меньше кода на сайте вызовов, но это, скорее всего, будет иметь самое большое значение, поскольку конструктор - это холодный код (cold = не вызывается часто). Квалификатор noexcept
в основном просто полезен для выбора между различными универсальными алгоритмами, см. Для чего не нужно использовать ничего кроме
Нет обработки ошибок
Помните, что stdbi_load
вернет NULL
, если произойдет ошибка. Этот случай не обрабатывается.
Нарушено правило трех
Класс Background
не определяет конструктор копирования или оператор присваивания копии, даже если он определяет деструктор. Хотя это не гарантирует, что ваша программа будет неправильной, это все равно, что оставить заряженный и взведенный пистолет на кухонном столе и надеяться, что никто не прикоснется к нему. Это называется Правило трех , и его легко исправить. Добавьте конструктор удаленных копий и оператор назначения копирования.
// These three go together, either define all of them or none.
// Hence, "rule of three".
Background(const Background &) = delete;
Background &operator=(const Background &) = delete;
~Background();
См. Что такое правило трех?
Буфер неправильно удален
Вот строка:
glDeleteBuffers(1, &VBO);
Короткая версия ... вы должны переместить это в Background::~Background()
.
Длинная версия ... когда вы удаляете буфер, он не удаляется из VAO, но имя может быть немедленно повторно использовано. Согласно спецификации OpenGL 4.6 5.1.2:
Когда объект буфера, текстуры или рендеринга буфера удаляется, он ... отделяется от любых вложений объектов-контейнеров, связанных с текущим контекстом, ...
Таким образом, поскольку VAO в настоящее время не связан, удаление VBO не удаляет VBO из VAO (если VAO были связаны, это было бы иначе). Но, раздел 5.1.3:
Когда объект буфера, текстуры, сэмплера, рендеринга, запроса или синхронизации удаляется, его имя немедленно становится недействительным (например, помечается как неиспользуемый), но базовый объект не будет удален, пока он больше не используется.
Таким образом, VBO останется, но имя может быть использовано повторно. Это означает, что более поздний вызов glGenBuffers
может дать вам то же имя. Затем, когда вы звоните glBufferData
, он перезаписывает данные как на вашем фоне, так и на вашем логотипе. Или glGenBuffers
может дать вам совершенно другое имя буфера. Это полностью зависит от реализации, и это объясняет, почему вы видите различное поведение на разных компьютерах.
Как правило, я бы избегал вызова glDeleteBuffers
, пока я не закончу с использованием буфера. Технически вы можете вызвать glDeleteBuffers
раньше, но это означает, что вы можете получить тот же буфер обратно из glGenBuffers
.