Compare commits

...

7 Commits
v0.1 ... master

11 changed files with 184 additions and 49 deletions

View File

@ -11,7 +11,7 @@
// Ближняя граница области отсечения
#define CAMERA_NEAR 0.1f
// Дальняя граница области отсечения
#define CAMERA_FAR 15.0f
#define CAMERA_FAR 100.0f
// Вектор, задающий верх для камеры
#define CAMERA_UP_VECTOR glm::vec3(0.0f, 1.0f, 0.0f)
// Вектор, задающий стандартный поворот углами Эйлера (в положительном направлении оси Z)
@ -20,6 +20,11 @@
#define CAMERA_FOVy 60.0f
// Стандартная чувствительность
#define CAMERA_DEFAULT_SENSIVITY 0.005f
// Количество каскадов для карт теней
#define CAMERA_CASCADE_COUNT 4
// Данные о дистанциях каскадов
extern const float camera_cascade_distances[]; // src/Camera.cpp
// Данные о камере для шейдера
struct CameraData
@ -54,7 +59,7 @@ class Camera : public Node
static Camera& current(); // Ссылка на текущую используемую камеру
CameraData& getData(); // Данные о камере для шейдера
std::pair<bool, const glm::vec4*> getProjCoords(); // Доступ к координатам с флагом изменения, описывающим пространство вида с пересчетом, если это требуется
std::pair<bool, const glm::vec4(*)[8]> getProjCoords(); // Доступ к координатам с флагом изменения, описывающим пространство вида с пересчетом, если это требуется
protected:
Camera(const glm::vec3 &position, const glm::vec3 &initialRotation); // Защищенный (protected) конструктор камеры без перспективы
@ -63,7 +68,8 @@ class Camera : public Node
glm::mat4 vp; // Матрица произведения вида и проекции
bool requiredRecalcVP; // Необходимость пересчета матрицы вида и проекции камеры
bool requiredRecalcCoords; // Необходимость пересчета точек, описывающих пространство камеры
glm::vec4 coords[8]; // Координаты, описывающие пространство камеры
glm::vec4 coords[CAMERA_CASCADE_COUNT][8]; // Координаты, описывающие пространство камеры
glm::mat4 cascade_proj[CAMERA_CASCADE_COUNT]; // Матрицы проекций каскадов
float sensitivity; // Чувствительность мыши

View File

@ -4,6 +4,7 @@
#include <GLM/glm.hpp>
#include "Model.h"
#include "Camera.h"
// Максимальное число источников света
#define MAX_LIGHTS 300
@ -81,7 +82,7 @@ class Sun
alignas(16) glm::vec3 direction; // Направление лучей источника
alignas(16) glm::vec3 color; // Цвет
alignas(16) glm::mat4 vp; // Матрица вида-проекции источника
alignas(16) glm::mat4 vp[CAMERA_CASCADE_COUNT]; // Матрица вида-проекции источника
void recalcVP(); // Пересчитывает по необходимости матрицу вида-проекции

View File

@ -44,4 +44,18 @@ class Texture : public BaseTexture
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); // Оператор присваивания
void reallocate(GLuint levels, GLuint width, GLuint height, GLuint texType = TEX_DIFFUSE, GLint internalformat = GL_RGBA, GLint format = GL_RGBA, GLenum dataType = GL_FLOAT); // Пересоздает текстуру для имеющегося дескриптора
virtual void use(); // Привязка текстуры
};
#endif // TEXTURE_H

6
shaders/empty.frag Normal file
View File

@ -0,0 +1,6 @@
#version 330 core
void main()
{
}

View File

