Возвращение объекта класса шейдера для использования функции uniform в классе OpenGL приводит к GL_INVALID_OPERATION.

Вопрос или проблема

У меня есть этот блок кода, который должен помочь мне использовать несколько шейдеров и соответствующие VAOs/VBOs (не спрашивайте, почему, я просто решил сделать это, чтобы использовать в будущем, и даже я не знаю, зачем).

#pragma once
#include"shader/_Shader.h"
#include"includes.h"

namespace SHADER
{
    enum class ShaderID
    {
        SH_00 = 0,
        SH_01 = 1, SH_02,..., SH_50,
    };
#define MAX_SHADER_ID 50
    class Shader_Binder
    {
    private:
        struct Data
        {
            ShaderID id;
            Shader m_shader;
            std::vector<unsigned int> m_VAOs;
            std::vector<unsigned int> m_VBOs;
        };
        ShaderID m_CurrentShader = ShaderID::SH_00; // Отслеживание текущего шейдера
        unsigned int m_CurrentVAO = 0; // Отслеживание текущего VAO
        unsigned int m_CurrentVBO = 0; // Отслеживание текущего VBO
        std::unordered_map<ShaderID, Data> m_Data;

    public:

        // Статический метод для получения единственного экземпляра класса
        static Shader_Binder& getInstance()
        {
            static Shader_Binder instance; // Гарантированное уничтожение, создается при первом использовании
            return instance;
        }

        // Удаленный конструктор копирования и оператор присваивания для предотвращения копирования
        Shader_Binder(const Shader_Binder&) = delete;
        Shader_Binder& operator=(const Shader_Binder&) = delete;

        /* Создает новый шейдер, связывает его и возвращает ID */
        ShaderID CreateNewShader(const std::string vShader = "", const std::string fShader = "", const std::string gShader = "");
        /* Связывает переданный шейдер и возвращает ID */
        ShaderID CreateNewShader(Shader& shader);

        /* Связывает шейдер, VAO и VBO */
        void bind(ShaderID id = ShaderID::SH_00, int vaoid = -1, int vboid = -1);
        /* Отменяет связывание шейдера, VAO и VBO */
        void unbind(ShaderID id = ShaderID::SH_00, int vaoid = -1, int vboid = -1);

        /* Создает новый VAO для заданного ID шейдера, связывает его и возвращает значение созданного VAO. */
        unsigned int CreateNewVAO(ShaderID id);
        /* Создает новый VBO для заданного ID шейдера, связывает его и возвращает значение созданного VBO. */
        unsigned int CreateNewVBO(ShaderID id);

        /* Возвращает указатель на объект шейдера относительно переданного ID */
        Shader* getShader(ShaderID id);

        /* Устанавливает атрибуты вершин на основе ID переданного шейдера
         * vaoIndex по умолчанию равен 0,
         * учитывая, что для одного и того же шейдера не создано несколько VAO */
        void setVertexAttribs(ShaderID id, unsigned int Gl_location_index, int size,
            unsigned int type, GLboolean normalized, int stride, const void* pointer,
            unsigned int vaoIndex = 0);
        /*
        * Предполагается GL_DYNAMIC_DRAW как способ использования данных по умолчанию.
        * Требуется указать размер буфера, даже если способ использования GL_DYNAMIC_DRAW.
        * Предполагается, что необходимые шейдеры и другие связаны перед использованием.
        * Установите способ использования на GL_STATIC_DRAW, если данные статические и переданы другие параметры.
        * Предполагается GL_ARRAY_BUFFER как целевой объект по умолчанию
        */
        void setBufferData(GLenum usage = GL_DYNAMIC_DRAW, GLsizeiptr size = 0,
            const void* data = nullptr, GLenum target = GL_ARRAY_BUFFER);
        /*
        * Обновляет данные буфера, если способ использования был GL_DYNAMIC_DRAW.
        * Предполагается, что необходимые шейдеры и другие связаны перед использованием.
        * Использует glBufferSubData() для выполнения задачи
        * Предполагает GL_ARRAY_BUFFER как целевой объект по умолчанию
        */
        void UpdateBufferData(GLintptr offset, GLsizeiptr size, const void* data, GLenum target = GL_ARRAY_BUFFER);

        /* отменяет связывание последнего использованного (или любого в настоящее время связанного) шейдера и VAO и VBO */
        void reset();
    private:
        bool isIdUsed(ShaderID id);
        Shader_Binder() {}
        ~Shader_Binder() {}

    };
}

