14/src/main.cpp

493 lines
22 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <GLM/glm.hpp>
#include <iostream>
#include <random>
#include "Scene.h"
#include "Shader.h"
#include "Lights.h"
#define WINDOW_CAPTION "OPENGL notes on rekovalev.site"
// Указатели на текстуры для изменения размеров окна
Texture* pgPosition = NULL;
Texture* pgNormal = NULL;
Texture* pgDiffuseP = NULL;
Texture* pgAmbientSpecular = NULL;
RBO* pgrbo = NULL;
Texture* pssaoTexture = NULL;
// Размеры окна
int WINDOW_WIDTH = 800;
int WINDOW_HEIGHT = 600;
// Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
// Изменение размеров текстур для G-буфера
if (pgPosition)
pgPosition->reallocate(width, height, 0, GL_RGB32F, GL_RGB);
if (pgNormal)
pgNormal->reallocate(width, height, 1, GL_RGB16F, GL_RGB);
if (pgDiffuseP)
pgDiffuseP->reallocate(width, height, 2, GL_RGBA16F);
if (pgAmbientSpecular)
pgAmbientSpecular->reallocate(width, height, 3);
// И буфера глубины
if (pgrbo)
pgrbo->reallocate(width, height);
// SSAO
if (pssaoTexture)
pssaoTexture->reallocate(width, height, 6, GL_RED, GL_RED);
// Запомним новые размеры окна
WINDOW_WIDTH = width;
WINDOW_HEIGHT = height;
// Изменим параметры перспективной матрицы проекции для камеры
Camera::current().setPerspective(CAMERA_FOVy, (float)width/height);
}
bool firstMouse = true;
float lastX, lastY;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
glm::vec2 offset(xpos - lastX, lastY - ypos);
lastX = xpos;
lastY = ypos;
Camera::current().rotate(offset);
}
int main(void)
{
GLFWwindow* window; // Указатель на окно GLFW3
// Инициализация GLFW3
if (!glfwInit())
{
std::cout << "GLFW init error\n";
return -1;
}
// Завершение работы с GLFW3 перед выходом
atexit(glfwTerminate);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Минорная версия спецификаций OpenGL
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Контекст OpenGL, который поддерживает только основные функции
// Создание окна GLFW3 с заданными шириной, высотой и заголовком окна
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION, NULL, NULL);
if (!window)
{
std::cout << "GLFW create window error\n";
return -1;
}
// Установка основного контекста окна
glfwMakeContextCurrent(window);
// Установка callback-функции для изменения размеров окна и буфера кадра
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSwapInterval(1); // Вертикальная синхронизация
// Установка callback-функции для мыши и камеры
glfwSetCursorPosCallback(window, mouse_callback);
// Загрузка функций OpenGL с помощью GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "GLAD load GL error\n";
return -1;
}
// Включаем проверку по буферу глубины
glEnable(GL_DEPTH_TEST);
// Шейдер для G-буфера
ShaderProgram gShader;
// Загрузка и компиляция шейдеров
gShader.load(GL_VERTEX_SHADER, "shaders/gshader.vert");
gShader.load(GL_FRAGMENT_SHADER, "shaders/gshader.frag");
gShader.link();
// Установим значения текстур
const char* textures_base_shader_names[] = {"tex_diffuse", "tex_ambient", "tex_specular"};
gShader.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*));
// Загрузка сцены из obj файла
Scene scene = loadOBJtoScene("../resources/models/blob.obj", "../resources/models/", "../resources/textures/");
scene.root.e_scale() = glm::vec3(0.01);
scene.root.e_position().z = 1;
scene.models.begin()->material.kd = {0.5,0.5,0.5};
scene.models.begin()->material.ka = {0.2,0.2,0.2};
// Установка цвета очистки буфера цвета
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Сдвинем направленный источник света и камеру
Sun::get().e_direction().z = -1.0;
Camera::current().e_position().x = 0.3f;
// Источники света
Light& first = Light::getNew();
first.e_color() = {1.0f, 0.0f, 0.0f}; // цвет
first.e_position() = {0.3f, 0.0f, 0.6f}; // Позиция
first.e_angle() = 100.0f;
Light& second = Light::getNew();
second.e_color() = {0.0f, 0.0f, 1.0f}; // цвет
second.e_position() = {-0.3f, 0.3f, 0.5f}; // Позиция
// Uniform-буферы
UBO cameraUB(sizeof(CameraData), 0);
UBO material_data(sizeof(Material), 1);
UBO light_data(Light::getUBOsize(), 2);
UBO sun_data(sizeof(Sun), 3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Использование уменьшенных версий mipmap
// Создадим G-буфер с данными о используемых привязках
GLuint attachments[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
FBO gbuffer(attachments, sizeof(attachments) / sizeof(GLuint));
// Создадим текстуры для буфера кадра
Texture gPosition(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT0, 0, GL_RGB32F, GL_RGB); // Позиция вершины
Texture gNormal(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT1, 1, GL_RGB16F, GL_RGB); // Нормали
Texture gDiffuseP(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT2, 2, GL_RGBA16F); // Диффузная составляющая и коэф. глянцевости
Texture gAmbientSpecular(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT3, 3); // Фоновая составляющая и один канал зеркальной
// Создадим буфер рендера под буфер глубины и привяжем его
RBO grbo(WINDOW_WIDTH, WINDOW_HEIGHT);
gbuffer.assignRenderBuffer(grbo.getHandler());
// Активируем базовый буфер кадра
FBO::useDefault();
// Сохраним указатели на текстуры для изменения размеров окна
pgPosition = &gPosition;
pgNormal = &gNormal;
pgDiffuseP = &gDiffuseP;
pgAmbientSpecular = &gAmbientSpecular;
pgrbo = &grbo;
// Шейдер для расчета освещенности
ShaderProgram lightShader;
// Загрузка и компиляция шейдеров
lightShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
lightShader.load(GL_FRAGMENT_SHADER, "shaders/lighting.frag");
lightShader.link();
// Привязка текстур
const char* gtextures_shader_names[] = {"gPosition", "gNormal", "gDiffuseP", "gAmbientSpecular", "sunShadowDepth", "pointShadowDepth", "ssao"};
lightShader.bindTextures(gtextures_shader_names, sizeof(gtextures_shader_names)/sizeof(const char*));
// Загрузка данных о границах каскадов
glUniform1fv(lightShader.getUniformLoc("camera_cascade_distances"), CAMERA_CASCADE_COUNT, &camera_cascade_distances[1]);
glm::vec3 quadVertices[] = { {-1.0f, 1.0f, 0.0f}
, {-1.0f, -1.0f, 0.0f}
, { 1.0f, 1.0f, 0.0f}
, { 1.0f, -1.0f, 0.0f}
};
GLuint quadIndices[] = {0,1,2,2,1,3};
Model quadModel;
quadModel.load_verteces(quadVertices, 4);
quadModel.load_indices(quadIndices, 6);
// Размер текстуры тени от солнца
const GLuint sunShadow_resolution = 1024;
// Создадим буфер кадра для рендера теней
FBO sunShadowBuffer;
// Создадим текстуры для буфера кадра
TextureArray sunShadowDepth(CAMERA_CASCADE_COUNT, sunShadow_resolution, sunShadow_resolution, GL_DEPTH_ATTACHMENT, 4, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
// Правка фантомных теней
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float shadowBorderColor[] = { 1.0, 1.0, 1.0, 1.0 };
glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, shadowBorderColor);
// Отключим работу с цветом
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
// Активируем базовый буфер кадра
FBO::useDefault();
// Шейдер для расчета теней
ShaderProgram sunShadowShader;
// Загрузим шейдер
sunShadowShader.load(GL_VERTEX_SHADER, "shaders/sun_shadow.vert");
sunShadowShader.load(GL_GEOMETRY_SHADER, "shaders/sun_shadow.geom");
sunShadowShader.load(GL_FRAGMENT_SHADER, "shaders/empty.frag");
sunShadowShader.link();
// Размер одной стороны кубической карты
const GLuint pointShadow_resolution = 500;
// Создадим буфер кадра для рендера теней от источников света
FBO pointShadowBuffer;
// Создадим текстуры для буфера кадра
TextureCubeArray pointShadowDepth(MAX_LIGHTS, pointShadow_resolution, pointShadow_resolution, GL_DEPTH_ATTACHMENT, 5, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
// Отключим работу с цветом
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
// Активируем базовый буфер кадра
FBO::useDefault();
// Шейдер для расчета теней от точечных источников
ShaderProgram pointShadowShader;
// Загрузим шейдер
pointShadowShader.load(GL_VERTEX_SHADER, "shaders/sun_shadow.vert");
pointShadowShader.load(GL_GEOMETRY_SHADER, "shaders/point_shadow.geom");
pointShadowShader.load(GL_FRAGMENT_SHADER, "shaders/point_shadow.frag");
pointShadowShader.link();
// Создадим буфер для вычисления SSAO
GLuint attachments_ssao[] = { GL_COLOR_ATTACHMENT0 };
FBO ssaoBuffer(attachments_ssao, sizeof(attachments_ssao) / sizeof(GLuint));
// Создадим текстуры для буфера кадра
Texture ssaoTexture(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT0, 6, GL_RED, GL_RED);
pssaoTexture = &ssaoTexture;
// Активируем базовый буфер кадра
FBO::useDefault();
// Стандартные параметры SSAO
SSAO_data ssao_data;
// Расчет масштабирования текстуры шума
ssao_data.scale = {WINDOW_WIDTH/4,WINDOW_HEIGHT/4};
// Генерируем случайные векторы
std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // Генерирует случайные вещественные числа в заданном диапазоне
std::default_random_engine generator;
glm::vec3 sample; // Выборка
for (int i = 0; i < ssao_data.size; i++)
{
sample = { randomFloats(generator) * 2.0 - 1.0
, randomFloats(generator) * 2.0 - 1.0
, randomFloats(generator)
};
sample = glm::normalize(sample);
sample *= randomFloats(generator);
// Отмасштабируем выборку
sample *= 0.1 + 0.9 * (i / (float)ssao_data.size) * (i / (float)ssao_data.size);
ssao_data.samples[i] = sample;
}
// Загрузка данных в uniform-буфер
UBO ssaoUB(&ssao_data, sizeof(SSAO_data), 4);
// Модель прямоугольника
Model rectangle;
// Вершины прямоугольника
glm::vec3 rectangle_verticies[] = { {-0.5f, 0.0f, -0.5f}
, { 0.5f, 0.0f, -0.5f}
, { 0.5f, 0.0f, 0.5f}
, {-0.5f, 0.0f, 0.5f}
};
// Загрузка вершин модели
rectangle.load_verteces(rectangle_verticies, sizeof(rectangle_verticies)/sizeof(glm::vec3));
// индексы вершин
GLuint rectangle_indices[] = {0, 1, 2, 2, 3, 0};
// Загрузка индексов модели
rectangle.load_indices(rectangle_indices, sizeof(rectangle_indices)/sizeof(GLuint));
// Нормали
glm::vec3 rectangle_normals[] = { {0.0f, 1.0f, 0.0f}
, {0.0f, 1.0f, 0.0f}
, {0.0f, 1.0f, 0.0f}
, {0.0f, 1.0f, 0.0f}
};
// Загрузка нормалей модели
rectangle.load_normals(rectangle_normals, sizeof(rectangle_normals)/sizeof(glm::vec3));
// Зададим горизонтальное положение перед камерой
rectangle.e_position().y = -1;
rectangle.e_position().z = 2;
rectangle.e_scale() = glm::vec3(4);
// Параметры материала
rectangle.material.ka = {0.2, 0.2, 0.2};
rectangle.material.kd = {0.9, 0.9, 0.9};
// Шейдер для рисования отладочных лампочек
ShaderProgram bulbShader;
// Загрузка и компиляция шейдеров
bulbShader.load(GL_VERTEX_SHADER, "shaders/bulb.vert");
bulbShader.load(GL_FRAGMENT_SHADER, "shaders/bulb.frag");
bulbShader.link();
// Вершины для скайбокса
glm::vec3 skybox_verticies[] = {
{-1.0f, 1.0f, -1.0f},
{-1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f},
{-1.0f, -1.0f, 1.0f},
{-1.0f, -1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, 1.0f},
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{-1.0f, -1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f},
{-1.0f, -1.0f, 1.0f},
{-1.0f, 1.0f, -1.0f},
{ 1.0f, 1.0f, -1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, -1.0f},
{-1.0f, -1.0f, -1.0f},
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f}
};
// Модель скайбокса
Model skybox;
skybox.load_verteces(skybox_verticies, sizeof(skybox_verticies)/sizeof(glm::vec3));
TextureCube skybox_texture(TEX_DIFFUSE, { "../resources/textures/skybox/px.jpg"
, "../resources/textures/skybox/nx.jpg"
, "../resources/textures/skybox/py.jpg"
, "../resources/textures/skybox/ny.jpg"
, "../resources/textures/skybox/pz.jpg"
, "../resources/textures/skybox/nz.jpg"
});
// Шейдер для скайбокса
ShaderProgram skyboxShader;
// Загрузим шейдеры
skyboxShader.load(GL_VERTEX_SHADER, "shaders/skybox.vert");
skyboxShader.load(GL_FRAGMENT_SHADER, "shaders/skybox.frag");
skyboxShader.link();
// Привязка текстуры скайбокса
const char* skybox_shader_names[] = {"skybox"};
skyboxShader.bindTextures(skybox_shader_names, sizeof(skybox_shader_names)/sizeof(const char*));
// Пока не произойдет событие запроса закрытия окна
while(!glfwWindowShouldClose(window))
{
// Загрузка данных о камере
cameraUB.loadSub(&Camera::current().getData(), sizeof(CameraData));
// Загрузим информацию об источниках света
Light::upload(light_data);
// Загружаем информацию о направленном источнике
Sun::upload(sun_data);
// Активируем G-кадра
gbuffer.use();
// Используем шейдер с освещением
gShader.use();
// Очистка буфера цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Тут производится рендер
scene.render(gShader, material_data);
rectangle.render(gShader, material_data);
// Изменим размер вывода для тени
glViewport(0, 0, sunShadow_resolution, sunShadow_resolution);
// Активируем буфер кадра для теней от солнца
sunShadowBuffer.use();
// Подключим шейдер для расчета теней
sunShadowShader.use();
// Очистка буфера глубины
glClear(GL_DEPTH_BUFFER_BIT);
// Рендерим геометрию в буфер глубины
scene.render(sunShadowShader, material_data);
rectangle.render(sunShadowShader, material_data);
// Изменим размер вывода для стороны кубической карты точечного источника
glViewport(0, 0, pointShadow_resolution, pointShadow_resolution);
// Активируем буфер кадра для теней от солнца
pointShadowBuffer.use();
// Подключим шейдер для расчета теней
pointShadowShader.use();
// Очистка буфера глубины
glClear(GL_DEPTH_BUFFER_BIT);
// Для каждого источника вызывается рендер сцены
for (int i = 0; i < Light::getCount(); i++)
{
glUniform1i(pointShadowShader.getUniformLoc("light_i"), i);
// Рендерим геометрию в буфер глубины
scene.render(pointShadowShader, material_data);
rectangle.render(pointShadowShader, material_data);
}
// Изменим размер вывода для окна
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
// Активируем базовый буфер кадра
FBO::useDefault();
// Подключаем шейдер для прямоугольника
lightShader.use();
// Очистка буфера цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Подключаем текстуры G-буфера
gPosition.use();
gNormal.use();
gDiffuseP.use();
gAmbientSpecular.use();
// Подключаем текстуры теней
sunShadowDepth.use();
pointShadowDepth.use();
// Подключим текстуру SSAO
ssaoTexture.use();
// Рендерим прямоугольник с расчетом освещения
quadModel.render();
// Перенос буфера глубины
FBO::useDefault(GL_DRAW_FRAMEBUFFER); // Базовый в режиме записи
gbuffer.use(GL_READ_FRAMEBUFFER); // Буфер геометрии в режиме чтения
// Копирование значений глубины
glBlitFramebuffer(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
FBO::useDefault(); // Использование базового буфера для дальнейших работ
// Отрисовка скайбокса без записи глубины
glDepthMask(GL_FALSE);
// Используем шейдер для скайбокса
skyboxShader.use();
// Подключаем текстуру скайбокса
skybox_texture.use();
// Рендерим куб
skybox.render();
// Возвращаем запись глубины
glDepthMask(GL_TRUE);
// Отрисовка отладочных лампочек со специальным шейдером
bulbShader.use();
Light::render(bulbShader, material_data);
// Представление содержимого буфера цепочки показа на окно
glfwSwapBuffers(window);
// Обработка системных событий
glfwPollEvents();
}
return 0;
}