@ -27,14 +27,16 @@ layout(std140, binding = 3) uniform Sun
{
vec3 direction;
vec3 color;
mat4 vp;
mat4 vp[4];
} sun;
uniform float camera_cascade_distances[4]; // Размер массива должен соответствовать количеству каскадов
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gDiffuseP;
uniform sampler2D gAmbientSpecular;
uniform sampler2D sunShadowDepth;
uniform sampler2DArray sunShadowDepth;
out vec4 color;
@ -60,10 +62,18 @@ void main()
float intensity; // Интенсивность для прожектора
vec3 fragPosLightSpace; // Фрагмент в пространстве источника
float shadowValue; // Значение затененности
vec2 texelSize = 1.0 / textureSize(sunShadowDepth, 0); // Размер текселя текстуры теней
vec2 texelSize = 1.0 / textureSize(sunShadowDepth, 0).xy; // Размер текселя текстуры теней
int x, y; // Счетчик для PCF
float pcfDepth; // Глубина PCF
vec4 fragPosCamSpace = camera.view * vec4(fragPos, 1); // Фрагмент в пространстве камеры
int cascade_index; // Индекс текущего каскада для вычисления теней
// Определение индекса каскада в который попадает фрагмент (цикл на 1 меньше чем кол-во каскадов)
for (cascade_index = 0; cascade_index < 3; cascade_index++)
if (abs(fragPosCamSpace.z) < camera_cascade_distances[cascade_index])
break;
// Фоновая освещенность
color = vec4(ka, 1);
@ -71,7 +81,7 @@ void main()
if (length(sun.color) > 0)
{
// Расположение фрагмента в координатах теневой карты
fragPosLightSpace = (sun.vp * vec4(fragPos, 1.0)).xyz;
fragPosLightSpace = (sun.vp[cascade_index] * vec4(fragPos, 1.0)).xyz;
// Переход от [-1;1] к [0;1]
fragPosLightSpace = (fragPosLightSpace + vec3(1.0)) / 2;
// Сдвиг для решения проблемы акне
@ -82,7 +92,7 @@ void main()
{
for(y = -1; y <= 1; ++y)
{
pcfDepth = texture(sunShadowDepth, fragPosLightSpace.xy + vec2(x, y) * texelSize).r;
pcfDepth = texture(sunShadowDepth, vec3(fragPosLightSpace.xy + vec2(x, y) * texelSize, cascade_index)).r;
shadowValue += fragPosLightSpace.z > pcfDepth ? 1.0 : 0.0;
}
}

22
shaders/sun_shadow.geom Normal file
View File

@ -0,0 +1,22 @@
#version 420 core
layout(triangles, invocations = 4) in; // здесь invocations должно соответствовать количеству каскадов
layout(triangle_strip, max_vertices = 3) out;
layout(std140, binding = 3) uniform Sun
{
vec3 direction;
vec3 color;
mat4 vp[4];
} sun;
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();
}

View File

@ -4,14 +4,7 @@ layout (location = 0) in vec3 pos;
uniform mat4 model;
layout(std140, binding = 3) uniform Sun
{
vec3 direction;
vec3 color;
mat4 vp;
} sun;
void main()
{
gl_Position = sun.vp * model * vec4(pos, 1.0);
gl_Position = model * vec4(pos, 1.0);
}

View File