typedef SHADER::ShaderID ShaderID;
static SHADER::Shader_Binder& Binder = SHADER::Shader_Binder::getInstance();

Единственная проблема, с которой я сталкиваюсь, – это Shader* getShader(ShaderID id);
чье определение:

    Shader* Shader_Binder::getShader(ShaderID id)
    {
        return &m_Data.at(id).m_shader;
    }

и все, что он должен делать, – это дать мне ссылку на объект шейдера с ID, который я сохранил в карте.
Я пытался вызвать функцию под названием Setuniform3f(), которая была одной из функций-членов внутри объекта шейдера, и, как ни странно, это не работает, и я получаю GL_INVALID_OPERATION.
Я пробовал делать это вне функции, и это работает:

    Binder.getShader(shaderID)->Setuniform3f("uColor", color.r, color.g, color.b);//это не работает
    auto x=glGetUniformLocation(Binder.getShader(shaderID)->Program_id, "uColor");
    glUniform3f(x, color.r, color.g, color.b);//это работает

Я не понимаю, почему это происходит.. я пробовал убрать указатель из функции или пытался использовать & как тип возвращаемого значения, но это не помогло.
Я искал в интернете и даже спрашивал у ChatGPT, и они упоминали что-то о сроке службы объекта шейдера или висячих указателях, и это совсем не помогло мне.

Не стесняйтесь задавать мне любые вопросы, связанные с этим, и я буду благодарен за любую помощь, даже если она небольшая. Это беспокоит меня уже довольно долго.

Ответ или решение

Проблема, с которой вы столкнулись, связана с использованием объектов шейдеров и ошибок OpenGL, таких как GL_INVALID_OPERATION, что указывает на нарушение правил использования API OpenGL. Давайте подробно разберем вашу ситуацию и решение.

Основные моменты

Как вы отметили, метод getShader(ShaderID id) возвращает указатель на объект шейдера, который хранится в мапе (аналоге словаря). Вы пытаетесь вызвать метод Setuniform3f() на этом объекте, но это приводит к ошибке. Однако вызов glGetUniformLocation() и glUniform3f() работает без проблем.

Давайте рассмотрим причины возникновения этой ошибки:

Причины GL_INVALID_OPERATION

  1. Неактивный шейдер:
    Прежде чем вызывать Setuniform3f(), необходимо убедиться, что соответствующий шейдер активен. В OpenGL текущий шейдер управляется с помощью команды glUseProgram(), и если шейдер не был установлен как текущий, любые вызовы к функции обновления униформ (например, Setuniform3f()) завершатся с ошибкой.

    Решение: Убедитесь, что перед вызовом Setuniform3f() установлен корректный шейдер. Это можно сделать, добавив:

    Binder.getShader(shaderID)->use();  // Предполагается, что use() вызывает glUseProgram()
    Binder.getShader(shaderID)->Setuniform3f("uColor", color.r, color.g, color.b);
  2. Ошибки в переданных параметрах:
    Если имя униформа неверное или не существует в текущем шейдере, это также может привести к GL_INVALID_OPERATION. Используйте glGetUniformLocation() для его проверки, как вы уже делали.

  3. Некорректное время выполнения:
    Если метод Setuniform3f() вызывается до завершения процесса инициализации OpenGL или после того, как контекст рендеринга был потерян, это также может вызвать ошибку.

    Решение: Убедитесь, что ваш OpenGL контекст активен и что все инициализацию шейдеров и VAO/VBO завершены перед вызовом этих функций.

Возможный код

Ваш код мог бы выглядеть следующим образом:

// Убеждаемся, что шейдер активен
Binder.getShader(shaderID)->use(); // Устанавливаем шейдер перед использованием униформ
Binder.getShader(shaderID)->Setuniform3f("uColor", color.r, color.g, color.b);

Дебаггинг

Используйте следующие методы для отладки:

  • Проверка ошибок OpenGL: Добавьте код для проверки ошибок OpenGL после вызовов glGetUniformLocation() и других вызовов OpenGL, чтобы узнать, происходит ли ошибка.
  • Логирование: Введите вывод в консоль для проверки значений и состояний во время выполнения программы.

Заключение

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

Если это не решит вашу проблему, пожалуйста, предоставьте больше информации о коде метода Setuniform3f и о том, как именно вы инициализируете и используете ваши шейдеры.

Оцените материал
Добавить комментарий

Капча загружается...