#include "Lights.h" #include "Scene.h" // Для отладочного вывода лампочек #include GLuint Light::count = 0; // количество используемых источников (должно быть <= MAX_LIGHTS) LightData Light::data[MAX_LIGHTS]; // Массив данных по источникам света Light Light::lights[MAX_LIGHTS]; // Массив источников-узлов сцены Sun Sun::instance; // Экземпляр синглтона bool Sun::uploadReq = true; // Необходимость загрузки в следствии изменений // возвращает размер буфера в байтах int Light::getUBOsize() { return sizeof(LightData) * MAX_LIGHTS + sizeof(GLuint); } // Загрузка данных в буфер void Light::upload(UBO& lights_data) { GLuint LightDataSize = sizeof(LightData); // Одного экземпляра структуры LightData int first = MAX_LIGHTS, last = -1; // Начало и конец диапазона загрузки источников static GLuint prev_count = -1; // Кол-во источников в прошлую посылку if (count) { for (int i = 0; i < MAX_LIGHTS; i++) { lights[i].recalcMatrices(); // Пересчитаем матрицы по необходимости (проверка внутри метода) // Если требуется загрузка if (lights[i].uploadReq) { lights[i].toData(); // Перевод ноды в данные для шейдера // Определение диапазона загрузки if (first > lights[i].index) first = lights[i].index; if (last < lights[i].index) last = lights[i].index; lights[i].uploadReq = false; // Сброс флага } } // Если есть что загрузить (определен диапазон) if (last > -1) lights_data.loadSub(data + first, LightDataSize*(last - first +1), LightDataSize*(first)); // Загрузка данных об источниках } // Если кол-во изменилось if (prev_count != count) { prev_count = count; // Загружаем кол-во источников lights_data.loadSub(&count, sizeof(count), LightDataSize*MAX_LIGHTS); } } // Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed void Light::recalcMatrices() { // Если были изменения - необходимо загрузить данные if (changed || parent_changed) uploadReq = true; // Выполняем вычисление матриц методом родительского класса Node::recalcMatrices(); } // Константный доступ к цвету const glm::vec3& Light::c_color() const { return color; } // Неконстантная ссылка для изменений цвета glm::vec3& Light::e_color() { uploadReq = true; return color; } // Проверка что не взаимодествуем с пустым источником void Light::check_id() { if (index < 0 || index >= count) throw std::runtime_error("Попытка использовать ссылку на пустой или некорректный источник"); } // Преобразует информацию об источнике в структуру LightData void Light::toData() { check_id(); // Проверка на работу с корректным индексом // Если позиция изменилась if (data[index].position.x != result_transform[3].x || data[index].position.y != result_transform[3].y || data[index].position.z != result_transform[3].z ) { data[index].position = glm::vec3(result_transform[3]); // Позиция из матрицы трансформации recalcVP(); // Пересчет матрицы вида-проекции для расчета теней } data[index].color = color; // Цвет // Если радиус изменился if (data[index].attenuation.r != radius) { data[index].attenuation.r = radius; // Радиус действия источника data[index].attenuation[1] = 4.5/radius; // Линейный коэф. угасания data[index].attenuation[2] = 4 * data[index].attenuation[1] * data[index].attenuation[1]; // Квадратичный коэф. угасания } // Направление и угол источника data[index].direction_angle = glm::vec4( glm::normalize(glm::vec3(result_transform * DEFAULT_LIGHT_DIRECTION)) , angle / 2 // Половинный угол для вычислений на шейдере ); } // Возвращает ссылку на новый источник света Light& Light::getNew() { Light& refNew = findByIndex(-1); refNew.index = count++; refNew.uploadReq = true; return refNew; } // Уничтожает источник света void Light::destroy() { check_id(); // Проверка на работу с корректным индексом // Если удаляемый элемент не последний if (count-1 != index) { // Найдем элемент для замены Light& replace = findByIndex(--count); replace.uploadReq = true; // Требуется загрузить данные replace.index = index; // Заменяем индекс данных } operator=(Light()); // Обнулим источник путем замены на новый } // Возвращает ссылку на источник с нужным индексом Light& Light::findByIndex(GLuint index) { // Если нет источников - возвращаем нулевой if (!count) return lights[0]; // Цикл по перебору источников for (int i = 0; i < MAX_LIGHTS; i++) if (lights[i].index == index) return lights[i]; throw std::runtime_error("Запрашиваемый источник освещения не найден, либо достигнут лимит"); } // Конструктор без параметров Light::Light() : Node(), index(-1), uploadReq(false), color(1.0f), radius(10.0f), angle(360.0f) { } // Оператор присваивания Light& Light::operator=(const Light& other) { // Проверка на самоприсваивание if (this != &other) { index = other.index; // Переносим индекс uploadReq = other.uploadReq; // Необходимость загрузки color = other.color; radius = other.radius; angle = other.angle; Node::operator=(other); } return *this; } Light::~Light() { } // Рисование отладочных лампочек void Light::render(ShaderProgram &shaderProgram, UBO &material_buffer) { // Загрузка модели лампочки при первом вызове функции static Scene bulb = loadOBJtoScene("../resources/models/bulb.obj", "../resources/models/", "../resources/textures/"); static Model sphere = genShpere(1, 16, &bulb.root); GLuint angle_uniform = shaderProgram.getUniformLoc("angle"); GLuint direction_uniform = shaderProgram.getUniformLoc("direction"); // Цикл по источникам света for (int i = 0; i < count; i++) { // Идентификатор источника как узла сцены для всей модели лампочки bulb.set_group_id((GLuint64) &lights[i]); sphere.id.value = (GLuint64) &lights[i]; // Загрузим направление glUniform3fv(direction_uniform, 1, &data[i].direction_angle.x); // Угол для лампочки = 180 (рисуем целую модель) glUniform1f(angle_uniform, 180); // Зададим параметры материала сфере действия // Сдвиг на позицию источника bulb.root.e_position() = data[i].position; sphere.e_scale() = glm::vec3(data[i].attenuation.r); // Масштабирование сферы // Задание цвета bulb.models.begin()->material.base_color = sphere.material.base_color = data[i].color; // Вызов отрисовки bulb.render(shaderProgram, material_buffer); // Угол для сферы (рисуем направленный конус) glUniform1f(angle_uniform, data[i].direction_angle.a); // Рисование сферы покрытия источника в режиме линий glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); sphere.render(shaderProgram, material_buffer); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } // Константный доступ к радиусу const float& Light::c_radius() const { return radius; } // Неконстантная ссылка для изменений радиуса float& Light::e_radius() { uploadReq = true; return radius; } // Константный доступ к углу освещенности const float& Light::c_angle() const { return angle; } // Неконстантная ссылка для изменений угла освещенности float& Light::e_angle() { uploadReq = true; return angle; } // Конструктор направленного источника с параметрами направления и цвета Sun::Sun(const glm::vec3 &dir, const glm::vec3 &c) : direction(dir), color(c) { } // Доступ к синглтону Sun& Sun::get() { return instance; } // Загрузка данных об источнике на шейдер void Sun::upload(UBO& sun_data) { instance.recalcVP(); // Пересчет матрицы вида-проекции источника по необходимости (влияет на флаг uploadReq) if (uploadReq) { sun_data.loadSub(&instance, sizeof(instance)); uploadReq = false; } } // Константный доступ к направлению лучей источника const glm::vec3& Sun::c_direction() const { return instance.direction; } // Неконстантная ссылка для изменений направления лучей источника glm::vec3& Sun::e_direction() { uploadReq = true; return instance.direction; } // Константный доступ к цвету const glm::vec3& Sun::c_color() const { return instance.color; } // Неконстантная ссылка для изменений цвета glm::vec3& Sun::e_color() { uploadReq = true; return instance.color; } // Пересчитывает по необходимости матрицу вида-проекции void Sun::recalcVP() { // Точки по краям проекции камеры std::pair camProjCoords = Camera::current().getProjCoords(); // Есть изменения по источнику или камере if (uploadReq || camProjCoords.first) { uploadReq = true; // Требуется загрузка в следствии пересчета матрицы glm::vec3 mean; // Среднее арифметическое glm::vec4 max, min; // макс и мин координаты glm::vec4 point; // Точка приведенная в пространство источника света for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++) { mean = glm::vec3(0); // Найдем среднее арифметическое от точек для нахождения центра прямоугольника for (int i = 0; i < 8; i++) mean += glm::vec3(camProjCoords.second[cascade][i]); mean /= 8; // Используем среднее арифметическое для получения матрицы вида параллельного источника glm::mat4 lightView = glm::lookAt(mean + glm::normalize(direction), mean, CAMERA_UP_VECTOR); // Примем первую точку как минимальную и максимальную (приведя в пространство вида источника) min = max = lightView * camProjCoords.second[cascade][0]; // Для оставшихся точек for (int i = 1; i < 8; i++) { // Приведем в пространство вида источника point = lightView * camProjCoords.second[cascade][i]; max = glm::max(max, point); min = glm::min(min, point); } // Максимальное значение глубины max.z = std::max(fabs(max.z), fabs(min.z)); // На основании максимальных и минимальных координат создадим матрицу проекции источника vp[cascade] = glm::ortho(min.x, max.x, min.y, max.y, min.z, max.z) * lightView; } } } // Пересчитывает по необходимости матрицу вида-проекции void Light::recalcVP() { float near_plane = 0.1f; glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), 1.0f, near_plane, radius); data[index].vp[0] = shadowProj * glm::lookAt(position, position + glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)); data[index].vp[1] = shadowProj * glm::lookAt(position, position + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)); data[index].vp[2] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); data[index].vp[3] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)); data[index].vp[4] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)); data[index].vp[5] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)); } // Возвращает количество источников int Light::getCount() { return count; }