@ -3,6 +3,9 @@
// Указатель на текущую используемую камеру
Camera* Camera::p_current = NULL;
// Границы каскадов
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::vec3 &initialRotation) : Node(NULL) // Пусть по умолчанию камера не относится к сцене
{
@ -33,7 +36,8 @@ Camera::Camera(float width, float height, const glm::vec3 &position, const glm::
// Конструктор копирования камеры
Camera::Camera(const Camera& copy)
: Node(copy), projection(copy.projection), requiredRecalcVP(copy.requiredRecalcVP), sensitivity(copy.sensitivity)
: Node(copy), projection(copy.projection), requiredRecalcVP(copy.requiredRecalcVP), sensitivity(copy.sensitivity),
requiredRecalcCoords(true)
{
// Если у оригинала не было изменений - перепишем матрицу вида-проекции
if (!requiredRecalcVP)
@ -90,6 +94,8 @@ void Camera::setPerspective(float fovy, float aspect)
{
projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR);
requiredRecalcVP = 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]);
}
// Устанавливает заданную ортографическую матрицу
@ -98,6 +104,9 @@ 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;
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]);
}
// Изменяет чувствительность мыши
@ -192,7 +201,7 @@ CameraData& Camera::getData()
}
// Доступ к координатам с флагом изменения, описывающим пространство вида с пересчетом, если это требуется
std::pair<bool, const glm::vec4*> Camera::getProjCoords()
std::pair<bool, const glm::vec4(*)[8]> Camera::getProjCoords()
{
const glm::mat4& cam_vp = getVP(); // Получение ссылки на матрицу вида-проекции с пересчетом, если требуется и активацией флага requiredRecalcCoords
bool changes = false; // Возвращаемое значение
@ -210,11 +219,16 @@ std::pair<bool, const glm::vec4*> Camera::getProjCoords()
, {-1, 1,-1,1}
, {-1,-1, 1,1}
, {-1,-1,-1,1}};
// Цикл по типовым точкам
for (int i = 0; i < 8; i++)
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
{
coords[i] = inv * typical_points[i];
coords[i] /= coords[i].w; // Переход от гомогенных координат к обычным
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; // Сбрасываем флаг

View File

@ -305,38 +305,44 @@ glm::vec3& Sun::e_color()
// Пересчитывает по необходимости матрицу вида-проекции
void Sun::recalcVP()
{
std::pair <bool, const glm::vec4*> camProjCoords = Camera::current().getProjCoords();
// Точки по краям проекции камеры
std::pair <bool, const glm::vec4(*)[8]> camProjCoords = Camera::current().getProjCoords();
// Есть изменения по источнику или камере
if (uploadReq || camProjCoords.first)
{
uploadReq = true; // Требуется загрузка в следствии пересчета матрицы
glm::vec3 mean = glm::vec3(0); // Среднее арифметическое
glm::vec3 mean; // Среднее арифметическое
glm::vec4 max, min; // макс и мин координаты
glm::vec4 point; // Точка приведенная в пространство источника света
// Найдем среднее арифметическое от точек для нахождения центра прямоугольника
for (int i = 0; i < 8; i++)
mean += glm::vec3(camProjCoords.second[i]);
mean /= 8;
// Используем среднее арифметическое для получения матрицы вида параллельного источника
glm::mat4 lightView = glm::lookAt(mean + glm::normalize(direction), mean, CAMERA_UP_VECTOR);
// Примем первую точку как минимальную и максимальную (приведя в пространство вида источника)
min = max = lightView * camProjCoords.second[0];
// Для оставшихся точек
for (int i = 1; i < 8; i++)
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
{
// Приведем в пространство вида источника
point = lightView * camProjCoords.second[i];
max = glm::max(max, point);
min = glm::min(min, point);
}
mean = glm::vec3(0);
// Найдем среднее арифметическое от точек для нахождения центра прямоугольника
for (int i = 0; i < 8; i++)
mean += glm::vec3(camProjCoords.second[cascade][i]);
mean /= 8;
// Используем среднее арифметическое для получения матрицы вида параллельного источника
glm::mat4 lightView = glm::lookAt(mean + glm::normalize(direction), mean, CAMERA_UP_VECTOR);
// Максимальное значение глубины
max.z = std::max(fabs(max.z), fabs(min.z));
// На основании максимальных и минимальных координат создадим матрицу проекции источника
vp = glm::ortho(min.x, max.x, min.y, max.y, min.z, max.z) * lightView;
// Примем первую точку как минимальную и максимальную (приведя в пространство вида источника)
min = max = lightView * camProjCoords.second[cascade][0];
// Для оставшихся точек
for (int i = 1; i < 8; i++)
{
// Приведем в пространство вида источника
point = lightView * camProjCoords.second[cascade][i];
max = glm::max(max, point);
min = glm::min(min, point);
}
// Максимальное значение глубины
max.z = std::max(fabs(max.z), fabs(min.z));
// На основании максимальных и минимальных координат создадим матрицу проекции источника
vp[cascade] = glm::ortho(min.x, max.x, min.y, max.y, min.z, max.z) * lightView;
}
}
}

View File

@ -151,3 +151,62 @@ 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::reallocate(GLuint levels, 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 TextureArray::use()
{
glActiveTexture(type + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, handler); // Привязка текстуры как активной
}

View File

@ -182,6 +182,8 @@ int main(void)
lightShader.link();
const char* gtextures_shader_names[] = {"gPosition", "gNormal", "gDiffuseP", "gAmbientSpecular", "sunShadowDepth"};
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}
@ -200,12 +202,12 @@ int main(void)
// Создадим буфер кадра для рендера теней
FBO sunShadowBuffer;
// Создадим текстуры для буфера кадра
Texture sunShadowDepth(sunShadow_resolution, sunShadow_resolution, GL_DEPTH_ATTACHMENT, 4, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
TextureArray sunShadowDepth(CAMERA_CASCADE_COUNT, sunShadow_resolution, sunShadow_resolution, GL_DEPTH_ATTACHMENT, 4, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
// Правка фантомных теней
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
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, GL_TEXTURE_BORDER_COLOR, shadowBorderColor);
glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, shadowBorderColor);
// Отключим работу с цветом
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
@ -216,6 +218,8 @@ int main(void)
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();
// Модель прямоугольника