Копия с проекта 15
This commit is contained in:
parent
d17b372562
commit
a35b6bf468
20
.vscode/c_cpp_properties.json
vendored
Normal file
20
.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "some_name",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/include",
|
||||||
|
"${workspaceFolder}/../dependencies/GLFW/include",
|
||||||
|
"${workspaceFolder}/../dependencies/glad/include",
|
||||||
|
"${workspaceFolder}/../dependencies/glm",
|
||||||
|
"${workspaceFolder}/../dependencies/stb",
|
||||||
|
"${workspaceFolder}/../dependencies/tinyobjloader"
|
||||||
|
],
|
||||||
|
"compilerPath": "C:/MinGW/bin/g++.exe",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++11",
|
||||||
|
"intelliSenseMode": "gcc-x86"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
48
.vscode/settings.json
vendored
Normal file
48
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"fstream": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"typeinfo": "cpp"
|
||||||
|
}
|
||||||
|
}
|
79
.vscode/tasks.json
vendored
Normal file
79
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "cppbuild",
|
||||||
|
"label": "C/C++: g++.exe сборка активного файла",
|
||||||
|
"command": "C:/MinGW/bin/g++.exe",
|
||||||
|
"args": [
|
||||||
|
"-fdiagnostics-color=always",
|
||||||
|
"${workspaceRoot}/src/*.cpp",
|
||||||
|
"${workspaceRoot}/../dependencies/glad/src/glad.c",
|
||||||
|
|
||||||
|
"-I${workspaceRoot}/include",
|
||||||
|
|
||||||
|
"--std=c++11",
|
||||||
|
|
||||||
|
"-I${workspaceRoot}/../dependencies/GLFW/include",
|
||||||
|
"-L${workspaceRoot}/../dependencies/GLFW/lib-mingw",
|
||||||
|
"-I${workspaceFolder}/../dependencies/glad/include",
|
||||||
|
"-I${workspaceFolder}/../dependencies/glm",
|
||||||
|
"-I${workspaceFolder}/../dependencies/stb",
|
||||||
|
"-I${workspaceFolder}/../dependencies/tinyobjloader",
|
||||||
|
"-static",
|
||||||
|
"-lopengl32",
|
||||||
|
"-lglfw3dll",
|
||||||
|
"-o",
|
||||||
|
"${workspaceRoot}/${workspaceFolderBasename}.exe"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${fileDirname}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"detail": "Задача создана отладчиком."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cppbuild",
|
||||||
|
"label": "C/C++ x64: g++.exe сборка активного файла",
|
||||||
|
"command": "C:/MinGW64/bin/g++.exe",
|
||||||
|
"args": [
|
||||||
|
"-fdiagnostics-color=always",
|
||||||
|
"${workspaceRoot}/src/*.cpp",
|
||||||
|
"${workspaceRoot}/../dependencies/glad/src/glad.c",
|
||||||
|
|
||||||
|
"-I${workspaceRoot}/include",
|
||||||
|
|
||||||
|
"--std=c++11",
|
||||||
|
|
||||||
|
"-I${workspaceRoot}/../dependencies/GLFW/include",
|
||||||
|
"-L${workspaceRoot}/../dependencies/GLFW/lib-mingw-w64",
|
||||||
|
"-I${workspaceFolder}/../dependencies/glad/include",
|
||||||
|
"-I${workspaceFolder}/../dependencies/glm",
|
||||||
|
"-I${workspaceFolder}/../dependencies/stb",
|
||||||
|
"-I${workspaceFolder}/../dependencies/tinyobjloader",
|
||||||
|
"-static",
|
||||||
|
"-lopengl32",
|
||||||
|
"-lglfw3dll",
|
||||||
|
"-o",
|
||||||
|
"${workspaceRoot}/${workspaceFolderBasename}.exe"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${fileDirname}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"detail": "Задача создана отладчиком."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
88
include/Buffers.h
Normal file
88
include/Buffers.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#ifndef BUFFERS_H
|
||||||
|
#define BUFFERS_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
// Объект массива вершин
|
||||||
|
class VAO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VAO(); // Создает VAO и активирует его
|
||||||
|
~VAO(); // Уничтожает VAO
|
||||||
|
VAO(const VAO & copy); // Конструктор копирования
|
||||||
|
VAO& operator=(const VAO & other); // Оператор присваивания
|
||||||
|
|
||||||
|
void use(); // Активация VAO
|
||||||
|
static void disable(); // Деактивация активного VAO
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint handler; // Дескриптор
|
||||||
|
static std::map<GLuint, GLuint> handler_count; // Счетчик использований дескриптора
|
||||||
|
};
|
||||||
|
|
||||||
|
// Тип буфера
|
||||||
|
enum BUFFER_TYPE { VERTEX = GL_ARRAY_BUFFER
|
||||||
|
, ELEMENT = GL_ELEMENT_ARRAY_BUFFER
|
||||||
|
, UNIFORM = GL_UNIFORM_BUFFER
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объект вершинного буфера
|
||||||
|
class BO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BO(BUFFER_TYPE type); // Создает пустой буфер заданного типа
|
||||||
|
BO(BUFFER_TYPE type, const void *data, int size); // Создает и загружает туда данные
|
||||||
|
~BO(); // Уничтожает буфер
|
||||||
|
BO(const BO & copy); // Конструктор копирования
|
||||||
|
BO& operator=(const BO & other); // Оператор присваивания
|
||||||
|
|
||||||
|
void load(const void *data, int size, GLuint mode = GL_STATIC_DRAW); // Загрузка данных в буфер
|
||||||
|
void use();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
GLuint handler; // Дескриптор
|
||||||
|
BUFFER_TYPE type; // Тип буфера
|
||||||
|
private:
|
||||||
|
static std::map<GLuint, GLuint> handler_count; // Счетчик использований дескриптора
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объект uniform-буфера
|
||||||
|
class UBO : public BO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UBO(int size, int binding); // Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
UBO(const void *data, int size, int binding); // Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
|
||||||
|
void rebind(int binding); // Перепривязка
|
||||||
|
void loadSub(const void *data, int size, int offset = 0); // Загрузка с отступом
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объект буфера кадра
|
||||||
|
class FBO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FBO(GLuint *attachments = 0, int count = 0); // Создает буфер кадра с нужным числом прикреплений текстур
|
||||||
|
~FBO(); // Уничтожение буфера
|
||||||
|
|
||||||
|
void use(GLuint mode = GL_FRAMEBUFFER); // Активирует буфер кадра в заданном режиме
|
||||||
|
static void useDefault(GLuint mode = GL_FRAMEBUFFER); // Активирует базовый буфер в заданном режиме
|
||||||
|
void assignRenderBuffer(GLuint hander, GLuint attachment = GL_DEPTH_ATTACHMENT); // Привязка рендер буфера
|
||||||
|
protected:
|
||||||
|
GLuint handler; // Дескриптор
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объект буфера рендера
|
||||||
|
class RBO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RBO(int w, int h, GLuint component = GL_DEPTH_COMPONENT); // Создает буфер рендера с заданными параметрами размеров и используемых компонент
|
||||||
|
~RBO(); // Уничтожение буфера
|
||||||
|
|
||||||
|
GLuint getHandler(); // Возвращает дескриптор буфера рендера
|
||||||
|
protected:
|
||||||
|
GLuint handler; // Дескриптор
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BUFFERS_H
|
66
include/Camera.h
Normal file
66
include/Camera.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef CAMERA_H
|
||||||
|
#define CAMERA_H
|
||||||
|
|
||||||
|
#include <GLM/glm.hpp>
|
||||||
|
|
||||||
|
// Ближняя граница области отсечения
|
||||||
|
#define CAMERA_NEAR 0.1f
|
||||||
|
// Дальняя граница области отсечения
|
||||||
|
#define CAMERA_FAR 100.0f
|
||||||
|
// Вектор, задающий верх для камеры
|
||||||
|
#define CAMERA_UP_VECTOR glm::vec3(0.0f, 1.0f, 0.0f)
|
||||||
|
// Стандартный угол обзора
|
||||||
|
#define CAMERA_FOVy 60.0f
|
||||||
|
// Количество каскадов для карт теней
|
||||||
|
#define CAMERA_CASCADE_COUNT 4
|
||||||
|
|
||||||
|
// Данные о дистанциях каскадов
|
||||||
|
extern const float camera_cascade_distances[]; // src/Camera.cpp
|
||||||
|
|
||||||
|
struct CameraData
|
||||||
|
{
|
||||||
|
glm::mat4 projection;
|
||||||
|
glm::mat4 view;
|
||||||
|
glm::vec3 position;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Camera
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Camera(float aspect, const glm::vec3 &position = glm::vec3(0.0f, 0.0f, 0.0f), const glm::vec2 &xyOffset = glm::vec2(90.0f, 0.0f), float fovy = CAMERA_FOVy); // Конструктор камеры с проекцией перспективы
|
||||||
|
Camera(float width, float height, const glm::vec3 &position = glm::vec3(0.0f, 0.0f, 0.0f), const glm::vec2 &xyOffset = glm::vec2(90.0f, 0.0f)); // Конструктор ортографической камеры
|
||||||
|
virtual ~Camera(); // Деструктор
|
||||||
|
const glm::mat4& getVP(); // Возвращает ссылку на константную матрицу произведения матриц вида и проекции
|
||||||
|
const glm::mat4& getProjection(); // Возвращает ссылку на константную матрицу проекции
|
||||||
|
const glm::mat4& getView(); // Возвращает ссылку на константную матрицу вида
|
||||||
|
void rotate(const glm::vec2 &xyOffset); // Поворачивает камеру на dx и dy пикселей
|
||||||
|
void move(const glm::vec3 &posOffset); // Сдвигает камеру на указанный вектор (dx,dy,dz)
|
||||||
|
void setPosition(const glm::vec3 &position); // Устанавливает местоположение
|
||||||
|
void setRotation(const glm::vec2 &xyOffset); // Устанавливает угол поворота камеры
|
||||||
|
void setPerspective(float fov, float aspect); // Устанавливает заданную матрицу перспективы
|
||||||
|
void setOrtho(float width, float height); // Устанавливает заданную ортографическую матрицу
|
||||||
|
void setSensitivity(float sensitivity); // Изменяет чувствительность мыши
|
||||||
|
CameraData& getData(); // Данные о камере для шейдера
|
||||||
|
const glm::vec4 (*getProjCoords())[8];
|
||||||
|
protected:
|
||||||
|
Camera(const glm::vec3 &position, const glm::vec2 &xyOffset); // Защищенный (protected) констуктор камеры без перспективы
|
||||||
|
void recalcTarget(); // Пересчет цели, на которую смотрит камера
|
||||||
|
void recalcView(); // Пересчет матрицы вида
|
||||||
|
void recalcVP(); // Пересчет произведения матриц
|
||||||
|
|
||||||
|
glm::vec3 position; // Местоположение камеры
|
||||||
|
glm::vec3 target; // Цель, на которую смотрит камера
|
||||||
|
glm::vec2 currentRotation; // Текущий поворот камеры
|
||||||
|
glm::mat4 projection; // Матрица проекции
|
||||||
|
glm::mat4 view; // Матрица вида
|
||||||
|
glm::mat4 vp; // Матрица произведения вида и проекции
|
||||||
|
bool requiredRecalcVP; // Необходимость пересчета матрицы вида и проекции камеры
|
||||||
|
bool requiredRecalcView; // Необходимость пересчета матрицы вида камеры
|
||||||
|
bool requiredRecalcCoords; // Необходимость пересчета точек, описывающих трансф. объем проекции
|
||||||
|
float sensitivity; // Чувствительность мыши
|
||||||
|
glm::vec4 coords[CAMERA_CASCADE_COUNT][8]; // Координаты в проекции
|
||||||
|
glm::mat4 cascade_proj[CAMERA_CASCADE_COUNT]; // Матрицы проекций каскадов
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CAMERA_H
|
57
include/Lights.h
Normal file
57
include/Lights.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef LIGHTS_H
|
||||||
|
#define LIGHTS_H
|
||||||
|
|
||||||
|
#include <GLM/glm.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
|
#include "Camera.h"
|
||||||
|
|
||||||
|
// Максимальное число источников света
|
||||||
|
#define MAX_LIGHTS 64
|
||||||
|
// Максимальное число образцов для SSAO
|
||||||
|
#define MAX_SSAO 64
|
||||||
|
|
||||||
|
// Класс лампочки (точечный источник с возможностью отладочного вывода)
|
||||||
|
class Bulb
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Bulb(const glm::vec3 &pos = glm::vec3(0.0f, 0.0f, 0.0f), const glm::vec3 &color = glm::vec3(0.0f, 0.0f, 0.0f), float radius = 10.0f, float angle = 180, const glm::vec3 &direction = glm::vec3(0,0,1)); // Конструктор с координатами, цветом, радиусом и направлением
|
||||||
|
|
||||||
|
void render(ShaderProgram &shaderProgram, UBO &material_buffer); // Отрисовка отладочной лампы и сферы
|
||||||
|
|
||||||
|
alignas(16) glm::vec3 position; // Позиция
|
||||||
|
alignas(16) glm::vec3 color; // Цвет
|
||||||
|
void setRadius(float radius); // Задание радиуса и расчет коэф. угасания
|
||||||
|
float angle; // Угол освещенности
|
||||||
|
alignas(16) glm::vec3 direction; // Направление для прожектора
|
||||||
|
void recalc_pov(); // Пересчитывает матрицы проекции и трансформации в пространство источника
|
||||||
|
private:
|
||||||
|
float radius; // Радиус действия источника
|
||||||
|
alignas(16) glm::vec2 K; // линейный и квадратичный компоненты затухания
|
||||||
|
alignas(16) glm::mat4 vp[6]; // Матрицы проекции и трансформации в пространство источника
|
||||||
|
static GrouptedModel bulb_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс направленного источника освещения
|
||||||
|
class Sun
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Sun(const glm::vec3 &direction = glm::vec3(0.0f, 1.0f, 0.0f), const glm::vec3 &color = glm::vec3(0.4f, 0.4f, 0.4f));
|
||||||
|
alignas(16) glm::vec3 direction; // Направление лучей на источник
|
||||||
|
alignas(16) glm::vec3 color; // Цвет
|
||||||
|
void upload(ShaderProgram &shaderProgram); // Загрузка данных об источнике на шейдер
|
||||||
|
void pov(ShaderProgram &shaderProgram, Camera camera); // Загружает матрицу проекции и трансформации в пространство источника
|
||||||
|
};
|
||||||
|
|
||||||
|
// Данные для SSAO
|
||||||
|
struct SSAO_data
|
||||||
|
{
|
||||||
|
float radius = 0.05f;
|
||||||
|
float bias = 0.025f;
|
||||||
|
int size = MAX_SSAO;
|
||||||
|
alignas(16) glm::vec2 scale;
|
||||||
|
glm::vec3 samples[MAX_SSAO];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LIGHTS_H
|
74
include/Model.h
Normal file
74
include/Model.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#ifndef MODEL_H
|
||||||
|
#define MODEL_H
|
||||||
|
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include "Texture.h"
|
||||||
|
#include "Shader.h"
|
||||||
|
|
||||||
|
#include <GLM/glm.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define DEFAULT_MTL_DIR "./"
|
||||||
|
class GrouptedModel loadOBJtoGroupted(const char* filename, const char* mtl_directory = DEFAULT_MTL_DIR, const char* texture_directory = DEFAULT_MTL_DIR);
|
||||||
|
|
||||||
|
struct Material
|
||||||
|
{
|
||||||
|
alignas(16) glm::vec3 ka; // коэф. фонового отражения (цвет фонового освещения)
|
||||||
|
alignas(16) glm::vec3 kd; // коэф. диффузного отражения (цвет объекта)
|
||||||
|
alignas(16) glm::vec3 ks; // коэф. зеркального блика
|
||||||
|
float p; // показатель глянцевости
|
||||||
|
// Значения по умолчанию
|
||||||
|
Material() : ka(0.2f), kd(0.2f), ks(0.2f), p(1) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс определяющий положение, вращение и размер объекта
|
||||||
|
class Movable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Movable(); // Конструктор без параметров
|
||||||
|
Movable(const Movable& copy); // Конструктор копирования
|
||||||
|
|
||||||
|
glm::vec3 position; // позиция модели
|
||||||
|
glm::vec3 rotation; // поворот модели
|
||||||
|
glm::vec3 scale; // масштабирование модели
|
||||||
|
glm::mat4 getTransformMatrix(); // Матрица трансформации модели
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс модели с примененным материалом
|
||||||
|
class Model : public Movable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Model(); // Конструктор без параметров
|
||||||
|
Model(const Model& copy); // Конструктор копирования
|
||||||
|
~Model();
|
||||||
|
void render(); // Вызов отрисовки без uniform-даных
|
||||||
|
void render(ShaderProgram &shaderProgram, UBO &material_buffer, const glm::mat4& global_tranform = glm::mat4(1)); // Вызов отрисовки
|
||||||
|
void load_verteces(glm::vec3* verteces, GLuint count); // Загрузка вершин в буфер
|
||||||
|
void load_indices(GLuint* indices, GLuint count); // Загрузка индексов в буфер
|
||||||
|
void load_texCoords(glm::vec2* texCoords, GLuint count); // Загрузка текстурных координат в буфер
|
||||||
|
void load_normals(glm::vec3* normals, GLuint count); // Загрузка нормалей в буфер
|
||||||
|
void set_texture(Texture& texture); // Привязка текстуры к модели
|
||||||
|
void set_index_range(GLuint beg, GLuint count); // Ограничение диапазона из буфера индексов
|
||||||
|
|
||||||
|
Material material; // Материал модели
|
||||||
|
private:
|
||||||
|
VAO vao;
|
||||||
|
BO vertex_vbo, index_vbo; // вершинный и индексный буферы
|
||||||
|
BO normals_vbo, texCoords_vbo; // буферы с нормалями и текстурными координатами
|
||||||
|
GLuint verteces_count; // Количество вершин
|
||||||
|
GLuint first_index, indices_count; // Первый и количество индексов
|
||||||
|
Texture texture_diffuse; // Диффузная текстура
|
||||||
|
Texture texture_ambient; // Текстура фонового освщения
|
||||||
|
Texture texture_specular; // Текстура зеркального отражения
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс сгруппированной модели
|
||||||
|
class GrouptedModel: public Movable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void render(ShaderProgram &shaderProgram, UBO &material_buffer); // Вызов отрисовки
|
||||||
|
std::vector<Model> parts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MODEL_H
|
30
include/Shader.h
Normal file
30
include/Shader.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef SHADER_H
|
||||||
|
#define SHADER_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Класс шейдерной программы
|
||||||
|
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<int, int> handler_count; // Получение количества использований по дескриптору шейдера (Shared pointer)
|
||||||
|
std::map<const char*, GLuint> uniformLocations; // Местоположения uniform-переменных
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHADER_H
|
83
include/Texture.h
Normal file
83
include/Texture.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#ifndef TEXTURE_H
|
||||||
|
#define TEXTURE_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
enum TexType {
|
||||||
|
TEX_DIFFUSE,
|
||||||
|
TEX_AMBIENT,
|
||||||
|
TEX_SPECULAR,
|
||||||
|
TEX_AVAILABLE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
// Абстрактный класс базовой текстуры
|
||||||
|
class BaseTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~BaseTexture();
|
||||||
|
virtual void use() = 0; // Привязка текстуры
|
||||||
|
static void disable(GLuint type); // Отвязка текстуры по типу
|
||||||
|
GLuint getType(); // Возвращает тип текстуры
|
||||||
|
void setType(GLuint type); // Задает тип текстуры
|
||||||
|
protected:
|
||||||
|
GLuint handler; // Дескриптор текстуры
|
||||||
|
GLuint type; // Тип текстуры, соответствует её слоту
|
||||||
|
static std::map<std::string, int> filename_handler; // Получение дескриптора текстуры по её имени
|
||||||
|
static std::map<int, int> handler_count; // Получение количества использований по дескриптору текстуры (Shared pointer)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс 2D текстуры
|
||||||
|
class Texture : public BaseTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Texture(GLuint type = TEX_AVAILABLE_COUNT, const std::string& filename = ""); // Загрузка текстуры с диска или использование "пустой"
|
||||||
|
Texture(GLuint width, GLuint height, GLuint attachment, GLuint texType = TEX_DIFFUSE, GLint internalformat = GL_RGBA, GLint format = GL_RGBA, GLenum dataType = GL_FLOAT); // Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
Texture(GLuint width, GLuint height, void* data, GLuint texType = TEX_DIFFUSE, GLint internalformat = GL_RGBA, GLint format = GL_RGBA, GLenum dataType = GL_FLOAT); // Конструктор текстуры заданного размера без привязки к буферу с загрузкой пикселей по указателю
|
||||||
|
Texture(const Texture& other); // Конструктор копирования
|
||||||
|
|
||||||
|
Texture& operator=(const Texture& other); // Оператор присваивания
|
||||||
|
|
||||||
|
virtual void use(); // Привязка текстуры
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс 3D текстуры
|
||||||
|
class TextureArray : public BaseTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextureArray(GLuint levels, GLuint width, GLuint height, GLuint attachment, GLuint texType = TEX_DIFFUSE, GLint internalformat = GL_RGBA, GLint format = GL_RGBA, GLenum dataType = GL_FLOAT); // Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
TextureArray(const TextureArray& other); // Конструктор копирования
|
||||||
|
|
||||||
|
TextureArray& operator=(const TextureArray& other); // Оператор присваивания
|
||||||
|
|
||||||
|
virtual void use(); // Привязка текстуры
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс кубической текстуры
|
||||||
|
class TextureCube : public BaseTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextureCube(GLuint type = TEX_AVAILABLE_COUNT, const std::string (&filename)[6] = {""}); // Загрузка текстуры с диска или использование "пустой"
|
||||||
|
TextureCube(GLuint width, GLuint height, GLuint attachment, GLuint texType = TEX_DIFFUSE, GLint internalformat = GL_RGBA, GLint format = GL_RGBA, GLenum dataType = GL_FLOAT); // Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
TextureCube(const TextureCube& other); // Конструктор копирования
|
||||||
|
|
||||||
|
TextureCube& operator=(const TextureCube& other); // Оператор присваивания
|
||||||
|
|
||||||
|
virtual void use(); // Привязка текстуры
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс 3D кубической текстуры
|
||||||
|
class TextureCubeArray : public BaseTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextureCubeArray(GLuint levels, GLuint width, GLuint height, GLuint attachment, GLuint texType = TEX_DIFFUSE, GLint internalformat = GL_RGBA, GLint format = GL_RGBA, GLenum dataType = GL_FLOAT); // Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
TextureCubeArray(const TextureCubeArray& other); // Конструктор копирования
|
||||||
|
|
||||||
|
TextureCubeArray& operator=(const TextureCubeArray& other); // Оператор присваивания
|
||||||
|
|
||||||
|
virtual void use(); // Привязка текстуры
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXTURE_H
|
30
shaders/bulb.frag
Normal file
30
shaders/bulb.frag
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(std140, binding = 1) uniform Material
|
||||||
|
{
|
||||||
|
vec3 ka;
|
||||||
|
vec3 kd;
|
||||||
|
vec3 ks;
|
||||||
|
float p;
|
||||||
|
};
|
||||||
|
|
||||||
|
in vec3 pos_local;
|
||||||
|
|
||||||
|
layout(std140, binding = 4) uniform gamma
|
||||||
|
{
|
||||||
|
float inv_gamma;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
uniform float angle;
|
||||||
|
uniform vec3 direction;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float cosA = dot(normalize(pos_local), normalize(direction));
|
||||||
|
if (degrees(acos(cosA)) <= angle)
|
||||||
|
color = vec4(pow(ka, vec3(inv_gamma)), 1);
|
||||||
|
else
|
||||||
|
discard;
|
||||||
|
}
|
20
shaders/bulb.vert
Normal file
20
shaders/bulb.vert
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 pos;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform Camera
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
vec3 position;
|
||||||
|
} camera;
|
||||||
|
|
||||||
|
uniform mat4 model;
|
||||||
|
|
||||||
|
out vec3 pos_local;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
pos_local = pos;
|
||||||
|
gl_Position = camera.projection * camera.view * model * vec4(pos, 1.0);
|
||||||
|
}
|
6
shaders/empty.frag
Normal file
6
shaders/empty.frag
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
38
shaders/gshader.frag
Normal file
38
shaders/gshader.frag
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(std140, binding = 1) uniform Material
|
||||||
|
{
|
||||||
|
vec3 ka;
|
||||||
|
vec3 kd;
|
||||||
|
vec3 ks;
|
||||||
|
float p;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout (location = 0) out vec3 gPosition;
|
||||||
|
layout (location = 1) out vec3 gNormal;
|
||||||
|
layout (location = 2) out vec4 gDiffuseP;
|
||||||
|
layout (location = 3) out vec4 gAmbientSpecular;
|
||||||
|
|
||||||
|
in vec3 vertex; // Позиция вершины в пространстве
|
||||||
|
in vec3 N; // Нормаль трансформированноая
|
||||||
|
in vec2 texCoord; // Текстурные координаты
|
||||||
|
|
||||||
|
uniform sampler2D tex_diffuse;
|
||||||
|
uniform sampler2D tex_ambient;
|
||||||
|
uniform sampler2D tex_specular;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Сохранение позиции фрагмента в G-буфере
|
||||||
|
gPosition = vertex;
|
||||||
|
// Сохранение нормали в G-буфере
|
||||||
|
gNormal = N;
|
||||||
|
// Сохранение диффузного цвета
|
||||||
|
gDiffuseP.rgb = texture(tex_diffuse, texCoord).rgb * kd;
|
||||||
|
// Сохранение глянцевости
|
||||||
|
gDiffuseP.a = p;
|
||||||
|
// Сохранение фоновой составляющей
|
||||||
|
gAmbientSpecular.rgb = texture(tex_ambient, texCoord).rgb * ka;
|
||||||
|
// Сохранение зеркальной составляющей
|
||||||
|
gAmbientSpecular.a = texture(tex_specular, texCoord).r * ks.r;
|
||||||
|
}
|
30
shaders/gshader.vert
Normal file
30
shaders/gshader.vert
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 pos;
|
||||||
|
layout(location = 1) in vec2 inTexCoord;
|
||||||
|
layout(location = 2) in vec3 normals;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform Camera
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
vec3 position;
|
||||||
|
} camera;
|
||||||
|
|
||||||
|
uniform mat4 model;
|
||||||
|
|
||||||
|
out vec3 vertex; // Позиция вершины в пространстве
|
||||||
|
out vec3 N; // Нормаль трансформированноая
|
||||||
|
out vec2 texCoord; // Текстурные координаты
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 P = model * vec4(pos, 1.0); // трансформация вершины
|
||||||
|
vertex = P.xyz;
|
||||||
|
|
||||||
|
N = normalize(mat3(model) * normals); // трансформация нормали
|
||||||
|
|
||||||
|
texCoord = inTexCoord; // Текстурные координаты
|
||||||
|
|
||||||
|
gl_Position = camera.projection * camera.view * P;
|
||||||
|
}
|
199
shaders/lighting.frag
Normal file
199
shaders/lighting.frag
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform Camera
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
vec3 position;
|
||||||
|
} camera;
|
||||||
|
|
||||||
|
struct LightData
|
||||||
|
{
|
||||||
|
vec3 position;
|
||||||
|
vec3 color;
|
||||||
|
float angle;
|
||||||
|
vec3 direction;
|
||||||
|
float radius;
|
||||||
|
vec2 K;
|
||||||
|
mat4 vp[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std140, binding = 2) uniform Light
|
||||||
|
{
|
||||||
|
LightData data[64];
|
||||||
|
int count;
|
||||||
|
} light_f;
|
||||||
|
|
||||||
|
uniform vec3 Sun_direction;
|
||||||
|
uniform vec3 Sun_color;
|
||||||
|
uniform mat4 Sun_VP[4]; // Размер массива должен соответствовать количеству каскадов
|
||||||
|
|
||||||
|
uniform float camera_cascade_distances[4]; // Размер массива должен соответствовать количеству каскадов
|
||||||
|
|
||||||
|
uniform sampler2D gPosition;
|
||||||
|
uniform sampler2D gNormal;
|
||||||
|
uniform sampler2D gDiffuseP;
|
||||||
|
uniform sampler2D gAmbientSpecular;
|
||||||
|
uniform sampler2DArray sunShadowDepth;
|
||||||
|
uniform samplerCubeArray pointShadowDepth;
|
||||||
|
uniform sampler2D ssao;
|
||||||
|
|
||||||
|
layout(std140, binding = 4) uniform gamma
|
||||||
|
{
|
||||||
|
float inv_gamma;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Получим данные из текстур буфера
|
||||||
|
vec3 fragPos = texture(gPosition, texCoord).rgb;
|
||||||
|
vec3 N = texture(gNormal, texCoord).rgb;
|
||||||
|
vec3 kd = texture(gDiffuseP, texCoord).rgb;
|
||||||
|
vec3 ka = texture(gAmbientSpecular, texCoord).rgb;
|
||||||
|
float ks = texture(gAmbientSpecular, texCoord).a;
|
||||||
|
float p = texture(gDiffuseP, texCoord).a;
|
||||||
|
float ssao_value = texture(ssao, texCoord).r;
|
||||||
|
|
||||||
|
// Переменные используемые в цикле:
|
||||||
|
vec3 L_vertex; // Расположение источника относительно фрагмента
|
||||||
|
float L_distance; // Расстояние от поверхности до источника
|
||||||
|
vec3 Cam_vertex = normalize(camera.position - fragPos); // Расположение камеры относительно фрагмента
|
||||||
|
float diffuse; // Диффузная составляющая
|
||||||
|
vec3 H; // Вектор половины пути
|
||||||
|
float specular; // Зеркальная составляющая
|
||||||
|
float attenuation; // Угасание с учетом расстояния
|
||||||
|
float acosA; // Косинус между вектором от поверхности к источнику и обратным направлением источника
|
||||||
|
float intensity; // Интенсивность для прожектора
|
||||||
|
vec3 fragPosLightSpace; // Фрагмент в пространстве источника
|
||||||
|
vec4 fragPosCamSpace = camera.view * vec4(fragPos, 1); // Фрагмент в пространстве камеры
|
||||||
|
float shadowValue; // Значение затененности
|
||||||
|
vec2 texelSize; // Размер текселя текстуры теней
|
||||||
|
int x, y, z; // Счетчик для PCF
|
||||||
|
float pcfDepth; // Глубина PCF
|
||||||
|
int cascade_index; // Индекс текущего каскада для вычисления теней
|
||||||
|
float cubemap_offset = 0.05f; // Отступ в текстурных координатах для PCF
|
||||||
|
float cubemap_depth; // Дистанция между фрагментом и источником в диапазоне [0;1]
|
||||||
|
|
||||||
|
// Определение индекса каскада в который попадает фрагмент (цикл на 1 меньше чем кол-во каскадов)
|
||||||
|
for (cascade_index = 0; cascade_index < 3; cascade_index++)
|
||||||
|
if (abs(fragPosCamSpace.z) < camera_cascade_distances[cascade_index])
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Фоновая освещенность
|
||||||
|
color = vec4(ka, 1) * ssao_value;
|
||||||
|
|
||||||
|
// Расчет солнца, если его цвет не черный
|
||||||
|
if (length(Sun_color) > 0)
|
||||||
|
{
|
||||||
|
// Расположение фрагмента в координатах теневой карты
|
||||||
|
fragPosLightSpace = (Sun_VP[cascade_index] * vec4(fragPos, 1.0)).xyz;
|
||||||
|
// Переход от [-1;1] к [0;1]
|
||||||
|
fragPosLightSpace = (fragPosLightSpace + vec3(1.0)) / 2;
|
||||||
|
// Сдвиг для решения проблемы акне
|
||||||
|
fragPosLightSpace.z -= max(0.05 * (1.0 - dot(N, Sun_direction)), 0.005);
|
||||||
|
// Проверка PCF
|
||||||
|
shadowValue = 0.0;
|
||||||
|
texelSize = 1.0 / textureSize(sunShadowDepth, 0).xy; // Размер текселя текстуры теней
|
||||||
|
for(x = -1; x <= 1; ++x)
|
||||||
|
{
|
||||||
|
for(y = -1; y <= 1; ++y)
|
||||||
|
{
|
||||||
|
pcfDepth = texture(sunShadowDepth, vec3(fragPosLightSpace.xy + vec2(x, y) * texelSize, cascade_index)).r;
|
||||||
|
shadowValue += fragPosLightSpace.z > pcfDepth ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadowValue /= 9.0;
|
||||||
|
// Рассчитываем освещенность, если значение тени меньше 1
|
||||||
|
if (shadowValue < 1.0)
|
||||||
|
{
|
||||||
|
// Данные об источнике относительно фрагмента
|
||||||
|
L_vertex = normalize(Sun_direction);
|
||||||
|
// Диффузная составляющая
|
||||||
|
diffuse = max(dot(L_vertex, N), 0.0); // скалярное произведение с отсеканием значений < 0
|
||||||
|
|
||||||
|
// Вектор половины пути
|
||||||
|
H = normalize(L_vertex + Cam_vertex);
|
||||||
|
// Зеркальная составляющая
|
||||||
|
specular = pow(max(dot(H, N), 0.0), p*4); // скалярное произведение с отсеканием значений < 0 в степени p
|
||||||
|
// Результирующий цвет с учетом солнца
|
||||||
|
color += ( vec4(Sun_color*kd*diffuse, 1)
|
||||||
|
+ vec4(Sun_color*ks*specular, 1) ) * (1.0 - shadowValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Цикл по источникам света
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < light_f.count; i++)
|
||||||
|
{
|
||||||
|
// Обнулим значение тени
|
||||||
|
shadowValue = 0;
|
||||||
|
// Позиция фрагмента относительно источника
|
||||||
|
fragPosLightSpace = fragPos - light_f.data[i].position;
|
||||||
|
// Дистанция между фрагментом и источником в диапазоне [0;1]
|
||||||
|
cubemap_depth = length(fragPosLightSpace) / light_f.data[i].radius;
|
||||||
|
// Сдвиг для решения проблемы акне
|
||||||
|
cubemap_depth -= max(0.05 * (1.0 - dot(N, Sun_direction)), 0.005);
|
||||||
|
for(x = -1; x <= 1; ++x)
|
||||||
|
{
|
||||||
|
for(y = -1; y <= 1; ++y)
|
||||||
|
{
|
||||||
|
for(z = -1; z <= 1; ++z)
|
||||||
|
{
|
||||||
|
// Значение из кубической текстуры с учетом источника (i)
|
||||||
|
pcfDepth = texture(pointShadowDepth, vec4(fragPosLightSpace + vec3(x, y, z)*cubemap_offset, i)).r;
|
||||||
|
if(cubemap_depth > pcfDepth)
|
||||||
|
shadowValue += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadowValue /= (27);
|
||||||
|
if (shadowValue < 1.0)
|
||||||
|
{
|
||||||
|
// Данные об источнике относительно фрагмента
|
||||||
|
L_vertex = light_f.data[i].position - fragPos;
|
||||||
|
// Расстояние от поверхности до источника
|
||||||
|
L_distance = length(L_vertex);
|
||||||
|
|
||||||
|
// Проверка на дистанцию
|
||||||
|
if (L_distance < light_f.data[i].radius)
|
||||||
|
{
|
||||||
|
// Нормирование вектора
|
||||||
|
L_vertex = normalize(L_vertex);
|
||||||
|
// арккосинус между вектором от поверхности к источнику и обратным направлением источника
|
||||||
|
acosA = degrees(acos(dot(-L_vertex, normalize(light_f.data[i].direction))));
|
||||||
|
// Если угол меньше угла источника или угол источника минимален, то считаем освещенность
|
||||||
|
if(acosA <= light_f.data[i].angle)
|
||||||
|
{
|
||||||
|
// Диффузная составляющая
|
||||||
|
diffuse = max(dot(L_vertex, N), 0.0); // скалярное произведение с отсеканием значений < 0
|
||||||
|
|
||||||
|
// Вектор половины пути
|
||||||
|
H = normalize(L_vertex + Cam_vertex);
|
||||||
|
// Зеркальная составляющая
|
||||||
|
specular = pow(max(dot(H, N), 0.0), p*4); // скалярное произведение с отсеканием значений < 0 в степени p
|
||||||
|
|
||||||
|
// Угасание с учетом расстояния
|
||||||
|
attenuation = 1 / (1 + light_f.data[i].K[0] * L_distance + light_f.data[i].K[1] * L_distance * L_distance);
|
||||||
|
|
||||||
|
// Если источник - прожектор, то добавим смягчение
|
||||||
|
if (light_f.data[i].angle < 180)
|
||||||
|
{
|
||||||
|
intensity = clamp((light_f.data[i].angle - acosA) / 5, 0.0, 1.0);
|
||||||
|
diffuse *= intensity;
|
||||||
|
specular *= intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
color += ( vec4(light_f.data[i].color*kd*diffuse * attenuation, 1)
|
||||||
|
+ vec4(light_f.data[i].color*ks*specular * attenuation, 1) ) * (1.0 - shadowValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Применение гамма-коррекции
|
||||||
|
color.rgb = pow(color.rgb, vec3(inv_gamma));
|
||||||
|
}
|
16
shaders/point_shadow.frag
Normal file
16
shaders/point_shadow.frag
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#version 330 core
|
||||||
|
in vec4 FragPos;
|
||||||
|
in vec3 lightPos;
|
||||||
|
in float radius;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Расстояние между источником и фрагментом
|
||||||
|
float lightDistance = length(FragPos.xyz - lightPos);
|
||||||
|
|
||||||
|
// Приведение к диапазону [0;1]
|
||||||
|
lightDistance = lightDistance / radius;
|
||||||
|
|
||||||
|
// Замена значения глубины
|
||||||
|
gl_FragDepth = lightDistance;
|
||||||
|
}
|
40
shaders/point_shadow.geom
Normal file
40
shaders/point_shadow.geom
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#version 420 core
|
||||||
|
layout (triangles, invocations = 6) in; // здесь invocations соответствует числу сторон кубической карты теней
|
||||||
|
layout (triangle_strip, max_vertices=18) out; // здесь max_vertices = 3 вершины * 6 вызовов на стороны куба
|
||||||
|
|
||||||
|
struct LightData
|
||||||
|
{
|
||||||
|
vec3 position;
|
||||||
|
vec3 color;
|
||||||
|
float angle;
|
||||||
|
vec3 direction;
|
||||||
|
float radius;
|
||||||
|
vec2 K;
|
||||||
|
mat4 vp[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std140, binding = 2) uniform Light
|
||||||
|
{
|
||||||
|
LightData data[64];
|
||||||
|
int count;
|
||||||
|
} light_g;
|
||||||
|
|
||||||
|
uniform int light_i;
|
||||||
|
|
||||||
|
out vec4 FragPos;
|
||||||
|
out vec3 lightPos;
|
||||||
|
out float radius;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
FragPos = gl_in[i].gl_Position;
|
||||||
|
lightPos = light_g.data[light_i].position;
|
||||||
|
radius = light_g.data[light_i].radius;
|
||||||
|
gl_Position = light_g.data[light_i].vp[gl_InvocationID] * gl_in[i].gl_Position;
|
||||||
|
gl_Layer = gl_InvocationID + light_i*6;
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
11
shaders/quad.vert
Normal file
11
shaders/quad.vert
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 pos;
|
||||||
|
|
||||||
|
out vec2 texCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = vec4(pos, 1.0);
|
||||||
|
texCoord = (pos.xy + vec2(1.0)) / 2; // Переход от [-1;1] к [0;1]
|
||||||
|
}
|
17
shaders/skybox.frag
Normal file
17
shaders/skybox.frag
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#version 420 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
in vec3 TexCoords;
|
||||||
|
|
||||||
|
uniform samplerCube skybox;
|
||||||
|
|
||||||
|
layout(std140, binding = 4) uniform gamma
|
||||||
|
{
|
||||||
|
float inv_gamma;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
FragColor.rgb = pow(texture(skybox, TexCoords).rgb, vec3(inv_gamma));
|
||||||
|
gl_FragDepth = 0.9999f;
|
||||||
|
}
|
17
shaders/skybox.vert
Normal file
17
shaders/skybox.vert
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#version 420 core
|
||||||
|
layout (location = 0) in vec3 pos;
|
||||||
|
|
||||||
|
out vec3 TexCoords;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform Camera
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
vec3 position;
|
||||||
|
} camera;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
TexCoords = pos;
|
||||||
|
gl_Position = camera.projection * mat4(mat3(camera.view)) * vec4(pos, 1.0);
|
||||||
|
}
|
62
shaders/ssao.frag
Normal file
62
shaders/ssao.frag
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
|
||||||
|
out float occlusion;
|
||||||
|
|
||||||
|
uniform sampler2D gPosition;
|
||||||
|
uniform sampler2D gNormal;
|
||||||
|
uniform sampler2D noise;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform Camera
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
vec3 position;
|
||||||
|
} camera;
|
||||||
|
|
||||||
|
layout(std140, binding = 3) uniform SSAO
|
||||||
|
{
|
||||||
|
float radius;
|
||||||
|
float bias;
|
||||||
|
int size;
|
||||||
|
vec2 scale;
|
||||||
|
vec3 samples[64];
|
||||||
|
} ssao;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Получим информацию из текстур для данного фрагмента по текстурным координатам
|
||||||
|
vec3 fragPos = (camera.view * vec4(texture(gPosition, texCoord).xyz, 1)).xyz;
|
||||||
|
vec3 normal = normalize(texture(gNormal, texCoord).rgb);
|
||||||
|
vec3 randomVec = normalize(texture(noise, texCoord * ssao.scale).xyz);
|
||||||
|
// Расчет TBN матрицы
|
||||||
|
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
|
||||||
|
vec3 bitangent = cross(normal, tangent);
|
||||||
|
mat3 TBN = mat3(tangent, bitangent, normal);
|
||||||
|
|
||||||
|
float sampleDepth; // Значение глубины образца выборки
|
||||||
|
vec3 samplePos; // Выборка, ориентированная в пространстве вида камеры
|
||||||
|
vec4 sampleCoord; // Выборка, преобразованная к текстурным координатам
|
||||||
|
float rangeCheck; // Проверка диапазона
|
||||||
|
|
||||||
|
// Проинициализируем значение счетчика и запустим цикл по выборкам
|
||||||
|
occlusion = 0;
|
||||||
|
for(int i = 0; i < ssao.size; i++)
|
||||||
|
{
|
||||||
|
samplePos = TBN * ssao.samples[i]; // в TBN-пространстве
|
||||||
|
samplePos = fragPos + samplePos * ssao.radius; // в пространстве вида камеры
|
||||||
|
|
||||||
|
sampleCoord = camera.projection * vec4(samplePos, 1.0);
|
||||||
|
sampleCoord.xyz /= sampleCoord.w; // Деление на значение перспективы
|
||||||
|
sampleCoord.xyz = sampleCoord.xyz * 0.5 + 0.5; // Трансформация в диапазон [0.0; 1.0]
|
||||||
|
|
||||||
|
// Получаем значение глубины по образцу выборки
|
||||||
|
sampleDepth = (camera.view * vec4(texture(gPosition, sampleCoord.xy).rgb, 1)).z;
|
||||||
|
|
||||||
|
rangeCheck = smoothstep(0.0, 1.0, ssao.radius / abs(fragPos.z - sampleDepth));
|
||||||
|
occlusion += (sampleDepth >= samplePos.z + ssao.bias ? 1.0 : 0.0) * rangeCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
occlusion = 1 - (occlusion / ssao.size);
|
||||||
|
}
|
23
shaders/ssaoBlur.frag
Normal file
23
shaders/ssaoBlur.frag
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
|
||||||
|
out float occlusion;
|
||||||
|
|
||||||
|
uniform sampler2D ssao;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 texelSize = 1.0 / vec2(textureSize(ssao, 0));
|
||||||
|
vec2 offset;
|
||||||
|
occlusion = 0.0;
|
||||||
|
for (int x = -2; x < 2; x++)
|
||||||
|
{
|
||||||
|
for (int y = -2; y < 2; y++)
|
||||||
|
{
|
||||||
|
offset = vec2(x, y) * texelSize;
|
||||||
|
occlusion += texture(ssao, texCoord + offset).r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
occlusion = occlusion / (4.0 * 4.0);
|
||||||
|
}
|
17
shaders/sun_shadow.geom
Normal file
17
shaders/sun_shadow.geom
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(triangles, invocations = 4) in; // здесь invocations должно соответствовать количеству каскадов
|
||||||
|
layout(triangle_strip, max_vertices = 3) out;
|
||||||
|
|
||||||
|
uniform mat4 Sun_VP[4]; // Матрицы вида и проекции каскадов
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
gl_Position = Sun_VP[gl_InvocationID] * gl_in[i].gl_Position;
|
||||||
|
gl_Layer = gl_InvocationID;
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
10
shaders/sun_shadow.vert
Normal file
10
shaders/sun_shadow.vert
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 pos;
|
||||||
|
|
||||||
|
uniform mat4 model;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = model * vec4(pos, 1.0);
|
||||||
|
}
|
196
src/Buffers.cpp
Normal file
196
src/Buffers.cpp
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
#include "Buffers.h"
|
||||||
|
|
||||||
|
// Счетчики использований дескрипторов
|
||||||
|
std::map<GLuint, GLuint> VAO::handler_count;
|
||||||
|
std::map<GLuint, GLuint> BO::handler_count;
|
||||||
|
|
||||||
|
// Создает VAO и активирует его
|
||||||
|
VAO::VAO()
|
||||||
|
{
|
||||||
|
glGenVertexArrays(1, &handler); // Генерация одного объекта массива вершин
|
||||||
|
glBindVertexArray(handler); // Привязка для использования
|
||||||
|
handler_count[handler] = 1; // Инициализация счетчика для дескриптора
|
||||||
|
}
|
||||||
|
|
||||||
|
// Уничтожает VAO
|
||||||
|
VAO::~VAO()
|
||||||
|
{
|
||||||
|
// Если дескриптор никем не используется - освободим его
|
||||||
|
if (!--handler_count[handler])
|
||||||
|
{
|
||||||
|
glDeleteVertexArrays(1, &handler);
|
||||||
|
handler_count.erase(handler); // Удаление из словаря
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
VAO::VAO(const VAO & copy) : handler(copy.handler)
|
||||||
|
{
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
VAO& VAO::operator=(const VAO & other)
|
||||||
|
{
|
||||||
|
// Если это разные дескрипторы
|
||||||
|
if (handler != other.handler)
|
||||||
|
{ // то следуюет удалить текущий перед заменой
|
||||||
|
this->~VAO();
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Активация VAO
|
||||||
|
void VAO::use()
|
||||||
|
{
|
||||||
|
glBindVertexArray(handler); // Привязка VAO для использования
|
||||||
|
}
|
||||||
|
|
||||||
|
// Деактивация активного VAO
|
||||||
|
void VAO::disable()
|
||||||
|
{
|
||||||
|
glBindVertexArray(0); // Отключение VAO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает пустой буфер заданного типа
|
||||||
|
BO::BO(BUFFER_TYPE t) : type(t)
|
||||||
|
{
|
||||||
|
glGenBuffers(1, &handler); // Генерация одного объекта буфера
|
||||||
|
handler_count[handler] = 1;
|
||||||
|
use(); // Привязка буфера
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает и загружает туда данные
|
||||||
|
BO::BO(BUFFER_TYPE t, const void *data, int size) : BO(t)
|
||||||
|
{
|
||||||
|
load(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Уничтожает буфер
|
||||||
|
BO::~BO()
|
||||||
|
{
|
||||||
|
if (handler) // Если буфер был создан
|
||||||
|
{
|
||||||
|
// Если дескриптор никем не используется - освободим его
|
||||||
|
if (!--handler_count[handler])
|
||||||
|
{
|
||||||
|
glDeleteBuffers(1, &handler);
|
||||||
|
handler_count.erase(handler); // Удаление из словаря
|
||||||
|
}
|
||||||
|
handler = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
BO::BO(const BO & copy) : handler(copy.handler), type(copy.type)
|
||||||
|
{
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
BO& BO::operator=(const BO & other)
|
||||||
|
{
|
||||||
|
// Если это разные дескрипторы
|
||||||
|
if (handler != other.handler)
|
||||||
|
{ // то следуюет удалить текущий перед заменой
|
||||||
|
this->~BO();
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
// Изменим тип
|
||||||
|
type = other.type;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка вершин в буфер
|
||||||
|
void BO::load(const void *data, int size, GLuint mode)
|
||||||
|
{
|
||||||
|
use(); // Привязка буфера
|
||||||
|
glBufferData(type, size, data, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BO::use()
|
||||||
|
{
|
||||||
|
glBindBuffer(type, handler); // Привязка элементного буфера
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
UBO::UBO(int size, int binding) : BO(UNIFORM, 0, size)
|
||||||
|
{
|
||||||
|
rebind(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
UBO::UBO(const void *data, int size, int binding) : BO(UNIFORM, data, size)
|
||||||
|
{
|
||||||
|
rebind(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// перепривязка
|
||||||
|
void UBO::rebind(int binding)
|
||||||
|
{
|
||||||
|
glBindBufferBase(type, binding, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка с отступом
|
||||||
|
void UBO::loadSub(const void *data, int size, int offset)
|
||||||
|
{
|
||||||
|
use();
|
||||||
|
glBufferSubData(type, offset, size, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает буфер кадра с нужным числом прикреплений текстур
|
||||||
|
FBO::FBO(GLuint *attachments, int count)
|
||||||
|
{
|
||||||
|
glGenFramebuffers(1, &handler);
|
||||||
|
use();
|
||||||
|
glDrawBuffers(count, attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Уничтожение буфера
|
||||||
|
FBO::~FBO()
|
||||||
|
{
|
||||||
|
glDeleteFramebuffers(1, &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Активирует буфер кадра в заданном режиме
|
||||||
|
void FBO::use(GLuint mode)
|
||||||
|
{
|
||||||
|
glBindFramebuffer(mode, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Активирует базовый буфер в заданном режиме
|
||||||
|
void FBO::useDefault(GLuint mode)
|
||||||
|
{
|
||||||
|
glBindFramebuffer(mode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка рендер буфера
|
||||||
|
void FBO::assignRenderBuffer(GLuint hander, GLuint attachment)
|
||||||
|
{
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, hander);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает буфер рендера с заданными параметрами размеров и используемых компонент
|
||||||
|
RBO::RBO(int w, int h, GLuint component)
|
||||||
|
{
|
||||||
|
glGenRenderbuffers(1, &handler);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, handler);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Уничтожение буфера
|
||||||
|
RBO::~RBO()
|
||||||
|
{
|
||||||
|
glDeleteRenderbuffers(1, &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает дескриптор буфера рендера
|
||||||
|
GLuint RBO::getHandler()
|
||||||
|
{
|
||||||
|
return handler;
|
||||||
|
}
|
190
src/Camera.cpp
Normal file
190
src/Camera.cpp
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#include "Camera.h"
|
||||||
|
|
||||||
|
#include <GLM/gtc/matrix_transform.hpp>
|
||||||
|
#include <GLM/ext/matrix_transform.hpp>
|
||||||
|
|
||||||
|
// Границы каскадов
|
||||||
|
const float camera_cascade_distances[] = {CAMERA_NEAR, CAMERA_FAR / 50.0f, CAMERA_FAR / 10.0f, CAMERA_FAR / 3.0f, CAMERA_FAR};
|
||||||
|
|
||||||
|
// Защищенный (protected) конструктор камеры без перспективы
|
||||||
|
Camera::Camera(const glm::vec3 &pos, const glm::vec2 &xyOffset)
|
||||||
|
: position(pos), currentRotation(xyOffset)
|
||||||
|
{
|
||||||
|
sensitivity = 0.05;
|
||||||
|
recalcTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор камеры с проекцией перспективы
|
||||||
|
Camera::Camera(float aspect, const glm::vec3 &position, const glm::vec2 &xyOffset, float fovy)
|
||||||
|
: Camera(position, xyOffset)
|
||||||
|
{
|
||||||
|
setPerspective(fovy, aspect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор ортографической камеры
|
||||||
|
Camera::Camera(float width, float height, const glm::vec3 &position, const glm::vec2 &xyOffset)
|
||||||
|
: Camera(position, xyOffset)
|
||||||
|
{
|
||||||
|
setOrtho(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Деструктор
|
||||||
|
Camera::~Camera() { }
|
||||||
|
|
||||||
|
// Пересчет цели, на которую смотрит камера
|
||||||
|
void Camera::recalcTarget()
|
||||||
|
{
|
||||||
|
if(currentRotation.y > 89.0f)
|
||||||
|
currentRotation.y = 89.0f;
|
||||||
|
if(currentRotation.y < -89.0f)
|
||||||
|
currentRotation.y = -89.0f;
|
||||||
|
|
||||||
|
target.x = cos(glm::radians(currentRotation.x)) * cos(glm::radians(currentRotation.y));
|
||||||
|
target.y = sin(glm::radians(currentRotation.y));
|
||||||
|
target.z = sin(glm::radians(currentRotation.x)) * cos(glm::radians(currentRotation.y));
|
||||||
|
|
||||||
|
requiredRecalcView = true;
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пересчет матрицы вида
|
||||||
|
void Camera::recalcView()
|
||||||
|
{
|
||||||
|
view = glm::lookAt(position, position + target, CAMERA_UP_VECTOR);
|
||||||
|
requiredRecalcView = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пересчет произведения матриц
|
||||||
|
void Camera::recalcVP()
|
||||||
|
{
|
||||||
|
vp = projection * view;
|
||||||
|
requiredRecalcVP = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает ссылку на константную матрицу проекции
|
||||||
|
const glm::mat4& Camera::getProjection()
|
||||||
|
{
|
||||||
|
return projection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает ссылку на константную матрицу вида
|
||||||
|
const glm::mat4& Camera::getView()
|
||||||
|
{
|
||||||
|
if (requiredRecalcView)
|
||||||
|
recalcView();
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает ссылку на константную матрицу вида
|
||||||
|
const glm::mat4& Camera::getVP()
|
||||||
|
{
|
||||||
|
if (requiredRecalcVP)
|
||||||
|
{
|
||||||
|
if (requiredRecalcView)
|
||||||
|
recalcView();
|
||||||
|
recalcVP();
|
||||||
|
}
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Поворачивает камеру на dx и dy пикселей
|
||||||
|
void Camera::rotate(const glm::vec2 &xyOffset)
|
||||||
|
{
|
||||||
|
currentRotation += xyOffset * sensitivity;
|
||||||
|
|
||||||
|
recalcTarget();
|
||||||
|
requiredRecalcView = true;
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
requiredRecalcCoords = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сдвигает камеру на указанный вектор (dx,dy,dz)
|
||||||
|
void Camera::move(const glm::vec3 &posOffset)
|
||||||
|
{
|
||||||
|
position += posOffset;
|
||||||
|
|
||||||
|
requiredRecalcView = true;
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
requiredRecalcCoords = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливает местоположение
|
||||||
|
void Camera::setPosition(const glm::vec3 &pos)
|
||||||
|
{
|
||||||
|
position = pos;
|
||||||
|
|
||||||
|
requiredRecalcView = true;
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
requiredRecalcCoords = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливает угол поворота камеры
|
||||||
|
void Camera::setRotation(const glm::vec2 &xyOffset)
|
||||||
|
{
|
||||||
|
currentRotation = xyOffset;
|
||||||
|
recalcTarget();
|
||||||
|
requiredRecalcCoords = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливает заданную матрицу перспективы
|
||||||
|
void Camera::setPerspective(float fovy, float aspect)
|
||||||
|
{
|
||||||
|
projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR);
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
requiredRecalcCoords = true;
|
||||||
|
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||||
|
cascade_proj[cascade] = glm::perspective(glm::radians(fovy), aspect, camera_cascade_distances[cascade], camera_cascade_distances[cascade+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливает заданную ортографическую матрицу
|
||||||
|
void Camera::setOrtho(float width, float height)
|
||||||
|
{
|
||||||
|
const float aspect = width / height;
|
||||||
|
projection = glm::ortho(-1.0f, 1.0f, -1.0f/aspect, 1.0f/aspect, CAMERA_NEAR, CAMERA_FAR);
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
requiredRecalcCoords = true;
|
||||||
|
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||||
|
cascade_proj[cascade] = glm::ortho(-1.0f, 1.0f, -1.0f/aspect, 1.0f/aspect, camera_cascade_distances[cascade], camera_cascade_distances[cascade+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Изменяет чувствительность мыши
|
||||||
|
void Camera::setSensitivity(float sens)
|
||||||
|
{
|
||||||
|
sensitivity = sens;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Данные о камере для шейдера
|
||||||
|
CameraData& Camera::getData()
|
||||||
|
{
|
||||||
|
static CameraData data;
|
||||||
|
data = {getProjection(), getView(), position};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Доступ к координатам проекции
|
||||||
|
const glm::vec4 (*Camera::getProjCoords())[8]
|
||||||
|
{
|
||||||
|
if (requiredRecalcCoords)
|
||||||
|
{
|
||||||
|
glm::vec4 typical_points[8] = { { 1, 1, 1,1}
|
||||||
|
, { 1, 1,-1,1}
|
||||||
|
, { 1,-1, 1,1}
|
||||||
|
, { 1,-1,-1,1}
|
||||||
|
, {-1, 1, 1,1}
|
||||||
|
, {-1, 1,-1,1}
|
||||||
|
, {-1,-1, 1,1}
|
||||||
|
, {-1,-1,-1,1}};
|
||||||
|
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||||
|
{
|
||||||
|
glm::mat4 inv = glm::inverse(cascade_proj[cascade] * getView());
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
coords[cascade][i] = inv * typical_points[i];
|
||||||
|
coords[cascade][i] /= coords[cascade][i].w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requiredRecalcCoords = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return coords;
|
||||||
|
}
|
153
src/Lights.cpp
Normal file
153
src/Lights.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include "Lights.h"
|
||||||
|
|
||||||
|
#include <GLM/gtc/matrix_transform.hpp>
|
||||||
|
#include <GLM/ext/matrix_transform.hpp>
|
||||||
|
|
||||||
|
void genShpere(Model& model, float radius, int sectorsCount); // Model.cpp
|
||||||
|
|
||||||
|
// Статическое поле для модели лампочки
|
||||||
|
GrouptedModel Bulb::bulb_model;
|
||||||
|
|
||||||
|
// Конструктор с координатами, цветом и радиусом
|
||||||
|
Bulb::Bulb(const glm::vec3 &pos, const glm::vec3 &c, float r, float a, const glm::vec3 &dir)
|
||||||
|
{
|
||||||
|
// Если отладочная модель не загружена - загрузим
|
||||||
|
if (!bulb_model.parts.size())
|
||||||
|
{
|
||||||
|
bulb_model = loadOBJtoGroupted("../resources/models/bulb.obj", "../resources/models/", "../resources/textures/");
|
||||||
|
|
||||||
|
Model radius_sphere;
|
||||||
|
// Сгенерируем и загрузим меш сферы
|
||||||
|
genShpere(radius_sphere, 1, 16);
|
||||||
|
bulb_model.parts.insert(bulb_model.parts.begin(), radius_sphere);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраним данные о параметрах источника:
|
||||||
|
position = pos;
|
||||||
|
color = c;
|
||||||
|
radius = r;
|
||||||
|
K[0] = 4.5/radius;
|
||||||
|
K[1] = 4 * K[0] * K[0];
|
||||||
|
angle = a;
|
||||||
|
direction = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отрисовка отладочной лампы и сферы
|
||||||
|
void Bulb::render(ShaderProgram &shaderProgram, UBO &material_buffer)
|
||||||
|
{
|
||||||
|
// Расположение uniform-переменных
|
||||||
|
GLuint model_uniform = shaderProgram.getUniformLoc("model");
|
||||||
|
GLuint angle_uniform = shaderProgram.getUniformLoc("angle");
|
||||||
|
GLuint direction_uniform = shaderProgram.getUniformLoc("direction");
|
||||||
|
|
||||||
|
// Загрузим направление
|
||||||
|
glUniform3fv(direction_uniform, 1, &direction[0]);
|
||||||
|
|
||||||
|
// Зададим параметры материала сфере действия
|
||||||
|
bulb_model.parts[0].material.ka = color;
|
||||||
|
bulb_model.parts[0].position = position;
|
||||||
|
bulb_model.parts[0].scale = {radius, radius, radius};
|
||||||
|
|
||||||
|
// Угол для сферы (рисуем направленный конус)
|
||||||
|
glUniform1f(angle_uniform, angle);
|
||||||
|
|
||||||
|
// Рисование сферы покрытия источника в режиме линий
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||||
|
bulb_model.parts[0].render(shaderProgram, material_buffer);
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
|
||||||
|
// Угол для лампочки = 180 (рисуем целую модель)
|
||||||
|
glUniform1f(angle_uniform, 180); // Зададим параметры материала сфере действия
|
||||||
|
|
||||||
|
// Зададим цвет для колбы (первая в составе модели)
|
||||||
|
bulb_model.parts[1].material.ka = color;
|
||||||
|
|
||||||
|
bulb_model.position = position;
|
||||||
|
glm::mat4 transform = bulb_model.getTransformMatrix();
|
||||||
|
for (int i = 1; i < bulb_model.parts.size(); i++)
|
||||||
|
{
|
||||||
|
bulb_model.parts[i].render(shaderProgram, material_buffer, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Задание радиуса и расчет коэф. угасания
|
||||||
|
void Bulb::setRadius(float r)
|
||||||
|
{
|
||||||
|
radius = r;
|
||||||
|
K[0] = 4.5/radius;
|
||||||
|
K[1] = 4 * K[0] * K[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор направленного источника с параметрами направления и цвета
|
||||||
|
Sun::Sun(const glm::vec3 &dir, const glm::vec3 &c) : direction(dir), color(c)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка данных об источнике на шейдер
|
||||||
|
void Sun::upload(ShaderProgram &shaderProgram)
|
||||||
|
{
|
||||||
|
GLuint direction_uniform = shaderProgram.getUniformLoc("Sun_direction");
|
||||||
|
GLuint color_uniform = shaderProgram.getUniformLoc("Sun_color");
|
||||||
|
|
||||||
|
// Загрузим данные
|
||||||
|
glUniform3fv(direction_uniform, 1, &direction[0]);
|
||||||
|
glUniform3fv(color_uniform, 1, &color[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружает матрицу проекции и трансформации в пространство источника
|
||||||
|
void Sun::pov(ShaderProgram &shaderProgram, Camera camera)
|
||||||
|
{
|
||||||
|
// Точки по краям проекции камеры
|
||||||
|
const glm::vec4 (*projCoords)[8] = camera.getProjCoords();
|
||||||
|
|
||||||
|
glm::vec3 mean; // Среднее арифметическое
|
||||||
|
glm::vec4 max, min; // макс и мин координаты
|
||||||
|
glm::vec4 point; // Точка приведенная в пространство источника света
|
||||||
|
|
||||||
|
glm::mat4 lightView; // Матрица вида для вычисляемого каскада
|
||||||
|
glm::mat4 results[CAMERA_CASCADE_COUNT]; // Результат вычисления каждого каскада
|
||||||
|
|
||||||
|
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||||
|
{
|
||||||
|
mean = glm::vec3(0);
|
||||||
|
// Найдем среднее арифметическое от точек для нахождения центра прямоугольника
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
mean += glm::vec3(projCoords[cascade][i]);
|
||||||
|
mean /= 8;
|
||||||
|
// Используем среднее арифметическое для получения матрицы вида параллельного источника
|
||||||
|
lightView = glm::lookAt(mean + glm::normalize(direction), mean, CAMERA_UP_VECTOR);
|
||||||
|
|
||||||
|
// Примем первую точку как минимальную и максимальную (приведя в пространство вида источника)
|
||||||
|
min = max = lightView * projCoords[cascade][0];
|
||||||
|
// Для оставшихся точек
|
||||||
|
for (int i = 1; i < 8; i++)
|
||||||
|
{
|
||||||
|
// Приведем в пространство вида источника
|
||||||
|
point = lightView * projCoords[cascade][i];
|
||||||
|
max = glm::max(max, point);
|
||||||
|
min = glm::min(min, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Максимальное значение глубины
|
||||||
|
max.z = std::max(fabs(max.z), fabs(min.z));
|
||||||
|
// На основании максимальных и минимальных координат создадим матрицу проекции источника
|
||||||
|
results[cascade] = glm::ortho(min.x, max.x, min.y, max.y, min.z, max.z) * lightView;
|
||||||
|
}
|
||||||
|
// Загрузим данные о матрице проекции на выбранный шейдер
|
||||||
|
GLuint lightVP_uniform = shaderProgram.getUniformLoc("Sun_VP");
|
||||||
|
glUniformMatrix4fv(lightVP_uniform, CAMERA_CASCADE_COUNT, GL_FALSE, &results[0][0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пересчитывает матрицы проекции и трансформации в пространство источника
|
||||||
|
void Bulb::recalc_pov()
|
||||||
|
{
|
||||||
|
float near_plane = 0.1f;
|
||||||
|
glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), 1.0f, near_plane, radius);
|
||||||
|
vp[0] = shadowProj * glm::lookAt(position, position + glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||||
|
vp[1] = shadowProj * glm::lookAt(position, position + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||||
|
vp[2] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
|
vp[3] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f));
|
||||||
|
vp[4] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||||
|
vp[5] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||||
|
}
|
417
src/Model.cpp
Normal file
417
src/Model.cpp
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
#include "Model.h"
|
||||||
|
#include "Camera.h"
|
||||||
|
extern Camera camera;
|
||||||
|
|
||||||
|
Movable::Movable() : position(0), rotation(0), scale(1) {}
|
||||||
|
// Конструктор копирования
|
||||||
|
Movable::Movable(const Movable& copy) : position(copy.position), rotation(copy.rotation), scale(copy.scale) {}
|
||||||
|
|
||||||
|
// Конструктор без параметров
|
||||||
|
Model::Model() : verteces_count(0), first_index(0), indices_count(0),
|
||||||
|
vertex_vbo(VERTEX), index_vbo(ELEMENT), normals_vbo(VERTEX), texCoords_vbo(VERTEX)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
Model::Model(const Model& copy) :
|
||||||
|
vao(copy.vao),
|
||||||
|
verteces_count(copy.verteces_count), first_index(copy.first_index), indices_count(copy.indices_count),
|
||||||
|
vertex_vbo(copy.vertex_vbo), index_vbo(copy.index_vbo), normals_vbo(copy.normals_vbo), texCoords_vbo(copy.texCoords_vbo),
|
||||||
|
texture_diffuse(copy.texture_diffuse), texture_ambient(copy.texture_ambient), texture_specular(copy.texture_specular), material(copy.material)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Model::~Model()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вызов отрисовки без uniform-даных
|
||||||
|
void Model::render()
|
||||||
|
{
|
||||||
|
// Подключаем VAO
|
||||||
|
vao.use();
|
||||||
|
// Если есть индексы - рисуем с их использованием
|
||||||
|
if (indices_count)
|
||||||
|
glDrawElements(GL_TRIANGLES, indices_count, GL_UNSIGNED_INT, (void*)(first_index*sizeof(GLuint)));
|
||||||
|
// Если есть вершины - рисуем на основании массива вершин
|
||||||
|
else if (verteces_count)
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, verteces_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вызов отрисовки
|
||||||
|
void Model::render(ShaderProgram &shaderProgram, UBO &material_buffer, const glm::mat4& global_tranform)
|
||||||
|
{
|
||||||
|
// Расчитаем матрицу трансформации
|
||||||
|
glm::mat4 model = global_tranform * this->getTransformMatrix();
|
||||||
|
GLuint model_uniform = shaderProgram.getUniformLoc("model");
|
||||||
|
glUniformMatrix4fv(model_uniform, 1, GL_FALSE, &model[0][0]);
|
||||||
|
|
||||||
|
// Подключаем текстуры
|
||||||
|
texture_diffuse.use();
|
||||||
|
texture_ambient.use();
|
||||||
|
texture_specular.use();
|
||||||
|
|
||||||
|
|
||||||
|
// Загружаем данные о материале
|
||||||
|
material_buffer.load(&material, sizeof(material));
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для конфигурации атрибута вершинного буфера
|
||||||
|
void vertex_attrib_config()
|
||||||
|
{
|
||||||
|
// Включаем необходимый атрибут у выбранного VAO
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
// Устанавливаем связь между VAO и привязанным VBO
|
||||||
|
glVertexAttribPointer( 0 // индекс атрибута, должен совпадать с Layout шейдера
|
||||||
|
, 3 // количество компонент одного элемента
|
||||||
|
, GL_FLOAT // тип
|
||||||
|
, GL_FALSE // нормализованность значений
|
||||||
|
, 0 // шаг
|
||||||
|
, (void *)0 // отступ с начала массива
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка вершин в буфер
|
||||||
|
void Model::load_verteces(glm::vec3* verteces, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO и вершинный буфер
|
||||||
|
vao.use();
|
||||||
|
vertex_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
vertex_vbo.load(verteces, sizeof(glm::vec3)*count);
|
||||||
|
vertex_attrib_config();
|
||||||
|
// Запоминаем количество вершин для отрисовки
|
||||||
|
verteces_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка индексов в буфер
|
||||||
|
void Model::load_indices(GLuint* indices, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO и индексный буфер
|
||||||
|
vao.use();
|
||||||
|
index_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
index_vbo.load(indices, sizeof(GLuint)*count);
|
||||||
|
// Запоминаем количество вершин для отрисовки
|
||||||
|
indices_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для конфигурации атрибута вершинного буфера
|
||||||
|
void texCoords_attrib_config()
|
||||||
|
{
|
||||||
|
// Включаем необходимый атрибут у выбранного VAO
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
// Устанавливаем связь между VAO и привязанным VBO
|
||||||
|
glVertexAttribPointer( 1 // индекс атрибута, должен совпадать с Layout шейдера
|
||||||
|
, 2 // количество компонент одного элемента
|
||||||
|
, GL_FLOAT // тип
|
||||||
|
, GL_FALSE // нормализованность значений
|
||||||
|
, 0 // шаг
|
||||||
|
, (void *)0 // отступ с начала массива
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка текстурных координат в буфер
|
||||||
|
void Model::load_texCoords(glm::vec2* texCoords, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO
|
||||||
|
vao.use();
|
||||||
|
|
||||||
|
texCoords_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
texCoords_vbo.load(texCoords, sizeof(glm::vec2)*count);
|
||||||
|
texCoords_attrib_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для конфигурации атрибута вершинного буфера
|
||||||
|
void normals_attrib_config()
|
||||||
|
{
|
||||||
|
// Включаем необходимый атрибут у выбранного VAO
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
// Устанавливаем связь между VAO и привязанным VBO
|
||||||
|
glVertexAttribPointer( 2 // индекс атрибута, должен совпадать с Layout шейдера
|
||||||
|
, 3 // количество компонент одного элемента
|
||||||
|
, GL_FLOAT // тип
|
||||||
|
, GL_FALSE // нормализованность значений
|
||||||
|
, 0 // шаг
|
||||||
|
, (void *)0 // отступ с начала массива
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка нормалей в буфер
|
||||||
|
void Model::load_normals(glm::vec3* normals, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO
|
||||||
|
vao.use();
|
||||||
|
|
||||||
|
normals_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
normals_vbo.load(normals, sizeof(glm::vec3)*count);
|
||||||
|
normals_attrib_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
// Матрица трансформации модели
|
||||||
|
glm::mat4 Movable::getTransformMatrix()
|
||||||
|
{
|
||||||
|
glm::mat4 transformMatrix = glm::mat4(1.0f);
|
||||||
|
// Перемещение модели
|
||||||
|
transformMatrix = glm::translate(transformMatrix, position);
|
||||||
|
// Поворот модели
|
||||||
|
transformMatrix = glm::rotate(transformMatrix, glm::radians(rotation.x), glm::vec3(1.0, 0.0, 0.0));
|
||||||
|
transformMatrix = glm::rotate(transformMatrix, glm::radians(rotation.y), glm::vec3(0.0, 1.0, 0.0));
|
||||||
|
transformMatrix = glm::rotate(transformMatrix, glm::radians(rotation.z), glm::vec3(0.0, 0.0, 1.0));
|
||||||
|
// Масштабирование
|
||||||
|
transformMatrix = glm::scale(transformMatrix, scale);
|
||||||
|
|
||||||
|
return transformMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка текстуры к модели
|
||||||
|
void Model::set_texture(Texture& texture)
|
||||||
|
{
|
||||||
|
GLuint type = texture.getType();
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case TEX_DIFFUSE:
|
||||||
|
texture_diffuse = texture;
|
||||||
|
break;
|
||||||
|
case TEX_AMBIENT:
|
||||||
|
texture_ambient = texture;
|
||||||
|
break;
|
||||||
|
case TEX_SPECULAR:
|
||||||
|
texture_specular = texture;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ограничение диапазона из буфера индексов
|
||||||
|
void Model::set_index_range(GLuint beg, GLuint count)
|
||||||
|
{
|
||||||
|
first_index = beg;
|
||||||
|
indices_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TINYOBJLOADER_IMPLEMENTATION
|
||||||
|
#include "tiny_obj_loader.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
inline void hash_combine(std::size_t& seed) { }
|
||||||
|
|
||||||
|
template <typename T, typename... Rest>
|
||||||
|
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
|
||||||
|
std::hash<T> hasher;
|
||||||
|
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
||||||
|
hash_combine(seed, rest...);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrouptedModel loadOBJtoGroupted(const char* filename, const char* mtl_directory, const char* texture_directory)
|
||||||
|
{
|
||||||
|
GrouptedModel result;
|
||||||
|
Model model;
|
||||||
|
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
|
||||||
|
// Значение гамма-коррекции
|
||||||
|
extern float inv_gamma;
|
||||||
|
|
||||||
|
// Если в процессе загрузки возникли ошибки - выдадим исключение
|
||||||
|
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, mtl_directory))
|
||||||
|
throw std::runtime_error(err);
|
||||||
|
|
||||||
|
std::vector<GLuint> indices; // индексы модели
|
||||||
|
std::vector<glm::vec3> verteces; // вершины
|
||||||
|
std::vector<glm::vec3> normals; // нормали
|
||||||
|
std::vector<glm::vec2> texCords; // текстурные координаты
|
||||||
|
size_t hash; // Для уникальных вершин
|
||||||
|
std::map <int, int> uniqueVerteces; // словарь для уникальных вершин: ключ - хеш, значение - индекс вершины
|
||||||
|
|
||||||
|
int last_material_index = 0; // индекс последнего материала (для группировки моделей)
|
||||||
|
int count = 0, offset; // для индексов начала и конца в индексном буфере
|
||||||
|
std::vector<int> materials_range; // хранилище индексов
|
||||||
|
std::vector<int> materials_ids; // индексы материалов
|
||||||
|
|
||||||
|
materials_range.push_back(count); // Закидываем начало отрезка в индексном буфере
|
||||||
|
// Цикл по считанным моделям
|
||||||
|
for (const auto& shape : shapes)
|
||||||
|
{
|
||||||
|
offset = count; // Переменная для
|
||||||
|
last_material_index = shape.mesh.material_ids[(count - offset)/3]; // Запоминаем индекс материала
|
||||||
|
|
||||||
|
// Цикл по индексам модели
|
||||||
|
for (const auto& index : shape.mesh.indices)
|
||||||
|
{
|
||||||
|
hash = 0;
|
||||||
|
hash_combine( hash
|
||||||
|
, attrib.vertices[3 * index.vertex_index + 0], attrib.vertices[3 * index.vertex_index + 1], attrib.vertices[3 * index.vertex_index + 2]
|
||||||
|
, attrib.normals[3 * index.normal_index + 0], attrib.normals[3 * index.normal_index + 1], attrib.normals[3 * index.normal_index + 2]
|
||||||
|
, attrib.texcoords[2 * index.texcoord_index + 0], attrib.texcoords[2 * index.texcoord_index + 1]);
|
||||||
|
|
||||||
|
if (!uniqueVerteces.count(hash))
|
||||||
|
{
|
||||||
|
uniqueVerteces[hash] = verteces.size();
|
||||||
|
|
||||||
|
// группируем вершины в массив на основании индексов
|
||||||
|
verteces.push_back({ attrib.vertices[3 * index.vertex_index + 0]
|
||||||
|
, attrib.vertices[3 * index.vertex_index + 1]
|
||||||
|
, attrib.vertices[3 * index.vertex_index + 2]
|
||||||
|
});
|
||||||
|
// группируем нормали в массив на основании индексов
|
||||||
|
normals.push_back({ attrib.normals[3 * index.normal_index + 0]
|
||||||
|
, attrib.normals[3 * index.normal_index + 1]
|
||||||
|
, attrib.normals[3 * index.normal_index + 2]
|
||||||
|
});
|
||||||
|
// группируем текстурные координаты в массив на основании индексов
|
||||||
|
texCords.push_back({ attrib.texcoords[2 * index.texcoord_index + 0]
|
||||||
|
, 1-attrib.texcoords[2 * index.texcoord_index + 1]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Сохраняем индекс в массив
|
||||||
|
indices.push_back(uniqueVerteces[hash]);
|
||||||
|
|
||||||
|
// Если индекс последнего материала изменился, то необходимо сохранить его
|
||||||
|
if (last_material_index != shape.mesh.material_ids[(count - offset)/3])
|
||||||
|
{
|
||||||
|
materials_range.push_back(count); // как конец отрезка
|
||||||
|
materials_ids.push_back(last_material_index); // как используемый материал
|
||||||
|
last_material_index = shape.mesh.material_ids[(count - offset)/3];
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
} // for (const auto& index : shape.mesh.indices)
|
||||||
|
|
||||||
|
// Если последний материал не загружен - загружаем его
|
||||||
|
if (materials_range[materials_range.size()-1] != count-1)
|
||||||
|
{
|
||||||
|
materials_range.push_back(count); // последний конец отрезка
|
||||||
|
materials_ids.push_back(last_material_index); // последний используемый материал
|
||||||
|
}
|
||||||
|
} // for (const auto& shape : shapes)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Загрузка в буферы
|
||||||
|
model.load_verteces (&verteces[0], verteces.size());
|
||||||
|
model.load_normals (&normals[0], normals.size());
|
||||||
|
model.load_texCoords(&texCords[0], texCords.size());
|
||||||
|
// Загрузка индексного буфера
|
||||||
|
model.load_indices (&indices[0], indices.size());
|
||||||
|
|
||||||
|
|
||||||
|
// Создаем копии модели, которые будут рендериться в заданном диапазоне
|
||||||
|
// И присваиваем текстуры копиям на основании материала
|
||||||
|
for (int i = 0; i < materials_range.size()-1; i++)
|
||||||
|
{
|
||||||
|
result.parts.push_back(model); // Создание копии с общим VAO
|
||||||
|
auto s = --result.parts.end();
|
||||||
|
s->set_index_range(materials_range[i], materials_range[i+1]-materials_range[i]);
|
||||||
|
|
||||||
|
// Текстуры
|
||||||
|
Texture diffuse(TEX_DIFFUSE, texture_directory + materials[materials_ids[i]].diffuse_texname);
|
||||||
|
s->set_texture(diffuse);
|
||||||
|
Texture ambient(TEX_AMBIENT, texture_directory + materials[materials_ids[i]].ambient_texname);
|
||||||
|
s->set_texture(ambient);
|
||||||
|
Texture specular(TEX_SPECULAR, texture_directory + materials[materials_ids[i]].specular_texname);
|
||||||
|
s->set_texture(specular);
|
||||||
|
|
||||||
|
// Материал
|
||||||
|
s->material.ka = pow(glm::vec3(materials[materials_ids[i]].ambient[0], materials[materials_ids[i]].ambient[1], materials[materials_ids[i]].ambient[2]), glm::vec3(1/inv_gamma));
|
||||||
|
s->material.kd = pow(glm::vec3(materials[materials_ids[i]].diffuse[0], materials[materials_ids[i]].diffuse[1], materials[materials_ids[i]].diffuse[2]), glm::vec3(1/inv_gamma));
|
||||||
|
s->material.ks = glm::vec3(materials[materials_ids[i]].specular[0], materials[materials_ids[i]].specular[1], materials[materials_ids[i]].specular[2]);
|
||||||
|
s->material.p = (materials[materials_ids[i]].shininess > 0.0f) ? 1000.0f / materials[materials_ids[i]].shininess : 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вызов отрисовки групповой модели
|
||||||
|
void GrouptedModel::render(ShaderProgram &shaderProgram, UBO &material_buffer)
|
||||||
|
{
|
||||||
|
glm::mat4 transform = this->getTransformMatrix();
|
||||||
|
for (auto& model : parts)
|
||||||
|
model.render(shaderProgram, material_buffer, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерирует сферу заданного радиуса с определенным количеством сегментов
|
||||||
|
void genShpere(Model& model, float radius, int sectorsCount)
|
||||||
|
{
|
||||||
|
std::vector<glm::vec3> vertices;
|
||||||
|
std::vector<glm::vec3> normals;
|
||||||
|
std::vector<GLuint> indices;
|
||||||
|
|
||||||
|
float x, y, z, xy; // Позиция вершины
|
||||||
|
float nx, ny, nz, lengthInv = 1.0f / radius; // Нормаль вершины
|
||||||
|
float PI = 3.14159265;
|
||||||
|
float sectorStep = PI / sectorsCount; // Шаг сектора
|
||||||
|
float longAngle, latAngle; // Углы
|
||||||
|
|
||||||
|
for(int i = 0; i <= sectorsCount; ++i)
|
||||||
|
{
|
||||||
|
latAngle = PI / 2 - i * sectorStep; // Начиная с pi/2 до -pi/2
|
||||||
|
xy = radius * cos(latAngle); // r * cos(lat)
|
||||||
|
z = radius * sin(latAngle); // r * sin(lat)
|
||||||
|
|
||||||
|
// добавляем (sectorCount+1) вершин на сегмент
|
||||||
|
// Последняя и первая вершины имеют одинаковые нормали и координаты
|
||||||
|
for(int j = 0; j <= sectorsCount; ++j)
|
||||||
|
{
|
||||||
|
longAngle = j * 2 * sectorStep; // Начиная с 0 до 2*pi
|
||||||
|
|
||||||
|
// Положение вершины (x, y, z)
|
||||||
|
x = xy * cos(longAngle); // r * cos(lat) * cos(long)
|
||||||
|
y = xy * sin(longAngle); // r * cos(lat) * sin(long)
|
||||||
|
vertices.push_back({x, y, z});
|
||||||
|
|
||||||
|
// Нормали (nx, ny, nz)
|
||||||
|
nx = x * lengthInv;
|
||||||
|
ny = y * lengthInv;
|
||||||
|
nz = z * lengthInv;
|
||||||
|
normals.push_back({nx, ny, nz});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int k1, k2;
|
||||||
|
for(int i = 0; i < sectorsCount; ++i)
|
||||||
|
{
|
||||||
|
k1 = i * (sectorsCount + 1); // начало текущего сегмента
|
||||||
|
k2 = k1 + sectorsCount + 1; // начало следующего сегмента
|
||||||
|
|
||||||
|
for(int j = 0; j < sectorsCount; ++j, ++k1, ++k2)
|
||||||
|
{
|
||||||
|
// 2 треугольника на один сегмент
|
||||||
|
// k1, k2, k1+1
|
||||||
|
if(i != 0)
|
||||||
|
{
|
||||||
|
indices.push_back(k1);
|
||||||
|
indices.push_back(k2);
|
||||||
|
indices.push_back(k1 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// k1+1, k2, k2+1
|
||||||
|
if(i != (sectorsCount-1))
|
||||||
|
{
|
||||||
|
indices.push_back(k1 + 1);
|
||||||
|
indices.push_back(k2);
|
||||||
|
indices.push_back(k2 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Загрузка в модель
|
||||||
|
model.load_verteces(&vertices[0], vertices.size());
|
||||||
|
model.load_normals(&normals[0], normals.size());
|
||||||
|
model.load_indices(&indices[0], indices.size());
|
||||||
|
}
|
154
src/Shader.cpp
Normal file
154
src/Shader.cpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#include "Shader.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
std::map<int, int> 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);
|
||||||
|
}
|
374
src/Texture.cpp
Normal file
374
src/Texture.cpp
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include <stb_image.h>
|
||||||
|
|
||||||
|
std::map<std::string, int> BaseTexture::filename_handler; // Получение дескриптора текстуры по её имени
|
||||||
|
std::map<int, int> BaseTexture::handler_count; // Получение количества использований по дескриптору текстуры (Shared pointer)
|
||||||
|
|
||||||
|
// Загрузка текстуры с диска или использование "пустой"
|
||||||
|
Texture::Texture(GLuint t, const std::string& filename)
|
||||||
|
{
|
||||||
|
type = t;
|
||||||
|
if (!filename_handler.count(filename))
|
||||||
|
{
|
||||||
|
std::string empty = "";
|
||||||
|
int width, height, channels; // Ширина, высота и цветовые каналы текстуры
|
||||||
|
unsigned char* image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_default); // Загрузка в оперативную память изображения
|
||||||
|
// Если изображение успешно счиитано с диска или отсутствует пустая текстура
|
||||||
|
if (image || !filename_handler.count(empty))
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glGenTextures(1, &handler); // Генерация одной текстуры
|
||||||
|
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
|
||||||
|
|
||||||
|
filename_handler[filename] = handler; // Запоминим её дескриптор для этого имени файла
|
||||||
|
handler_count[handler] = 0; // Создадим счетчик использований дескриптора, который будет изменен в конце
|
||||||
|
|
||||||
|
// Если изображение успешно считано
|
||||||
|
if (image)
|
||||||
|
{
|
||||||
|
// Загрузка данных с учетом прозрачности
|
||||||
|
if (channels == 3) // RGB
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
|
||||||
|
else if (channels == 4) // RGBA
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
|
||||||
|
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D); // Генерация мипмапа для активной текстуры
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка активной текстуры
|
||||||
|
|
||||||
|
stbi_image_free(image); // Освобождение оперативной памяти
|
||||||
|
}
|
||||||
|
// Иначе изображение не считано и надо создать пустую текстуру
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image = new unsigned char[3] {255,255,255}; // RGB по 1 байту на
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, image); // Загрузка данных на видеокарту
|
||||||
|
delete image; // Освобождение оперативной памяти
|
||||||
|
|
||||||
|
filename_handler[empty] = handler; // Запоминим дополнительно её дескриптор для NULL-строки
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Иначе используем существующую пустую текстуру (текстура не загружена, пустую создавать не нужно)
|
||||||
|
else
|
||||||
|
handler = filename_handler[empty];
|
||||||
|
}
|
||||||
|
// Иначе используем уже существующую по имени файла
|
||||||
|
else
|
||||||
|
handler = filename_handler[filename];
|
||||||
|
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
Texture::Texture(GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||||
|
{
|
||||||
|
type = texType;
|
||||||
|
// Генерация текстуры заданного размера
|
||||||
|
glGenTextures(1, &handler);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, handler);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, dataType, NULL);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
// Привязка к буферу кадра
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, handler, 0);
|
||||||
|
|
||||||
|
// Создаем счетчик использований дескриптора
|
||||||
|
handler_count[handler] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор текстуры заданного размера без привязки к буферу с загрузкой пикселей по указателю
|
||||||
|
Texture::Texture(GLuint width, GLuint height, void* data, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||||
|
{
|
||||||
|
type = texType;
|
||||||
|
// Генерация текстуры заданного размера
|
||||||
|
glGenTextures(1, &handler);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, handler);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, dataType, data);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
// Создаем счетчик использований дескриптора
|
||||||
|
handler_count[handler] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
Texture::Texture(const Texture& other)
|
||||||
|
{
|
||||||
|
handler = other.handler;
|
||||||
|
type = other.type;
|
||||||
|
// Делаем копию и увеличиваем счетчик
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
Texture& Texture::operator=(const Texture& other)
|
||||||
|
{
|
||||||
|
// Если это разные текстуры
|
||||||
|
if (handler != other.handler)
|
||||||
|
{
|
||||||
|
this->~Texture(); // Уничтожаем имеющуюся
|
||||||
|
// Заменяем новой
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
type = other.type;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseTexture::~BaseTexture()
|
||||||
|
{
|
||||||
|
if (!--handler_count[handler]) // Если количество ссылок = 0
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &handler); // Удаление текстуры
|
||||||
|
// Удаление из словаря имен файлов и дескрипторов
|
||||||
|
for (auto it = filename_handler.begin(); it != filename_handler.end();)
|
||||||
|
{
|
||||||
|
if (it->second == handler)
|
||||||
|
it = filename_handler.erase(it);
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка текстуры
|
||||||
|
void Texture::use()
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отвязка текстуры по типу
|
||||||
|
void BaseTexture::disable(GLuint type)
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка текстуры
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает тип текстуры
|
||||||
|
GLuint BaseTexture::getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Задает тип текстуры
|
||||||
|
void BaseTexture::setType(GLuint type)
|
||||||
|
{
|
||||||
|
this->type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
TextureArray::TextureArray(GLuint levels, GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||||
|
{
|
||||||
|
type = texType;
|
||||||
|
// Генерация текстуры заданного размера
|
||||||
|
glGenTextures(1, &handler);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, handler);
|
||||||
|
glTexImage3D(
|
||||||
|
GL_TEXTURE_2D_ARRAY, 0, internalformat, width, height, levels, 0, format, dataType, 0);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
// Привязка к буферу кадра
|
||||||
|
glFramebufferTexture(GL_FRAMEBUFFER, attachment, handler, 0);
|
||||||
|
|
||||||
|
// Создаем счетчик использований дескриптора
|
||||||
|
handler_count[handler] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
TextureArray::TextureArray(const TextureArray& other)
|
||||||
|
{
|
||||||
|
handler = other.handler;
|
||||||
|
type = other.type;
|
||||||
|
// Делаем копию и увеличиваем счетчик
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
TextureArray& TextureArray::operator=(const TextureArray& other)
|
||||||
|
{
|
||||||
|
// Если это разные текстуры
|
||||||
|
if (handler != other.handler)
|
||||||
|
{
|
||||||
|
this->~TextureArray(); // Уничтожаем имеющуюся
|
||||||
|
// Заменяем новой
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
type = other.type;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка текстуры
|
||||||
|
void TextureArray::use()
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, handler); // Привязка текстуры как активной
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка текстуры с диска или использование "пустой"
|
||||||
|
TextureCube::TextureCube(GLuint t, const std::string (&filename)[6])
|
||||||
|
{
|
||||||
|
type = t;
|
||||||
|
std::string complex_name;
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
complex_name += filename[i];
|
||||||
|
if (!filename_handler.count(complex_name))
|
||||||
|
{
|
||||||
|
std::string empty = "";
|
||||||
|
int width, height, channels; // Ширина, высота и цветовые каналы текстуры
|
||||||
|
unsigned char* image;
|
||||||
|
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glGenTextures(1, &handler); // Генерация одной текстуры
|
||||||
|
glBindTexture(GL_TEXTURE_CUBE_MAP, handler); // Привязка текстуры как активной
|
||||||
|
|
||||||
|
filename_handler[complex_name] = handler; // Запомним её дескриптор для этого имени файла
|
||||||
|
handler_count[handler] = 0; // Создадим счетчик использований дескриптора, который будет изменен в конце
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
image = stbi_load(filename[i].c_str(), &width, &height, &channels, STBI_default); // Загрузка в оперативную память изображения
|
||||||
|
|
||||||
|
// Если изображение успешно считано
|
||||||
|
if (image)
|
||||||
|
{
|
||||||
|
// Загрузка данных с учетом прозрачности
|
||||||
|
if (channels == 3) // RGB
|
||||||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
|
||||||
|
else if (channels == 4) // RGBA
|
||||||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB_ALPHA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
|
||||||
|
|
||||||
|
stbi_image_free(image); // Освобождение оперативной памяти
|
||||||
|
}
|
||||||
|
// Иначе изображение не считано и надо создать пустую текстуру
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image = new unsigned char[3] {255,255,255}; // RGB по 1 байту на
|
||||||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, image); // Загрузка данных на видеокарту
|
||||||
|
delete image; // Освобождение оперативной памяти
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Иначе используем уже существующую по имени файла
|
||||||
|
else
|
||||||
|
handler = filename_handler[complex_name];
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
TextureCube::TextureCube(GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||||
|
{
|
||||||
|
type = texType;
|
||||||
|
// Генерация текстуры заданного размера
|
||||||
|
glGenTextures(1, &handler);
|
||||||
|
glBindTexture(GL_TEXTURE_CUBE_MAP, handler);
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalformat, width, height, 0, format, dataType, 0);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
// Привязка к буферу кадра
|
||||||
|
glFramebufferTexture(GL_FRAMEBUFFER, attachment, handler, 0);
|
||||||
|
|
||||||
|
// Создаем счетчик использований дескриптора
|
||||||
|
handler_count[handler] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
TextureCube::TextureCube(const TextureCube& other)
|
||||||
|
{
|
||||||
|
handler = other.handler;
|
||||||
|
type = other.type;
|
||||||
|
// Делаем копию и увеличиваем счетчик
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
TextureCube& TextureCube::operator=(const TextureCube& other)
|
||||||
|
{
|
||||||
|
// Если это разные текстуры
|
||||||
|
if (handler != other.handler)
|
||||||
|
{
|
||||||
|
this->~TextureCube(); // Уничтожаем имеющуюся
|
||||||
|
// Заменяем новой
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
type = other.type;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка текстуры
|
||||||
|
void TextureCube::use()
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_CUBE_MAP, handler); // Привязка текстуры как активной
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор текстуры заданного размера для использования в буфере
|
||||||
|
TextureCubeArray::TextureCubeArray(GLuint levels, GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||||
|
{
|
||||||
|
type = texType;
|
||||||
|
// Генерация текстуры заданного размера
|
||||||
|
glGenTextures(1, &handler);
|
||||||
|
glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, handler);
|
||||||
|
glTexImage3D(
|
||||||
|
GL_TEXTURE_CUBE_MAP_ARRAY, 0, internalformat, width, height, 6*levels, 0, format, dataType, 0);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
// Привязка к буферу кадра
|
||||||
|
glFramebufferTexture(GL_FRAMEBUFFER, attachment, handler, 0);
|
||||||
|
|
||||||
|
// Создаем счетчик использований дескриптора
|
||||||
|
handler_count[handler] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
TextureCubeArray::TextureCubeArray(const TextureCubeArray& other)
|
||||||
|
{
|
||||||
|
handler = other.handler;
|
||||||
|
type = other.type;
|
||||||
|
// Делаем копию и увеличиваем счетчик
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
TextureCubeArray& TextureCubeArray::operator=(const TextureCubeArray& other)
|
||||||
|
{
|
||||||
|
// Если это разные текстуры
|
||||||
|
if (handler != other.handler)
|
||||||
|
{
|
||||||
|
this->~TextureCubeArray(); // Уничтожаем имеющуюся
|
||||||
|
// Заменяем новой
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
type = other.type;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка текстуры
|
||||||
|
void TextureCubeArray::use()
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, handler); // Привязка текстуры как активной
|
||||||
|
}
|
536
src/main.cpp
Normal file
536
src/main.cpp
Normal file
@ -0,0 +1,536 @@
|
|||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <GLM/glm.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include "Camera.h"
|
||||||
|
#include "Model.h"
|
||||||
|
#include "Texture.h"
|
||||||
|
#include "Shader.h"
|
||||||
|
#include "Lights.h"
|
||||||
|
|
||||||
|
#define WINDOW_WIDTH 800
|
||||||
|
#define WINDOW_HEIGHT 600
|
||||||
|
#define WINDOW_CAPTION "OPENGL notes on rekovalev.site"
|
||||||
|
|
||||||
|
// Значение гамма-коррекции
|
||||||
|
float inv_gamma = 1/2.2;
|
||||||
|
|
||||||
|
// Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна
|
||||||
|
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||||
|
{
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera camera(800.0f/600.0f);
|
||||||
|
|
||||||
|
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.rotate(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
GLFWwindow* window; // Указатель на окно GLFW3
|
||||||
|
|
||||||
|
// Инициализация GLFW3
|
||||||
|
if (!glfwInit())
|
||||||
|
{
|
||||||
|
std::cout << "GLFW init error\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); // Минорная версия спецификаций 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";
|
||||||
|
glfwTerminate(); // Завершение работы с GLFW3 в случае ошибки
|
||||||
|
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";
|
||||||
|
glfwTerminate(); // Завершение работы с GLFW3 в случае ошибки
|
||||||
|
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 файла
|
||||||
|
GrouptedModel scene = loadOBJtoGroupted("../resources/models/blob.obj", "../resources/models/", "../resources/textures/");
|
||||||
|
scene.scale = glm::vec3(0.01);
|
||||||
|
scene.position.z = 1;
|
||||||
|
scene.parts[0].material.kd = {0.5,0.5,0.5};
|
||||||
|
scene.parts[0].material.ka = {0.05,0.05,0.05};
|
||||||
|
|
||||||
|
// Установка цвета очистки буфера цвета
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// Шейдер для рисования лампочки
|
||||||
|
ShaderProgram bulbShader;
|
||||||
|
// Загрузка и компиляция шейдеров
|
||||||
|
bulbShader.load(GL_VERTEX_SHADER, "shaders/bulb.vert");
|
||||||
|
bulbShader.load(GL_FRAGMENT_SHADER, "shaders/bulb.frag");
|
||||||
|
bulbShader.link();
|
||||||
|
|
||||||
|
// Направленный источник света
|
||||||
|
Sun sun;
|
||||||
|
sun.direction.z = -1.0;
|
||||||
|
|
||||||
|
camera.move({0.3,0,0});
|
||||||
|
|
||||||
|
// Источники света
|
||||||
|
Bulb lights[MAX_LIGHTS];
|
||||||
|
// Количество используемых источников
|
||||||
|
GLint lights_count = 0;
|
||||||
|
|
||||||
|
lights[lights_count].position = {0.3f, 0.0f, 0.6f}; // позиция
|
||||||
|
lights[lights_count].color = {1.0f, 0.0f, 0.0f}; // цвет
|
||||||
|
lights[lights_count++].angle = 50;
|
||||||
|
lights[lights_count].position = {-0.3f, 0.3f, 0.5f}; // позиция
|
||||||
|
lights[lights_count++].color = {0.0f, 0.0f, 1.0f}; // цвет
|
||||||
|
|
||||||
|
// Uniform-буферы
|
||||||
|
UBO cameraUB(sizeof(CameraData), 0);
|
||||||
|
UBO material_data(sizeof(Material), 1);
|
||||||
|
UBO light_data(sizeof(lights_count) + sizeof(lights), 2);
|
||||||
|
|
||||||
|
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_RGB16F, 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();
|
||||||
|
|
||||||
|
// Шейдер для расчета освещенности
|
||||||
|
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_raw(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT0, 0, GL_RED, GL_RED);
|
||||||
|
// Активируем базовый буфер кадра
|
||||||
|
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), 3);
|
||||||
|
|
||||||
|
// Текстура шума
|
||||||
|
glm::vec3 noise_vecs[16];
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
noise_vecs[i] = { randomFloats(generator) * 2.0 - 1.0
|
||||||
|
, randomFloats(generator) * 2.0 - 1.0
|
||||||
|
, 0.0f
|
||||||
|
};
|
||||||
|
Texture noiseTexture(4,4, noise_vecs, 2, GL_RGBA32F, GL_RGB);
|
||||||
|
|
||||||
|
// Шейдер для расчета SSAO
|
||||||
|
ShaderProgram ssaoShader;
|
||||||
|
// Загрузим шейдер
|
||||||
|
ssaoShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
|
||||||
|
ssaoShader.load(GL_FRAGMENT_SHADER, "shaders/ssao.frag");
|
||||||
|
ssaoShader.link();
|
||||||
|
// Текстуры, используемые в шейдере
|
||||||
|
const char* ssaoShader_names[] = {"gPosition", "gNormal", "noise"};
|
||||||
|
ssaoShader.bindTextures(ssaoShader_names, sizeof(ssaoShader_names)/sizeof(const char*));
|
||||||
|
|
||||||
|
// Создадим буфер для размытия SSAO
|
||||||
|
FBO ssaoBlurBuffer(attachments_ssao, sizeof(attachments_ssao) / sizeof(GLuint));
|
||||||
|
// Создадим текстуры для буфера кадра
|
||||||
|
Texture ssaoTexture(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT0, 6, GL_RED, GL_RED);
|
||||||
|
// Активируем базовый буфер кадра
|
||||||
|
FBO::useDefault();
|
||||||
|
|
||||||
|
// Шейдер для размытия SSAO
|
||||||
|
ShaderProgram ssaoBlurShader;
|
||||||
|
// Загрузим шейдер
|
||||||
|
ssaoBlurShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
|
||||||
|
ssaoBlurShader.load(GL_FRAGMENT_SHADER, "shaders/ssaoBlur.frag");
|
||||||
|
ssaoBlurShader.link();
|
||||||
|
|
||||||
|
// Модель прямоугольника
|
||||||
|
Model rectangle;
|
||||||
|
|
||||||
|
// Вершины прямоугольника
|
||||||
|
glm::vec3 rectangle_verticies[] = { {-0.5f, -0.5f, 0.0f}
|
||||||
|
, { 0.5f, -0.5f, 0.0f}
|
||||||
|
, { 0.5f, 0.5f, 0.0f}
|
||||||
|
, {-0.5f, 0.5f, 0.0f}
|
||||||
|
};
|
||||||
|
// Загрузка вершин модели
|
||||||
|
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, 0.0f, -1.0f}
|
||||||
|
, {0.0f, 0.0f, -1.0f}
|
||||||
|
, {0.0f, 0.0f, -1.0f}
|
||||||
|
, {0.0f, 0.0f, -1.0f}
|
||||||
|
};
|
||||||
|
// Загрузка нормалей модели
|
||||||
|
rectangle.load_normals(rectangle_normals, sizeof(rectangle_normals)/sizeof(glm::vec3));
|
||||||
|
|
||||||
|
// Зададим горизонтальное положение перед камерой
|
||||||
|
rectangle.position.y = -1;
|
||||||
|
rectangle.position.z = 2;
|
||||||
|
rectangle.rotation.x = 90;
|
||||||
|
rectangle.scale = glm::vec3(4);
|
||||||
|
|
||||||
|
// Параметры материала
|
||||||
|
rectangle.material.ka = {0.05, 0.05, 0.05};
|
||||||
|
rectangle.material.kd = {1, 1, 1};
|
||||||
|
|
||||||
|
// Вершины для скайбокса
|
||||||
|
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*));
|
||||||
|
|
||||||
|
// Значение гамма-коррекции
|
||||||
|
UBO gamma(&inv_gamma, sizeof(inv_gamma), 4);
|
||||||
|
|
||||||
|
// Пока не произойдет событие запроса закрытия окна
|
||||||
|
while(!glfwWindowShouldClose(window))
|
||||||
|
{
|
||||||
|
// Активируем G-кадра
|
||||||
|
gbuffer.use();
|
||||||
|
// Используем шейдер с освещением
|
||||||
|
gShader.use();
|
||||||
|
// Очистка буфера цвета и глубины
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Загрузка данных о камере
|
||||||
|
cameraUB.loadSub(&camera.getData(), sizeof(CameraData));
|
||||||
|
// Пересчет матриц проекции и вида точечных источников
|
||||||
|
for (int i = 0; i < lights_count; i++)
|
||||||
|
lights[i].recalc_pov();
|
||||||
|
// Загружаем информацию об источниках света и их количестве
|
||||||
|
light_data.loadSub(lights, sizeof(Bulb) * lights_count);
|
||||||
|
light_data.loadSub(&lights_count, sizeof(GLint), sizeof(lights));
|
||||||
|
|
||||||
|
// Тут производится рендер
|
||||||
|
scene.render(gShader, material_data);
|
||||||
|
rectangle.render(gShader, material_data);
|
||||||
|
|
||||||
|
// Активируем буфер SSAO
|
||||||
|
ssaoBuffer.use();
|
||||||
|
// Используем шейдер для расчета SSAO
|
||||||
|
ssaoShader.use();
|
||||||
|
// Очистка буфера цвета
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
// Подключаем текстуры G-буфера
|
||||||
|
gPosition.use();
|
||||||
|
gNormal.use();
|
||||||
|
// Подключаем текстуру шума для SSAO
|
||||||
|
noiseTexture.use();
|
||||||
|
// Рендерим прямоугольник
|
||||||
|
quadModel.render();
|
||||||
|
|
||||||
|
// Активируем буфер размытия SSAO
|
||||||
|
ssaoBlurBuffer.use();
|
||||||
|
// Используем шейдер для размытия SSAO
|
||||||
|
ssaoBlurShader.use();
|
||||||
|
// Очистка буфера цвета
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
// Подключаем текстуру сырого SSAO
|
||||||
|
ssaoTexture_raw.use();
|
||||||
|
// Рендерим прямоугольник
|
||||||
|
quadModel.render();
|
||||||
|
|
||||||
|
// Изменим размер вывода для тени
|
||||||
|
glViewport(0, 0, sunShadow_resolution, sunShadow_resolution);
|
||||||
|
// Активируем буфер кадра для теней от солнца
|
||||||
|
sunShadowBuffer.use();
|
||||||
|
// Подключим шейдер для расчета теней
|
||||||
|
sunShadowShader.use();
|
||||||
|
// Очистка буфера глубины
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
// Загружаем матрицу проекции и трансформации в пространство источника
|
||||||
|
sun.pov(sunShadowShader, camera);
|
||||||
|
// Рендерим геометрию в буфер глубины
|
||||||
|
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 < lights_count; 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();
|
||||||
|
// Загружаем информацию о направленном источнике
|
||||||
|
sun.upload(lightShader);
|
||||||
|
// Загружаем матрицу проекции и трансформации в пространство источника
|
||||||
|
sun.pov(lightShader, camera);
|
||||||
|
// Рендерим прямоугольник с расчетом освещения
|
||||||
|
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();
|
||||||
|
for (int i = 0; i < lights_count; i++)
|
||||||
|
lights[i].render(bulbShader, material_data);
|
||||||
|
|
||||||
|
// Представление содержимого буфера цепочки показа на окно
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
// Обработка системных событий
|
||||||
|
glfwPollEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отключение атрибутов
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
|
||||||
|
// Завершение работы с GLFW3 перед выходом
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user