Копия проекта с 09

This commit is contained in:
parent abadef13ed
commit 8ef0116aef
25 changed files with 2688 additions and 0 deletions

20
.vscode/c_cpp_properties.json vendored Normal file
View 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
}

46
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,46 @@
{
"files.associations": {
"fstream": "cpp",
"iosfwd": "cpp",
"map": "cpp",
"atomic": "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"
}

32
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,32 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: make сборка",
"command": "make",
"args": [
"${input:target}"
],
"options": {
"cwd": "${workspaceRoot}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Задача создана отладчиком."
}
],
"inputs": [
{
"id": "target",
"description": "Цель сборки (all, list, clean)",
"default": "all",
"type": "promptString"
},
],
"version": "2.0.0"
}

101
Makefile Normal file
View File

@ -0,0 +1,101 @@
# Компилятор и директория проекта
ifeq ($(OS), Windows_NT)
# С возможностью сборки x32
ifeq ($(MAKECMDGOALS), x32)
CC = C:/MinGW/bin/g++.exe
else
CC = C:/MinGW64/bin/g++.exe
endif
PROJECT_DIR = $(shell echo %cd%)
PATH_SEPARATOR = \\
# Имя исполняемого файла
EXECUTABLE = $(notdir $(strip $(PROJECT_DIR))).exe
else
CC = g++
PROJECT_DIR = $(shell pwd)
PATH_SEPARATOR = /
# Имя исполняемого файла
EXECUTABLE = $(notdir $(strip $(PROJECT_DIR)))
endif
# Опции компилятора
CFLAGS += -c
CFLAGS += -I./include
CFLAGS += -I../dependencies/GLFW/include
CFLAGS += -I../dependencies/glad/include
CFLAGS += -I../dependencies/glm
CFLAGS += -I../dependencies/stb
CFLAGS += -I../dependencies/tinyobjloader
# Опции линкера
LDFLAGS += --std=c++11
# Архитектурозависимые опции линкера
ifeq ($(OS), Windows_NT)
# GLFW в зависимости от архитектуры
ifeq ($(MAKECMDGOALS), x32)
LDFLAGS += -L../dependencies/GLFW/lib-mingw
else
LDFLAGS += -L../dependencies/GLFW/lib-mingw-w64
endif
LDFLAGS += -static
LDFLAGS += -lglfw3dll
LDFLAGS += -lopengl32
else
LDFLAGS += -lglfw
LDFLAGS += -lGL
endif
# Библиотека GLAD
GLAD := ../dependencies/glad/src/glad.c
GLAD_O := $(GLAD:.c=.o)
# Файлы из директории src
SOURCES_C = $(wildcard src/*.c)
SOURCES_CPP = $(wildcard src/*.cpp)
# Директория с объектными файлами
OBJ_DIR := Obj
# Объектные файлы
OBJECTS = $(addprefix $(OBJ_DIR)/,$(SOURCES_C:src/%.c=%.o) $(SOURCES_CPP:src/%.cpp=%.o))
# Для x32 сборки под Windows
ifeq ($(OS), Windows_NT)
ifeq ($(MAKECMDGOALS), x32)
x32: all
endif
endif
# Цель по умолчанию, зависит от EXECUTABLE
all: $(EXECUTABLE)
# Цель сборки исполняемого файла, зависит от OBJ_DIR, OBJECTS и GLAD_O
$(EXECUTABLE): $(OBJ_DIR) $(OBJECTS) $(GLAD_O)
$(CC) $(OBJECTS) $(GLAD_O) $(LDFLAGS) -o $@
# Цель для сборки GLAD
$(GLAD_O): $(GLAD)
$(CC) $(CFLAGS) $< -o $@
# Цель для создания директории с объектными файлами
$(OBJ_DIR):
@mkdir $(OBJ_DIR)
# Цель сборки объектных файлов
$(OBJ_DIR)/%.o: src/%.c
$(CC) $(CFLAGS) $< -o $@
$(OBJ_DIR)/%.o: src/%.cpp
$(CC) $(CFLAGS) $< -o $@
# Цель вывода всех файлов, учавствтующих в сборке
list:
@echo "В сборке участвуют:" $(OBJECTS)
# Очистка
ifeq ($(OS), Windows_NT)
clean: $(OBJ_DIR)
@rmdir /s /q $(OBJ_DIR)
else
clean: $(OBJ_DIR)
@rm -f $(EXECUTABLE) $(OBJECTS)
endif

90
include/Buffers.h Normal file
View File

@ -0,0 +1,90 @@
#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, int count); // Создает буфер кадра с нужным числом прикреплений текстур
~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(); // Уничтожение буфера
void reallocate(int w, int h, GLuint component = GL_DEPTH_COMPONENT); // Изменяет размеры буфера рендера
GLuint getHandler(); // Возвращает дескриптор буфера рендера
protected:
GLuint handler; // Дескриптор
};
#endif // BUFFERS_H

73
include/Camera.h Normal file
View File

@ -0,0 +1,73 @@
#ifndef CAMERA_H
#define CAMERA_H
#include <GLM/glm.hpp>
#include <GLM/gtx/euler_angles.hpp>
#include <GLM/gtc/matrix_transform.hpp>
#include <GLM/ext/matrix_transform.hpp>
#include "Model.h"
// Ближняя граница области отсечения
#define CAMERA_NEAR 0.1f
// Дальняя граница области отсечения
#define CAMERA_FAR 100.0f
// Вектор, задающий верх для камеры
#define CAMERA_UP_VECTOR glm::vec3(0.0f, 1.0f, 0.0f)
// Вектор, задающий стандартный поворот углами Эйлера (в положительном направлении оси Z)
#define CAMERA_DEFAULT_ROTATION glm::vec3(0.0f, 180.0f, 0.0f)
// Стандартный угол обзора
#define CAMERA_FOVy 60.0f
// Стандартная чувствительность
#define CAMERA_DEFAULT_SENSIVITY 0.005f
// Данные о камере для шейдера
struct CameraData
{
glm::mat4 projection;
glm::mat4 view;
glm::vec3 position;
};
// Класс камеры
class Camera : public Node
{
public:
Camera(float aspect, const glm::vec3 &position = glm::vec3(0.0f), const glm::vec3 &initialRotation = CAMERA_DEFAULT_ROTATION, float fovy = CAMERA_FOVy); // Конструктор камеры с проекцией перспективы
Camera(float width, float height, const glm::vec3 &position = glm::vec3(0.0f), const glm::vec3 &initialRotation = CAMERA_DEFAULT_ROTATION); // Конструктор ортографической камеры
Camera(const Camera& copy); // Конструктор копирования камеры
Camera& operator=(const Camera& other); // Оператор присваивания
virtual ~Camera(); // Деструктор
const glm::mat4& getVP(); // Возвращает ссылку на константную матрицу произведения матриц вида и проекции
const glm::mat4& getProjection(); // Возвращает ссылку на константную матрицу проекции
const glm::mat4& getView(); // Возвращает ссылку на константную матрицу вида
void rotate(const glm::vec2 &xyOffset); // Поворачивает камеру на dx и dy пикселей с учетом чувствительности
void setPerspective(float fov, float aspect); // Устанавливает заданную матрицу перспективы
void setOrtho(float width, float height); // Устанавливает заданную ортографическую матрицу
void setSensitivity(float sensitivity); // Изменяет чувствительность мыши
const float& getSensitivity() const; // Возвращает чувствительность мыши
void use(); // Использование этой камеры как текущей
static Camera& current(); // Ссылка на текущую используемую камеру
CameraData& getData(); // Данные о камере для шейдера
protected:
Camera(const glm::vec3 &position, const glm::vec3 &initialRotation); // Защищенный (protected) конструктор камеры без перспективы
glm::mat4 view; // Матрица вида
glm::mat4 projection; // Матрица проекции
glm::mat4 vp; // Матрица произведения вида и проекции
bool requiredRecalcVP; // Необходимость пересчета матрицы вида и проекции камеры
float sensitivity; // Чувствительность мыши
virtual void recalcMatrices(); // Метод пересчета матрицы вида и произведения Вида*Проекции по необходимости, должен сбрасывать флаг changed
static Camera* p_current; // Указатель на текущую используемую камеру
};
#endif // CAMERA_H

59
include/Lights.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef LIGHTS_H
#define LIGHTS_H
#include <GLM/glm.hpp>
#include "Model.h"
// Максимальное число источников света
#define MAX_LIGHTS 300
// Точечный источник света
struct LightData
{
alignas(16) glm::vec3 position; // Позиция
alignas(16) glm::vec3 color; // Цвет
alignas(16) glm::vec3 attenuation; // Радиус действия источника, линейный и квадратичный коэф. угасания
};
// Источник света
class Light : public Node
{
public:
static int getUBOsize(); // Возвращает размер буфера в байтах
static void upload(UBO& lights_data); // Загрузка данных в буфер
static Light& getNew(); // Возвращает ссылку на новый источник света
void destroy(); // Уничтожает источник света
const glm::vec3& c_color() const; // Константный доступ к цвету
glm::vec3& e_color(); // Неконстантная ссылка для изменений цвета
const float& c_radius() const; // Константный доступ к радиусу
float& e_radius(); // Неконстантная ссылка для изменений радиуса
static void render(ShaderProgram &shaderProgram, UBO &material_buffer); // Рисование отладочных лампочек
private:
Light(); // Конструктор без параметров
Light(const Light& copy) = delete; // Конструктор копирования ОТКЛЮЧЕН
Light& operator=(const Light& other); // Оператор присваивания
virtual ~Light();
glm::vec3 color; // Цвет
float radius; // Радиус действия источника
int index; // Индекс в массиве отправки (может не совпадать с lights) для дефрагментированного доступа
static Light& findByIndex(GLuint index); // Возвращает ссылку на источник с нужным индексом
bool uploadReq; // Необходимость загрузки в следствии изменений
void check_id(); // Проверка что не взаимодествуем с пустым источником
void toData(); // Преобразует информацию об источнике в структуру LightData
virtual void recalcMatrices(); // Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed
static GLuint count; // количество используемых источников (должно быть <= MAX_LIGHTS)
static LightData data[MAX_LIGHTS]; // Массив данных по источникам света
static Light lights[MAX_LIGHTS]; // Массив источников-узлов сцены
};
#endif // LIGHTS_H

99
include/Model.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef MODEL_H
#define MODEL_H
#include "Buffers.h"
#include "Texture.h"
#include "Shader.h"
#include <GLM/glm.hpp>
#include <GLM/gtc/quaternion.hpp>
#include <GLM/gtc/matrix_transform.hpp>
#include <vector>
class Model genShpere(float radius, int sectorsCount, class Node* parent = NULL); // Генерирует сферу заданного радиуса с определенным количеством сегментов
// Класс узла сцены
class Node
{
public:
Node(Node* parent = NULL); // Конструктор с заданным родителем (по умолчанию NULL)
Node(const Node& copy); // Конструктор копирования
Node& operator=(const Node& other); // Оператор присваивания
virtual ~Node();
void setParent(Node * parent); // Устанавливает родителя для узла
virtual const glm::mat4& getTransformMatrix(); // Возвращает матрицу трансформации модели
bool isChanged(); // Возвращает необходимость пересчета матрицы трансформации
const glm::vec3& c_position() const; // Константный доступ к позиции
const glm::quat& c_rotation() const; // Константный доступ к повороту
const glm::vec3& c_scale() const; // Константный доступ к масштабированию
virtual glm::vec3& e_position(); // Неконстантная ссылка для изменений позиции
virtual glm::quat& e_rotation(); // Неконстантная ссылка для изменений поворота
virtual glm::vec3& e_scale(); // Неконстантная ссылка для изменений масштабирования
Node* getParent(); // Возвращает указатель на родителя
const std::vector<Node*>& getChildren() const; // Возвращает ссылку на вектор дочерних узлов
protected:
Node *parent; // Родительский узел
std::vector<Node*> children; // Узлы-потомки !Не должны указывать на NULL!
glm::vec3 position; // позиция модели
glm::quat rotation; // поворот модели
glm::vec3 scale; // масштабирование модели
bool changed; // Флаг необходимости пересчета матрицы трансформации
glm::mat4 transform; // Матрица трансформации модели
bool parent_changed; // Флаг изменений у родителя - необходимость пересчета итоговой трансформации
glm::mat4 result_transform; // Итоговая трансформация с учетом родительской
virtual void recalcMatrices(); // Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed
void invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
};
// Материал модели
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 Model : public Node
{
public:
Model(Node *parent = NULL); // Конструктор по умолчанию
Model(const Model& copy); // Конструктор копирования
Model& operator=(const Model& other); // Оператор присваивания
virtual ~Model();
void render(); // Вызов отрисовки без uniform-данных
void render(ShaderProgram &shaderProgram, UBO &material_buffer); // Вызов отрисовки
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_index_range(size_t first_byteOffset, size_t count); // Ограничение диапазона из буфера индексов
void set_texture(Texture& texture); // Привязка текстуры к модели
Material material; // Материал модели
private:
VAO vao;
BO vertex_vbo, index_vbo; // вершинный и индексный буферы
BO normals_vbo, texCoords_vbo; // буферы с нормалями и текстурными координатами
GLuint verteces_count; // Количество вершин
size_t first_index_byteOffset, indices_count; // Сдвиг в байтах для первого и количество индексов
Texture texture_diffuse; // Диффузная текстура
Texture texture_ambient; // Текстура фонового освщения
Texture texture_specular; // Текстура зеркального отражения
};
#endif // MODEL_H

37
include/Scene.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef SCENE_H
#define SCENE_H
#include <list>
#include "Model.h"
#include "Camera.h"
#define DEFAULT_MTL_DIR "./"
class Scene loadOBJtoScene(const char* filename, const char* mtl_directory = DEFAULT_MTL_DIR, const char* texture_directory = DEFAULT_MTL_DIR);
// Класс сцены
class Scene
{
public:
Scene(); // Конструктор пустой сцены
Scene(const Scene &copy); // Конструктор копирования
Scene& operator=(const Scene& other); // Оператор присваивания
void render(ShaderProgram &shaderProgram, UBO &material_buffer); // Рендер сцены
Node root; // Корневой узел
// Списки объектов, выступающих узлами
std::list<Node> nodes; // Список пустых узлов
std::list<Model> models; // Список моделей для рендера
std::list<Camera> cameras; // Список камер
protected:
void rebuld_tree(const Scene& from); // Перестройка дерева после копирования или присваивания
template <class T>
void rebuild_Nodes_list(T& nodes, const Scene& from); // Перестройка узлов выбранного списка
template <class T>
void move_parent(Node& for_node, const std::list<T>& from_nodes, std::list<T>& this_nodes); // Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному
};
#endif // SCENE_H

30
include/Shader.h Normal file
View 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 &copy);
~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

39
include/Texture.h Normal file
View File

@ -0,0 +1,39 @@
#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 Texture
{
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(const Texture& other); // Конструктор копирования
~Texture();
Texture& operator=(const Texture& other); // Оператор присваивания
void reallocate(GLuint width, GLuint height, GLuint texType = TEX_DIFFUSE, GLint internalformat = GL_RGBA, GLint format = GL_RGBA, GLenum dataType = GL_FLOAT); // Пересоздает текстуру для имеющегося дескриптора
void use(); // Привязка текстуры
static void disable(GLuint type); // Отвязка текстуры по типу
GLuint getType(); // Возвращает тип текстуры
void setType(GLuint type); // Задает тип текстуры
private:
GLuint handler; // Дескриптор текстуры
GLuint type; // Тип текстуры, соответствует её слоту
static std::map<std::string, int> filename_handler; // Получение дескриптора текстуры по её имени
static std::map<int, int> handler_count; // Получение количества использований по дескриптору текстуры (Shared pointer)
};
#endif // TEXTURE_H

16
shaders/bulb.frag Normal file
View File

@ -0,0 +1,16 @@
#version 420 core
layout(std140, binding = 1) uniform Material
{
vec3 ka;
vec3 kd;
vec3 ks;
float p;
};
out vec4 color;
void main()
{
color = vec4(ka, 1);
}

17
shaders/bulb.vert Normal file
View File

@ -0,0 +1,17 @@
#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;
void main()
{
gl_Position = camera.projection * camera.view * model * vec4(pos, 1.0);
}

38
shaders/gshader.frag Normal file
View 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
View 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;
}

85
shaders/lighting.frag Normal file
View File

@ -0,0 +1,85 @@
#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;
vec3 attenuation;
};
layout(std140, binding = 2) uniform Light
{
LightData data[300];
int count;
} light_f;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gDiffuseP;
uniform sampler2D gAmbientSpecular;
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;
// Переменные используемые в цикле:
vec3 L_vertex; // Данные об источнике относительно фрагмента
vec3 Cam_vertex = normalize(camera.position - fragPos); // Данные о камере относительно фрагмента
float diffuse; // Диффузная составляющая
vec3 H; // Вектор половины пути
float specular; // Зеркальная составляющая
float L_distance; // Расстояние от поверхности до источника
float attenuation; // Коэф. угасания
// Фоновая освещенность
color = vec4(ka, 1);
// Цикл по источникам света
int i;
for (i = 0; i < light_f.count; i++)
{
// Данные об источнике относительно фрагмента
L_vertex = light_f.data[i].position - fragPos;
// Расстояние от поверхности до источника
L_distance = length(L_vertex);
// Проверка на дистанцию
if (L_distance < light_f.data[i].attenuation.r)
{
// Нормирование вектора
L_vertex = normalize(L_vertex);
// Диффузная составляющая
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].attenuation[1] * L_distance + light_f.data[i].attenuation[2] * L_distance * L_distance);
color += vec4(light_f.data[i].color*kd*diffuse * attenuation, 1)
+ vec4(light_f.data[i].color*ks*specular * attenuation, 1);
}
}
}

11
shaders/quad.vert Normal file
View 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]
}

203
src/Buffers.cpp Normal file
View File

@ -0,0 +1,203 @@
#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);
}
// Изменяет размеры буфера рендера
void RBO::reallocate(int w, int h, GLuint component)
{
glBindRenderbuffer(GL_RENDERBUFFER, handler); // Привязка элементного буфера
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
}
// Возвращает дескриптор буфера рендера
GLuint RBO::getHandler()
{
return handler;
}

191
src/Camera.cpp Normal file
View File

@ -0,0 +1,191 @@
#include "Camera.h"
// Указатель на текущую используемую камеру
Camera* Camera::p_current = NULL;
// Защищенный (protected) конструктор камеры без перспективы
Camera::Camera(const glm::vec3 &pos, const glm::vec3 &initialRotation) : Node(NULL) // Пусть по умолчанию камера не относится к сцене
{
sensitivity = CAMERA_DEFAULT_SENSIVITY;
position = pos; // задаем позицию
// Определяем начальный поворот
glm::quat rotationAroundX = glm::angleAxis( glm::radians(initialRotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
glm::quat rotationAroundY = glm::angleAxis(-glm::radians(initialRotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat rotationAroundZ = glm::angleAxis( glm::radians(initialRotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
rotation = rotationAroundX * rotationAroundY * rotationAroundZ;
// Признак изменения
changed = true;
}
// Конструктор камеры с проекцией перспективы
Camera::Camera(float aspect, const glm::vec3 &position, const glm::vec3 &initialRotation, float fovy)
: Camera(position, initialRotation)
{
setPerspective(fovy, aspect);
}
// Конструктор ортографической камеры
Camera::Camera(float width, float height, const glm::vec3 &position, const glm::vec3 &initialRotation)
: Camera(position, initialRotation)
{
setOrtho(width, height);
}
// Конструктор копирования камеры
Camera::Camera(const Camera& copy)
: Node(copy), projection(copy.projection), requiredRecalcVP(copy.requiredRecalcVP), sensitivity(copy.sensitivity)
{
// Если у оригинала не было изменений - перепишем матрицу вида-проекции
if (!requiredRecalcVP)
vp = copy.vp;
}
// Оператор присваивания
Camera& Camera::operator=(const Camera& other)
{
Node::operator=(other); // Вызов родительского оператора= для переноса узла
projection = other.projection;
requiredRecalcVP = other.requiredRecalcVP;
sensitivity = other.sensitivity;
// Если у оригинала не было изменений - перепишем матрицу вида-проекции
if (!requiredRecalcVP)
vp = other.vp;
return *this;
}
// Деструктор
Camera::~Camera()
{
if (p_current == this)
p_current = NULL;
}
// Возвращает ссылку на константную матрицу проекции
const glm::mat4& Camera::getProjection()
{
return projection;
}
// Возвращает ссылку на константную матрицу вида
const glm::mat4& Camera::getView()
{
recalcMatrices();
return view;
}
// Возвращает ссылку на константную матрицу вида
const glm::mat4& Camera::getVP()
{
recalcMatrices();
return vp;
}
// Устанавливает заданную матрицу перспективы
void Camera::setPerspective(float fovy, float aspect)
{
projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR);
requiredRecalcVP = true;
}
// Устанавливает заданную ортографическую матрицу
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;
}
// Изменяет чувствительность мыши
void Camera::setSensitivity(float sens)
{
sensitivity = sens;
}
// Возвращает чувствительность мыши
const float& Camera::getSensitivity() const
{
return sensitivity;
}
// Метод пересчета матрицы вида и произведения Вида*Проекции по необходимости, должен сбрасывать флаг changed
void Camera::recalcMatrices()
{
if (changed || parent_changed)
{
glm::vec3 _position = position;
glm::quat _rotation = rotation;
if (parent) // Если есть родитель
{
glm::mat4 normalized_transform = parent->getTransformMatrix();
for (int i = 0; i < 3; i++)
{
glm::vec3 axis = glm::vec3(normalized_transform[i]);
normalized_transform[i] = glm::vec4(glm::normalize(axis), normalized_transform[i].w);
}
glm::vec4 tmp = normalized_transform * glm::vec4(_position, 1.0f);
tmp /= tmp.w;
_position = glm::vec3(tmp);
_rotation = glm::quat_cast(normalized_transform) * _rotation;
}
glm::mat4 rotationMatrix = glm::mat4_cast(glm::conjugate(_rotation));
glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), -_position);
view = rotationMatrix * translationMatrix;
requiredRecalcVP = true;
}
Node::recalcMatrices();
if (requiredRecalcVP)
{
vp = projection * view;
requiredRecalcVP = false; // Изменения применены
}
}
// Поворачивает камеру на dx и dy пикселей с учетом чувствительности
void Camera::rotate(const glm::vec2 &xyOffset)
{
// xyOffset - сдвиги координат мыши, xyOffset.x означает поворот вокруг оси Y, а xyOffset.y - поворот вокруг оси X
// Вращение вокруг оси Y
glm::quat qY = glm::angleAxis(-xyOffset.x * sensitivity, glm::vec3(0.0f, 1.0f, 0.0f));
// Вращение вокруг оси X
glm::quat qX = glm::angleAxis(xyOffset.y * sensitivity, glm::vec3(1.0f, 0.0f, 0.0f));
// Сначала применяем вращение вокруг Y, затем вокруг X
rotation = qY * rotation * qX;
changed = true;
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
}
// Использование этой камеры как текущей
void Camera::use()
{
p_current = this;
}
// Ссылка на текущую используемую камеру
Camera& Camera::current()
{
static Camera default_cam(800.0f/600.0f);
if (!p_current)
return default_cam;
else
return *p_current;
}
// Данные о камере для шейдера
CameraData& Camera::getData()
{
static CameraData data;
data = {getProjection(), getView(), position};
return data;
}

217
src/Lights.cpp Normal file
View File

@ -0,0 +1,217 @@
#include "Lights.h"
#include "Scene.h" // Для отладочного вывода лампочек
#include <stdexcept>
GLuint Light::count = 0; // количество используемых источников (должно быть <= MAX_LIGHTS)
LightData Light::data[MAX_LIGHTS]; // Массив данных по источникам света
Light Light::lights[MAX_LIGHTS]; // Массив источников-узлов сцены
// возвращает размер буфера в байтах
int Light::getUBOsize()
{
return sizeof(LightData) * MAX_LIGHTS + sizeof(GLuint);
}
// Загрузка данных в буфер
void Light::upload(UBO& lights_data)
{
GLuint LightDataSize = sizeof(LightData); // Одного экземпляра структуры LightData
int first = MAX_LIGHTS, last = -1; // Начало и конец диапазона загрузки источников
static GLuint prev_count = -1; // Кол-во источников в прошлую посылку
if (count)
{
for (int i = 0; i < MAX_LIGHTS; i++)
{
lights[i].recalcMatrices(); // Пересчитаем матрицы по необходимости (проверка внутри метода)
// Если требуется загрузка
if (lights[i].uploadReq)
{
lights[i].toData(); // Перевод ноды в данные для шейдера
// Определение диапазона загрузки
if (first > lights[i].index)
first = lights[i].index;
if (last < lights[i].index)
last = lights[i].index;
lights[i].uploadReq = false; // Сброс флага
}
}
// Если есть что загрузить (определен диапазон)
if (last > -1)
lights_data.loadSub(data + first, LightDataSize*(last - first +1), LightDataSize*(first)); // Загрузка данных об источниках
}
// Если кол-во изменилось
if (prev_count != count)
{
prev_count = count;
// Загружаем кол-во источников
lights_data.loadSub(&count, sizeof(count), LightDataSize*MAX_LIGHTS);
}
}
// Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed
void Light::recalcMatrices()
{
// Если были изменения - необходимо загрузить данные
if (changed || parent_changed)
uploadReq = true;
// Выполняем вычисление матриц методом родительского класса
Node::recalcMatrices();
}
// Константный доступ к цвету
const glm::vec3& Light::c_color() const
{
return color;
}
// Неконстантная ссылка для изменений цвета
glm::vec3& Light::e_color()
{
uploadReq = true;
return color;
}
// Проверка что не взаимодествуем с пустым источником
void Light::check_id()
{
if (index < 0
|| index >= count)
throw std::runtime_error("Попытка использовать ссылку на пустой или некорректный источник");
}
// Преобразует информацию об источнике в структуру LightData
void Light::toData()
{
check_id(); // Проверка на работу с корректным индексом
data[index].position = glm::vec3(result_transform[3]); // Позиция из матрицы трансформации
data[index].color = color; // Цвет
// Если радиус изменился
if (data[index].attenuation.r != radius)
{
data[index].attenuation.r = radius; // Радиус действия источника
data[index].attenuation[1] = 4.5/radius; // Линейный коэф. угасания
data[index].attenuation[2] = 4 * data[index].attenuation[1] * data[index].attenuation[1]; // Квадратичный коэф. угасания
}
}
// Возвращает ссылку на новый источник света
Light& Light::getNew()
{
Light& refNew = findByIndex(-1);
refNew.index = count++;
refNew.uploadReq = true;
return refNew;
}
// Уничтожает источник света
void Light::destroy()
{
check_id(); // Проверка на работу с корректным индексом
// Если удаляемый элемент не последний
if (count-1 != index)
{
// Найдем элемент для замены
Light& replace = findByIndex(--count);
replace.uploadReq = true; // Требуется загрузить данные
replace.index = index; // Заменяем индекс данных
}
operator=(Light()); // Обнулим источник путем замены на новый
}
// Возвращает ссылку на источник с нужным индексом
Light& Light::findByIndex(GLuint index)
{
// Если нет источников - возвращаем нулевой
if (!count)
return lights[0];
// Цикл по перебору источников
for (int i = 0; i < MAX_LIGHTS; i++)
if (lights[i].index == index)
return lights[i];
throw std::runtime_error("Запрашиваемый источник освещения не найден, либо достигнут лимит");
}
// Конструктор без параметров
Light::Light() : Node(), index(-1), uploadReq(false), color(1.0f), radius(10.0f)
{
}
// Оператор присваивания
Light& Light::operator=(const Light& other)
{
// Проверка на самоприсваивание
if (this != &other)
{
index = other.index; // Переносим индекс
uploadReq = other.uploadReq; // Необходимость загрузки
color = other.color;
radius = other.radius;
Node::operator=(other);
}
return *this;
}
Light::~Light()
{
}
// Рисование отладочных лампочек
void Light::render(ShaderProgram &shaderProgram, UBO &material_buffer)
{
// Загрузка модели лампочки при первом вызове функции
static Scene bulb = loadOBJtoScene("../resources/models/bulb.obj", "../resources/models/", "../resources/textures/");
static Model sphere = genShpere(1, 16, &bulb.root);
// Цикл по источникам света
for (int i = 0; i < count; i++)
{
// Сдвиг на позицию источника
bulb.root.e_position() = data[i].position;
sphere.e_scale() = glm::vec3(data[i].attenuation.r); // Масштабирование сферы
// Задание цвета
bulb.models.begin()->material.ka = sphere.material.ka = data[i].color;
// Вызов отрисовки
bulb.render(shaderProgram, material_buffer);
// Рисование сферы покрытия источника в режиме линий
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
sphere.render(shaderProgram, material_buffer);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
// Константный доступ к радиусу
const float& Light::c_radius() const
{
return radius;
}
// Неконстантная ссылка для изменений радиуса
float& Light::e_radius()
{
uploadReq = true;
return radius;
}

478
src/Model.cpp Normal file
View File

@ -0,0 +1,478 @@
#include "Model.h"
#include <algorithm>
// Конструктор с заданным родителем (по умолчанию NULL)
Node::Node(Node* parent_) : parent(parent_), result_transform(1), parent_changed(false),
position(0), rotation(1.0f, 0.0f, 0.0f, 0.0f), scale(1), changed(false), transform(1)
{
if (parent)
{
// Запишем себя в потомки
parent->children.push_back(this);
parent_changed = true;
}
}
// Конструктор копирования
Node::Node(const Node& copy): position(copy.position), rotation(copy.rotation), scale(copy.scale),
parent(copy.parent), changed(copy.changed), parent_changed(copy.parent_changed), transform(1), result_transform(1)
{
// Запишем себя в потомки
if (parent)
parent->children.push_back(this);
// Если у оригинала не было изменений - перепишем матрицу трансформации
if (!changed)
transform = copy.transform;
// Если у родителя не было изменений для оригинала - перепишем результирующую матрицу трансформации
if (!parent_changed)
result_transform = copy.result_transform;
}
Node::~Node()
{
setParent(NULL); // Удаляем себя из потомков
// Сообщаем потомкам об удалении родителя
for (Node* child : children)
child->setParent(NULL);
}
// Возвращает необходимость пересчета матрицы трансформации
bool Node::isChanged()
{
return changed;
}
// Константный доступ к позиции
const glm::vec3& Node::c_position() const
{
return position;
}
// Константный доступ к повороту
const glm::quat& Node::c_rotation() const
{
return rotation;
}
// Константный доступ к масштабированию
const glm::vec3& Node::c_scale() const
{
return scale;
}
// Неконстантная ссылка для изменений позиции
glm::vec3& Node::e_position()
{
changed = true; // Флаг о изменении
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
return position;
}
// Неконстантная ссылка для изменений поворота
glm::quat& Node::e_rotation()
{
changed = true; // Флаг о изменении
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
return rotation;
}
// Неконстантная ссылка для изменений масштабирования
glm::vec3& Node::e_scale()
{
changed = true; // Флаг о изменении
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
return scale;
}
// Возвращает матрицу трансформации модели
const glm::mat4& Node::getTransformMatrix()
{
// Если требуется - пересчитаем матрицу
recalcMatrices();
return result_transform;
}
// Пересчет матрицы трансформации модели, если это требуется
void Node::recalcMatrices()
{
// Если было изменение по векторам позиции, поворота и масштабирования
if (changed)
{
transform = glm::mat4(1.0f);
// Перемещение модели
transform = glm::translate(transform, position);
// Поворот модели
transform = transform * glm::mat4_cast(rotation);
// Масштабирование
transform = glm::scale(transform, scale);
}
// Если собственная или родительская матрицы менялись - необходимо пересчитать итоговую
if (changed || parent_changed)
{
if (parent) // Если есть родитель
result_transform = parent->getTransformMatrix() * transform;
else // Если нет родителя
result_transform = transform;
parent_changed = changed = false; // Изменения применены
}
}
// Проход потомков в глубину с изменением флага parent_changed
void Node::invalidateParent()
{
// Цикл по потомкам
for (Node* child : children)
{
child->parent_changed = true; // Флаг
child->invalidateParent(); // Рекурсивный вызов для потомков выбранного потомка
}
}
// Устанавливает родителя для узла
void Node::setParent(Node * parent)
{
// Если замена происходит на другого родителя
if (parent != this->parent)
{
Node* tmp = parent;
// Проверка на зацикливание об самого себя
while (tmp)
{
if (tmp == this)
return; // Можно выдать exception
tmp = tmp->parent;
}
// Если есть старый родитель - удалим себя из его потомков
if (this->parent)
{
// Поиск в списке родительских потомков
auto position = std::find(this->parent->children.begin(), this->parent->children.end(), this);
// Если итератор указывает в конец - ничего не найдено
if (position != this->parent->children.end())
this->parent->children.erase(position); // Само удаление
}
this->parent = parent; // Заменяем указатель на родителя
// Если родитель не NULL - добавляем себя в детей
if (parent)
parent->children.push_back(this);
// В любом случае необходимо пересчитать собственную итоговую матрицу
parent_changed = true;
}
}
// Возвращает указатель на родителя
Node* Node::getParent()
{
return parent;
}
// Возвращает ссылку на вектор дочерних узлов
const std::vector<Node*>& Node::getChildren() const
{
return children;
}
// Оператор присваивания
Node& Node::operator=(const Node& other)
{
position = other.position;
rotation = other.rotation;
scale = other.scale;
changed = other.changed;
if (!changed)
transform = other.transform;
setParent(other.parent);
// Если у other флаг parent_changed == false, то можно переписать матрицу результата с него
if (!other.parent_changed)
{
result_transform = other.result_transform;
parent_changed = false; // Сбрасываем флаг после смены родителя
}
return *this;
}
// Конструктор по умолчанию
Model::Model(Node *parent) : Node(parent), verteces_count(0), first_index_byteOffset(0), indices_count(0),
vertex_vbo(VERTEX), index_vbo(ELEMENT), normals_vbo(VERTEX), texCoords_vbo(VERTEX)
{
}
// Конструктор копирования
Model::Model(const Model& copy) : Node(copy),
vao(copy.vao),
verteces_count(copy.verteces_count), first_index_byteOffset(copy.first_index_byteOffset), 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::operator=(const Model& other)
{
Node::operator=(other); // Явный вызов родительского оператора копирования
vao = other.vao;
verteces_count = other.verteces_count;
first_index_byteOffset = other.first_index_byteOffset;
indices_count = other.indices_count;
vertex_vbo = other.vertex_vbo;
index_vbo = other.index_vbo;
texCoords_vbo = other.texCoords_vbo;
texture_diffuse = other.texture_diffuse;
texture_ambient = other.texture_ambient;
texture_specular = other.texture_specular;
material = other.material;
return *this;
}
Model::~Model()
{
}
// Вызов отрисовки без uniform-данных
void Model::render()
{
// Подключаем VAO
vao.use();
// Если есть индексы - рисуем с их использованием
if (indices_count)
{
index_vbo.use();
glDrawElements(GL_TRIANGLES, indices_count, GL_UNSIGNED_INT, (void*)(first_index_byteOffset));
}
// Если есть вершины - рисуем на основании массива вершин
else if (verteces_count)
glDrawArrays(GL_TRIANGLES, 0, verteces_count);
}
// Вызов отрисовки
void Model::render(ShaderProgram &shaderProgram, UBO &material_buffer)
{
// Расчитаем матрицу трансформации
glUniformMatrix4fv(shaderProgram.getUniformLoc("model"), 1, GL_FALSE, &this->getTransformMatrix()[0][0]);
// Подключаем текстуры
texture_diffuse.use();
texture_ambient.use();
texture_specular.use();
// Загружаем данные о материале
material_buffer.load(&material, sizeof(material));
render();
}
// Функция для конфигурации атрибута вершинного буфера
void vertex_attrib_config()
{
// Определим спецификацию атрибута
glVertexAttribPointer( 0 // индекс атрибута, должен совпадать с Layout шейдера
, 3 // количество компонент одного элемента
, GL_FLOAT // тип
, GL_FALSE // необходимость нормировать значения
, 0 // шаг
, (void *)0 // отступ с начала массива
);
// Включаем необходимый атрибут у выбранного VAO
glEnableVertexAttribArray(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()
{
// Определим спецификацию атрибута
glVertexAttribPointer( 1 // индекс атрибута, должен совпадать с Layout шейдера
, 2 // количество компонент одного элемента
, GL_FLOAT // тип
, GL_FALSE // необходимость нормировать значения
, 0 // шаг
, (void *)0 // отступ с начала массива
);
// Включаем необходимый атрибут у выбранного VAO
glEnableVertexAttribArray(1);
}
// Загрузка текстурных координат в буфер
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 и привязанным VBO
glVertexAttribPointer( 2 // индекс атрибута, должен совпадать с Layout шейдера
, 3 // количество компонент одного элемента
, GL_FLOAT // тип
, GL_FALSE // необходимость нормировать значения
, 0 // шаг
, (void *)0 // отступ с начала массива
);
// Включаем необходимый атрибут у выбранного VAO
glEnableVertexAttribArray(2);
}
// Загрузка нормалей в буфер
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();
}
// Ограничение диапазона из буфера индексов
void Model::set_index_range(size_t first_byteOffset, size_t count)
{
first_index_byteOffset = first_byteOffset;
indices_count = count;
}
// Привязка текстуры к модели
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;
};
}
// Генерирует сферу заданного радиуса с определенным количеством сегментов
Model genShpere(float radius, int sectorsCount, Node* parent)
{
Model result(parent);
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);
}
}
}
// Загрузка в модель
result.load_verteces(&vertices[0], vertices.size());
result.load_normals(&normals[0], normals.size());
result.load_indices(&indices[0], indices.size());
return result;
}

224
src/Scene.cpp Normal file
View File

@ -0,0 +1,224 @@
#include "Scene.h"
// Конструктор пустой сцены
Scene::Scene()
{
}
// Конструктор копирования
Scene::Scene(const Scene &copy): root(copy.root),
nodes(copy.nodes), models(copy.models), cameras(copy.cameras)
{
rebuld_tree(copy);
}
// Оператор присваивания
Scene& Scene::operator=(const Scene& other)
{
root = other.root;
nodes = other.nodes;
models = other.models;
cameras = other.cameras;
rebuld_tree(other);
return *this;
}
// Рендер сцены
void Scene::render(ShaderProgram &shaderProgram, UBO &material_buffer)
{
for (auto & model : models)
model.render(shaderProgram, material_buffer);
}
// Перестройка узлов выбранного списка
template <class T>
void Scene::rebuild_Nodes_list(T& nodes, const Scene& from)
{
for (auto it = nodes.begin(); it != nodes.end(); it++)
{
// Берем родителя, который указывает на оригинальный объект
Node* parent = it->getParent();
// Если родитель - оригинальный корневой узел, то меняем на собственный корневой узел
if (parent == &from.root)
{
it->setParent(&root);
continue;
}
// Если можно привести к модели, то ищем родителя среди моделей
if (dynamic_cast<Model*>(parent))
move_parent(*it, from.models, this->models);
else
// Иначе проверяем на принадлежность к камерам
if (dynamic_cast<Camera*>(parent))
move_parent(*it, from.cameras, this->cameras);
// Иначе это пустой узел
else
move_parent(*it, from.nodes, this->nodes);
// Не нашли родителя - значит он не часть этой сцены
// и изменений по нему не требуется
}
}
// Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному
template <class T>
void Scene::move_parent(Node& for_node, const std::list<T>& from_nodes, std::list<T>& this_nodes)
{
// Возьмем адрес родителя
Node* parent = for_node.getParent();
// Цикл по элементам списков для перемещения родителя
// Списки в процессе копирования идеинтичные, вторая проверка не требуется
for (auto it_from = from_nodes.begin(), it_this = this_nodes.begin(); it_from != from_nodes.end(); ++it_from, ++it_this)
// Если адрес объекта, на который указывает итератор, совпадает с родителем - меняем родителя по второму итератору (it_this)
if (&(*it_from) == parent)
for_node.setParent(&(*it_this));
}
// Перестройка дерева после копирования или присваивания
void Scene::rebuld_tree(const Scene& from)
{
// Восстановим родителей в пустых узлах для копии
rebuild_Nodes_list(nodes, from);
rebuild_Nodes_list(models, from);
rebuild_Nodes_list(cameras, from);
}
#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...);
}
Scene loadOBJtoScene(const char* filename, const char* mtl_directory, const char* texture_directory)
{
Scene result;
Model model;
// Все модели образованные на основании этой модели будут иметь общего родителя
model.setParent(&result.root);
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
// Если в процессе загрузки возникли ошибки - выдадим исключение
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.models.push_back(model); // Создание копии с общим VAO
auto s = --result.models.end();
s->set_index_range(materials_range[i]*sizeof(GLuint), 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 = glm::vec3(materials[materials_ids[i]].ambient[0], materials[materials_ids[i]].ambient[1], materials[materials_ids[i]].ambient[2]);
s->material.kd = glm::vec3(materials[materials_ids[i]].diffuse[0], materials[materials_ids[i]].diffuse[1], materials[materials_ids[i]].diffuse[2]);
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;
}

154
src/Shader.cpp Normal file
View 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 &copy) : 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);
}

149
src/Texture.cpp Normal file
View File

@ -0,0 +1,149 @@
#include "Texture.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
std::map<std::string, int> Texture::filename_handler; // Получение дескриптора текстуры по её имени
std::map<int, int> Texture::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_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
else if (channels == 4) // RGBA
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 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(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;
}
Texture::~Texture()
{
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::reallocate(GLuint width, GLuint height, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
{
use();
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, dataType, NULL);
}
// Привязка текстуры
void Texture::use()
{
glActiveTexture(type + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
}
// Отвязка текстуры по типу
void Texture::disable(GLuint type)
{
glActiveTexture(type + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка текстуры
}
// Возвращает тип текстуры
GLuint Texture::getType()
{
return type;
}
// Задает тип текстуры
void Texture::setType(GLuint type)
{
this->type = type;
}

249
src/main.cpp Executable file
View File

@ -0,0 +1,249 @@
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <GLM/glm.hpp>
#include <iostream>
#include "Scene.h"
#include "Shader.h"
#include "Lights.h"
#define WINDOW_CAPTION "OPENGL notes on rekovalev.site"
// Указатели на текстуры для изменения размеров окна
Texture* pgPosition = NULL;
Texture* pgNormal = NULL;
Texture* pgDiffuseP = NULL;
Texture* pgAmbientSpecular = NULL;
RBO* pgrbo = NULL;
// Размеры окна
int WINDOW_WIDTH = 800;
int WINDOW_HEIGHT = 600;
// Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
// Изменение размеров текстур для G-буфера
if (pgPosition)
pgPosition->reallocate(width, height, 0, GL_RGB32F, GL_RGB);
if (pgNormal)
pgNormal->reallocate(width, height, 1, GL_RGB16F, GL_RGB);
if (pgDiffuseP)
pgDiffuseP->reallocate(width, height, 2, GL_RGBA16F);
if (pgAmbientSpecular)
pgAmbientSpecular->reallocate(width, height, 3);
// И буфера глубины
if (pgrbo)
pgrbo->reallocate(width, height);
// Запомним новые размеры окна
WINDOW_WIDTH = width;
WINDOW_HEIGHT = height;
// Изменим параметры перспективной матрицы проекции для камеры
Camera::current().setPerspective(CAMERA_FOVy, (float)width/height);
}
bool firstMouse = true;
float lastX, lastY;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
glm::vec2 offset(xpos - lastX, lastY - ypos);
lastX = xpos;
lastY = ypos;
Camera::current().rotate(offset);
}
int main(void)
{
GLFWwindow* window; // Указатель на окно GLFW3
// Инициализация GLFW3
if (!glfwInit())
{
std::cout << "GLFW init error\n";
return -1;
}
// Завершение работы с GLFW3 перед выходом
atexit(glfwTerminate);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Минорная версия спецификаций OpenGL
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Контекст OpenGL, который поддерживает только основные функции
// Создание окна GLFW3 с заданными шириной, высотой и заголовком окна
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION, NULL, NULL);
if (!window)
{
std::cout << "GLFW create window error\n";
return -1;
}
// Установка основного контекста окна
glfwMakeContextCurrent(window);
// Установка callback-функции для изменения размеров окна и буфера кадра
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSwapInterval(1); // Вертикальная синхронизация
// Установка callback-функции для мыши и камеры
glfwSetCursorPosCallback(window, mouse_callback);
// Загрузка функций OpenGL с помощью GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "GLAD load GL error\n";
return -1;
}
// Включаем проверку по буферу глубины
glEnable(GL_DEPTH_TEST);
// Шейдер для G-буфера
ShaderProgram gShader;
// Загрузка и компиляция шейдеров
gShader.load(GL_VERTEX_SHADER, "shaders/gshader.vert");
gShader.load(GL_FRAGMENT_SHADER, "shaders/gshader.frag");
gShader.link();
// Установим значения текстур
const char* textures_base_shader_names[] = {"tex_diffuse", "tex_ambient", "tex_specular"};
gShader.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*));
// Загрузка сцены из obj файла
Scene scene = loadOBJtoScene("../resources/models/blob.obj", "../resources/models/", "../resources/textures/");
scene.root.e_scale() = glm::vec3(0.01);
scene.root.e_position().z = 1;
scene.models.begin()->material.kd = {0.5,0.5,0.5};
scene.models.begin()->material.ka = {0.2,0.2,0.2};
// Установка цвета очистки буфера цвета
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Источники света
Light& first = Light::getNew();
first.e_color() = {1.0f, 0.0f, 0.0f}; // цвет
first.e_position() = {0.3f, 0.1f, 0.5f}; // Позиция
Light& second = Light::getNew();
second.e_color() = {0.0f, 0.0f, 1.0f}; // цвет
second.e_position() = {-0.3f, -0.1f, 0.5f}; // Позиция
// Uniform-буферы
UBO cameraUB(sizeof(CameraData), 0);
UBO material_data(sizeof(Material), 1);
UBO light_data(Light::getUBOsize(), 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_RGB32F, GL_RGB); // Позиция вершины
Texture gNormal(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT1, 1, GL_RGB16F, GL_RGB); // Нормали
Texture gDiffuseP(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT2, 2, GL_RGBA16F); // Диффузная составляющая и коэф. глянцевости
Texture gAmbientSpecular(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT3, 3); // Фоновая составляющая и один канал зеркальной
// Создадим буфер рендера под буфер глубины и привяжем его
RBO grbo(WINDOW_WIDTH, WINDOW_HEIGHT);
gbuffer.assignRenderBuffer(grbo.getHandler());
// Активируем базовый буфер кадра
FBO::useDefault();
// Сохраним указатели на текстуры для изменения размеров окна
pgPosition = &gPosition;
pgNormal = &gNormal;
pgDiffuseP = &gDiffuseP;
pgAmbientSpecular = &gAmbientSpecular;
pgrbo = &grbo;
// Шейдер для расчета освещенности
ShaderProgram lightShader;
// Загрузка и компиляция шейдеров
lightShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
lightShader.load(GL_FRAGMENT_SHADER, "shaders/lighting.frag");
lightShader.link();
const char* gtextures_shader_names[] = {"gPosition", "gNormal", "gDiffuseP", "gAmbientSpecular"};
lightShader.bindTextures(gtextures_shader_names, sizeof(gtextures_shader_names)/sizeof(const char*));
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);
// Шейдер для рисования отладочных лампочек
ShaderProgram bulbShader;
// Загрузка и компиляция шейдеров
bulbShader.load(GL_VERTEX_SHADER, "shaders/bulb.vert");
bulbShader.load(GL_FRAGMENT_SHADER, "shaders/bulb.frag");
bulbShader.link();
// Пока не произойдет событие запроса закрытия окна
while(!glfwWindowShouldClose(window))
{
// Загрузка данных о камере
cameraUB.loadSub(&Camera::current().getData(), sizeof(CameraData));
// Загрузим информацию об источниках света
Light::upload(light_data);
// Активируем G-кадра
gbuffer.use();
// Используем шейдер с освещением
gShader.use();
// Очистка буфера цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Тут производится рендер
scene.render(gShader, material_data);
// Активируем базовый буфер кадра
FBO::useDefault();
// Подключаем шейдер для прямоугольника
lightShader.use();
// Очистка буфера цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Подключаем текстуры G-буфера
gPosition.use();
gNormal.use();
gDiffuseP.use();
gAmbientSpecular.use();
// Рендерим прямоугольник с расчетом освещения
quadModel.render();
// Перенос буфера глубины
FBO::useDefault(GL_DRAW_FRAMEBUFFER); // Базовый в режиме записи
gbuffer.use(GL_READ_FRAMEBUFFER); // Буфер геометрии в режиме чтения
// Копирование значений глубины
glBlitFramebuffer(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
FBO::useDefault(); // Использование базового буфера для дальнейших работ
// Отрисовка отладочных лампочек со специальным шейдером
bulbShader.use();
Light::render(bulbShader, material_data);
// Представление содержимого буфера цепочки показа на окно
glfwSwapBuffers(window);
// Обработка системных событий
glfwPollEvents();
}
return 0;
}