diff --git a/include/Shader.h b/include/Shader.h new file mode 100644 index 0000000..0562dd7 --- /dev/null +++ b/include/Shader.h @@ -0,0 +1,30 @@ +#ifndef SHADER_H +#define SHADER_H + +#include + +#include +#include + +// Класс шейдерной программы +class ShaderProgram +{ + public: + ShaderProgram(); + ShaderProgram(const ShaderProgram ©); + ~ShaderProgram(); + ShaderProgram& operator=(const ShaderProgram& other); + + void use(); // Использование шейдеров + void load(GLuint type, const char* filename); // Функция для загрузки шейдеров + void link(); // Формирование программы из загруженных шейдеров + GLuint getUniformLoc(const char* name); // Возвращает местоположение uniform-переменной + void bindUniformBlock(const char* name, int binding); // Привязка uniform-блока + void bindTextures(const char* textures_base_shader_names[], int count); // Инициализация текстур на шейдере + private: + GLuint program; // Дескриптор + static std::map handler_count; // Получение количества использований по дескриптору шейдера (Shared pointer) + std::map uniformLocations; // Местоположения uniform-переменных +}; + +#endif // SHADER_H \ No newline at end of file diff --git a/include/Texture.h b/include/Texture.h index 650ee11..207116f 100644 --- a/include/Texture.h +++ b/include/Texture.h @@ -20,7 +20,6 @@ class Texture Texture& operator=(const Texture& other); // Оператор присваивания - static void init_textures(GLuint programID); // Инициализация текстур на шейдере void use(); // Привязка текстуры static void disable(GLuint type); // Отвязка текстуры по типу GLuint getType(); // Возвращает тип текстуры diff --git a/src/Shader.cpp b/src/Shader.cpp new file mode 100644 index 0000000..c218673 --- /dev/null +++ b/src/Shader.cpp @@ -0,0 +1,154 @@ +#include "Shader.h" + +#include +#include +#include + +std::map ShaderProgram::handler_count; // Получение количества использований по дескриптору ШП (Shared pointer) + +ShaderProgram::ShaderProgram() +{ + program = glCreateProgram(); + handler_count[program] = 1; +} + +ShaderProgram::ShaderProgram(const ShaderProgram ©) : program(copy.program) +{ + handler_count[program]++; +} + +ShaderProgram::~ShaderProgram() +{ + if (!--handler_count[program]) // Если количество ссылок = 0 + { + // Удаление шейдерной программы + glDeleteProgram(program); + } +} + +// Оператор присваивания +ShaderProgram& ShaderProgram::operator=(const ShaderProgram& other) +{ + // Если это разные шейдерные программы + if (program != other.program) + { + this->~ShaderProgram(); // Уничтожаем имеющуюся + // Заменяем новой + program = other.program; + handler_count[program]++; + } + return *this; +} + +// Использование шейдеров +void ShaderProgram::use() +{ + glUseProgram(program); +} + +// Функция чтения шейдера из файла +std::string readFile(const char* filename) +{ + std::string text; + std::ifstream file(filename, std::ios::in); // Открываем файл на чтение + // Если файл доступен и успешно открыт + if (file.is_open()) + { + std::stringstream sstr; // Буфер для чтения + sstr << file.rdbuf(); // Считываем файл + text = sstr.str(); // Преобразуем буфер к строке + file.close(); // Закрываем файл + } + + return text; +} + +// Функция для загрузки шейдеров +void ShaderProgram::load(GLuint type, const char* filename) +{ + // Создание дескрипторов шейдера + GLuint handler = glCreateShader(type); + + // Переменные под результат компиляции + GLint result = GL_FALSE; + int infoLogLength; + + // Считываем текст вершинного шейдера + std::string code = readFile(filename); + const char* pointer = code.c_str(); // Преобразование к указателю на const char, так как функция принимает массив си-строк + + // Компиляция кода вершинного шейдера + glShaderSource(handler, 1, &pointer, NULL); + glCompileShader(handler); + + // Проверка результата компиляции + glGetShaderiv(handler, GL_COMPILE_STATUS, &result); + glGetShaderiv(handler, GL_INFO_LOG_LENGTH, &infoLogLength); + if (infoLogLength > 0) + { + char* errorMessage = new char[infoLogLength + 1]; + glGetShaderInfoLog(handler, infoLogLength, NULL, errorMessage); + std::cout << errorMessage; + delete[] errorMessage; + } + + // Привязка скомпилированного шейдера + glAttachShader(program, handler); + + // Освобождение дескриптора шейдера + glDeleteShader(handler); +} + +// Формирование программы из загруженных шейдеров +void ShaderProgram::link() +{ + // Переменные под результат компиляции + GLint result = GL_FALSE; + int infoLogLength; + + // Формирование программы из привязанных шейдеров + glLinkProgram(program); + + // Проверка программы + glGetProgramiv(program, GL_LINK_STATUS, &result); + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); + if (infoLogLength > 0) + { + char* errorMessage = new char[infoLogLength + 1]; + glGetProgramInfoLog(program, infoLogLength, NULL, errorMessage); + std::cout << errorMessage; + delete[] errorMessage; + } + + // Используем шейдерную программу объекта из которого вызван метод + this->use(); +} + +// Возвращает местоположение uniform-переменной +GLuint ShaderProgram::getUniformLoc(const char* name) +{ + GLuint result; // Результат + // Если такую переменную ещё не искали - найдем, иначе вернем уже известный дескриптор + if (!uniformLocations.count(name)) + uniformLocations[name] = result = glGetUniformLocation(program, name); + else + result = uniformLocations[name]; + + return result; +} + +// Привязка uniform-блока +void ShaderProgram::bindUniformBlock(const char* name, int binding) +{ + glUniformBlockBinding( program + , glGetUniformBlockIndex(program, name) + , binding); +} + +// Инициализация текстур на шейдере +void ShaderProgram::bindTextures(const char* textures_base_shader_names[], int count) +{ + // Цикл по всем доступным текстурам + for (int i = 0; i < count; i++) + glUniform1i(getUniformLoc(textures_base_shader_names[i]), i); +} diff --git a/src/Texture.cpp b/src/Texture.cpp index 181db4b..9384658 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -98,16 +98,6 @@ Texture::~Texture() } } -const char* textures_base_shader_names[] = {"tex_diffuse"}; - -// Инициализация текстур на шейдере -void Texture::init_textures(GLuint programID) -{ - // Цикл по всем доступным текстурам - for (int i = 0; i < TEX_AVAILABLE_COUNT; i++) - glUniform1i(glGetUniformLocation(programID, textures_base_shader_names[i]), i); -} - // Привязка текстуры void Texture::use() { diff --git a/src/main.cpp b/src/main.cpp index 802b2bc..dba83c0 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include #include "Scene.h" +#include "Shader.h" #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 @@ -16,102 +17,6 @@ void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } - -#include -#include - -// Функция чтения шейдера из файла -std::string readFile(const char* filename) -{ - std::string text; - std::ifstream file(filename, std::ios::in); // Открываем файл на чтение - // Если файл доступен и успешно открыт - if (file.is_open()) - { - std::stringstream sstr; // Буфер для чтения - sstr << file.rdbuf(); // Считываем файл - text = sstr.str(); // Преобразуем буфер к строке - file.close(); // Закрываем файл - } - - return text; -} - -// Функция для загрузки шейдеров -// Принимает два адреса вершинного и фрагментного шейдеров -// Возвращает дескриптор шейдерной программы -GLuint LoadShaders(const char *vertex_file, const char *fragment_file) -{ - // Создание дескрипторов вершинного и фрагментного шейдеров - GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); - GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); - - // Переменные под результат компиляции - GLint result = GL_FALSE; - int infoLogLength; - - // Считываем текст вершинного шейдера - std::string code = readFile(vertex_file); - const char* pointer = code.c_str(); // Преобразование к указателю на const char, так как функция принимает массив си-строк - - // Компиляция кода вершинного шейдера - glShaderSource(vertexShaderID, 1, &pointer, NULL); - glCompileShader(vertexShaderID); - - // Проверка результата компиляции - glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &result); - glGetShaderiv(vertexShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); - if (infoLogLength > 0) - { - char* errorMessage = new char[infoLogLength + 1]; - glGetShaderInfoLog(vertexShaderID, infoLogLength, NULL, errorMessage); - std::cout << errorMessage; - delete[] errorMessage; - } - - // Считываем текст вершинного шейдера - code = readFile(fragment_file); - pointer = code.c_str(); // Преобразование к указателю на const char, так как функция принимает массив си-строк - - // Компиляция кода фрагментного шейдера - glShaderSource(fragmentShaderID, 1, &pointer, NULL); - glCompileShader(fragmentShaderID); - - // Проверка результата компиляции - glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &result); - glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); - if (infoLogLength > 0) - { - char* errorMessage = new char[infoLogLength + 1]; - glGetShaderInfoLog(fragmentShaderID, infoLogLength, NULL, errorMessage); - std::cout << errorMessage; - delete[] errorMessage; - } - - // Привязка скомпилированных шейдеров - GLuint programID = glCreateProgram(); - glAttachShader(programID, vertexShaderID); - glAttachShader(programID, fragmentShaderID); - glLinkProgram(programID); - - // Проверка программы - glGetProgramiv(programID, GL_LINK_STATUS, &result); - glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength); - if (infoLogLength > 0) - { - char* errorMessage = new char[infoLogLength + 1]; - glGetProgramInfoLog(programID, infoLogLength, NULL, errorMessage); - std::cout << errorMessage; - delete[] errorMessage; - } - - // Освобождение дескрипторов шейдеров - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); - - return programID; -} - bool firstMouse = true; float lastX, lastY; @@ -178,12 +83,15 @@ int main(void) // Включаем проверку по буферу глубины glEnable(GL_DEPTH_TEST); - // Компиляция шейдеров - GLuint shaderProgram = LoadShaders("shaders/shader.vert", "shaders/shader.frag"); - // Активация шейдера - glUseProgram(shaderProgram); + // Базовый шейдер + ShaderProgram base; + // Загрузка и компиляция шейдеров + base.load(GL_VERTEX_SHADER, "shaders/shader.vert"); + base.load(GL_FRAGMENT_SHADER, "shaders/shader.frag"); + base.link(); // Установим значения текстур - Texture::init_textures(shaderProgram); + const char* textures_base_shader_names[] = {"tex_diffuse"}; + base.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*)); // Загрузка сцены из obj файла Scene scene = loadOBJtoScene("../resources/models/cubes.obj", "../resources/models/", "../resources/textures/"); @@ -192,7 +100,7 @@ int main(void) glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Расположение Uniform-переменной - GLuint model_uniform = glGetUniformLocation(shaderProgram, "model"); + GLuint model_uniform = base.getUniformLoc("model"); // Uniform-буферы UBO cameraUB(sizeof(glm::mat4)*2, 0); @@ -217,9 +125,6 @@ int main(void) // Обработка системных событий glfwPollEvents(); } - - // Удаление шейдерной программы - glDeleteProgram(shaderProgram); return 0; }