Вопрос или проблема
У меня есть этот блок кода, который должен помочь мне использовать несколько шейдеров и соответствующие 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
-
Неактивный шейдер:
Прежде чем вызыватьSetuniform3f()
, необходимо убедиться, что соответствующий шейдер активен. В OpenGL текущий шейдер управляется с помощью командыglUseProgram()
, и если шейдер не был установлен как текущий, любые вызовы к функции обновления униформ (например,Setuniform3f()
) завершатся с ошибкой.Решение: Убедитесь, что перед вызовом
Setuniform3f()
установлен корректный шейдер. Это можно сделать, добавив:Binder.getShader(shaderID)->use(); // Предполагается, что use() вызывает glUseProgram() Binder.getShader(shaderID)->Setuniform3f("uColor", color.r, color.g, color.b);
-
Ошибки в переданных параметрах:
Если имя униформа неверное или не существует в текущем шейдере, это также может привести кGL_INVALID_OPERATION
. ИспользуйтеglGetUniformLocation()
для его проверки, как вы уже делали. -
Некорректное время выполнения:
Если метод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
и о том, как именно вы инициализируете и используете ваши шейдеры.