14/src/main.cpp

465 lines
20 KiB
C++
Raw Normal View History

2023-01-13 07:55:14 +00:00
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <GLM/glm.hpp>
#include <iostream>
#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;
2023-01-15 00:10:37 +00:00
Texture* pssaoTexture = NULL;
2023-01-13 07:55:14 +00:00
// Размеры окна
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);
2023-01-15 00:10:37 +00:00
// SSAO
if (pssaoTexture)
pssaoTexture->reallocate(width, height, 6, GL_RED, GL_RED);
2023-01-13 07:55:14 +00:00
// Запомним новые размеры окна
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"};
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();
2023-01-15 00:10:37 +00:00
// Создадим буфер для вычисления 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();
2023-01-13 07:55:14 +00:00
// Модель прямоугольника
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();
// Рендерим прямоугольник с расчетом освещения
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;
}