Приложение OpenGL работает по-разному на разных компьютерах - PullRequest
0 голосов
/ 02 июля 2018

Я отправил заявку на тестирование нескольким людям. Первый тестер имеет тот же результат, что и мой, но другие двое имеют что-то странное. По какой-то причине изображения, которые должны быть в оригинальном размере в левом нижнем углу, они видят растянутыми на весь экран. Также они не видят элементы GUI (однако кнопки работают, если они найдены мышью). Оговорюсь, что это не растянутое изображение перекрывает кнопки, я отправил им версию с прозрачным изображением, а кнопки все еще не прорисованы. Для рисования GUI я использую библиотеку Nuklear. Я дам скриншоты и код, который отвечает за размещение изображения проблемы. Что может вызвать это?

[ хорошее поведение / плохое поведение ]

int width, height;
{
    fs::path const path = fs::current_path() / "gamedata" / "images" / "logo.png";
    unsigned char *const texture = stbi_load(path.u8string().c_str(), &width, &height, nullptr, STBI_rgb_alpha);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
    stbi_image_free(texture);
}

...

{
    float const x = -1.0f + width * 2.0f / xResolution;
    float const y = -1.0f + height * 2.0f / yResolution;

    float const vertices[30] = {
        /* Position */        /* UV */
        -1.0f, -1.0f, 0.0f,   0.0f, 0.0f,
        -1.0f,  y,    0.0f,   0.0f, 1.0f,
        x,      y,    0.0f,   1.0f, 1.0f,
        -1.0f, -1.0f, 0.0f,   0.0f, 0.0f,
        x,     -1.0f, 0.0f,   1.0f, 0.0f,
        x,      y,    0.0f,   1.0f, 1.0f
    };

    glBufferData(GL_ARRAY_BUFFER, 30 * sizeof(float), vertices, GL_STATIC_DRAW);
}

[ ОБНОВЛЕНИЕ ]

Методом проб и ошибок я понял, что проблема вызвана классами, которые отвечают за отображение фона и логотипа, и оба они не правы. Отдельно они работают как надо, но как только что-то еще добавляется в игровой цикл, все ломается.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
background.render();
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);
logo.render();
nk_glfw3_render();
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);

Я написал эти классы сам, поэтому, скорее всего, я что-то пропустил. Найдя ошибку в одном, есть другое, потому что они почти идентичны. Пока я не могу определить, что именно в этих классах не так…

[ Background.hpp / Background.cpp ]

1 Ответ

0 голосов
/ 03 июля 2018

Я обнаружил некоторые ошибки в размещенном коде. В драматической манере я покажу виновника последним.

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...