Compare commits

...

8 Commits
v0.1 ... master

6 changed files with 192 additions and 40 deletions

View File

@ -18,6 +18,7 @@ struct LightData
alignas(16) glm::vec3 color; // Цвет alignas(16) glm::vec3 color; // Цвет
alignas(16) glm::vec3 attenuation; // Радиус действия источника, линейный и квадратичный коэф. угасания alignas(16) glm::vec3 attenuation; // Радиус действия источника, линейный и квадратичный коэф. угасания
alignas(16) glm::vec4 direction_angle; // Направление и половинный угол освещенности alignas(16) glm::vec4 direction_angle; // Направление и половинный угол освещенности
alignas(16) glm::mat4 vp[6]; // Матрицы проекции и трансформации в пространство источника
}; };
// Источник света // Источник света
@ -30,6 +31,7 @@ class Light : public Node
static Light& getNew(); // Возвращает ссылку на новый источник света static Light& getNew(); // Возвращает ссылку на новый источник света
void destroy(); // Уничтожает источник света void destroy(); // Уничтожает источник света
static int getCount(); // Возвращает количество источников
const glm::vec3& c_color() const; // Константный доступ к цвету const glm::vec3& c_color() const; // Константный доступ к цвету
glm::vec3& e_color(); // Неконстантная ссылка для изменений цвета glm::vec3& e_color(); // Неконстантная ссылка для изменений цвета
@ -58,6 +60,7 @@ class Light : public Node
void toData(); // Преобразует информацию об источнике в структуру LightData void toData(); // Преобразует информацию об источнике в структуру LightData
virtual void recalcMatrices(); // Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed virtual void recalcMatrices(); // Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed
void recalcVP(); // Пересчитывает по необходимости матрицу вида-проекции
static GLuint count; // количество используемых источников (должно быть <= MAX_LIGHTS) static GLuint count; // количество используемых источников (должно быть <= MAX_LIGHTS)
static LightData data[MAX_LIGHTS]; // Массив данных по источникам света static LightData data[MAX_LIGHTS]; // Массив данных по источникам света

View File

@ -15,6 +15,7 @@ struct LightData
vec3 color; vec3 color;
vec3 attenuation; vec3 attenuation;
vec4 direction_angle; vec4 direction_angle;
mat4 vp[6];
}; };
layout(std140, binding = 2) uniform Light layout(std140, binding = 2) uniform Light
@ -37,6 +38,7 @@ uniform sampler2D gNormal;
uniform sampler2D gDiffuseP; uniform sampler2D gDiffuseP;
uniform sampler2D gAmbientSpecular; uniform sampler2D gAmbientSpecular;
uniform sampler2DArray sunShadowDepth; uniform sampler2DArray sunShadowDepth;
uniform samplerCubeArray pointShadowDepth;
out vec4 color; out vec4 color;
@ -63,8 +65,10 @@ void main()
vec3 fragPosLightSpace; // Фрагмент в пространстве источника vec3 fragPosLightSpace; // Фрагмент в пространстве источника
float shadowValue; // Значение затененности float shadowValue; // Значение затененности
vec2 texelSize = 1.0 / textureSize(sunShadowDepth, 0).xy; // Размер текселя текстуры теней vec2 texelSize = 1.0 / textureSize(sunShadowDepth, 0).xy; // Размер текселя текстуры теней
int x, y; // Счетчик для PCF int x, y, z; // Счетчик для PCF
float pcfDepth; // Глубина PCF float pcfDepth; // Глубина PCF
float cubemap_offset = 0.05f; // Отступ в текстурных координатах для PCF
float cubemap_depth; // Дистанция между фрагментом и источником в диапазоне [0;1]
vec4 fragPosCamSpace = camera.view * vec4(fragPos, 1); // Фрагмент в пространстве камеры vec4 fragPosCamSpace = camera.view * vec4(fragPos, 1); // Фрагмент в пространстве камеры
int cascade_index; // Индекс текущего каскада для вычисления теней int cascade_index; // Индекс текущего каскада для вычисления теней
@ -118,6 +122,30 @@ void main()
// Цикл по источникам света // Цикл по источникам света
int i; int i;
for (i = 0; i < light_f.count; 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].attenuation.r;
// Сдвиг для решения проблемы акне
cubemap_depth -= max(0.05 * (1.0 - dot(N, light_f.data[i].direction_angle.xyz)), 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_vertex = light_f.data[i].position - fragPos;
@ -154,8 +182,9 @@ void main()
specular *= intensity; specular *= intensity;
} }
color += vec4(light_f.data[i].color*kd*diffuse * attenuation, 1) color += ( vec4(light_f.data[i].color*kd*diffuse * attenuation, 1)
+ vec4(light_f.data[i].color*ks*specular * attenuation, 1); + vec4(light_f.data[i].color*ks*specular * attenuation, 1) ) * (1.0 - shadowValue);
}
} }
} }
} }

17
shaders/point_shadow.frag Normal file
View File

@ -0,0 +1,17 @@
#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;
}

38
shaders/point_shadow.geom Normal file
View File

@ -0,0 +1,38 @@
#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;
vec3 attenuation;
vec4 direction_angle;
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].attenuation.r;
gl_Position = light_g.data[light_i].vp[gl_InvocationID] * gl_in[i].gl_Position;
gl_Layer = gl_InvocationID + light_i*6;
EmitVertex();
}
EndPrimitive();
}

View File

@ -98,7 +98,15 @@ void Light::toData()
{ {
check_id(); // Проверка на работу с корректным индексом check_id(); // Проверка на работу с корректным индексом
// Если позиция изменилась
if (data[index].position.x != result_transform[3].x
|| data[index].position.y != result_transform[3].y
|| data[index].position.z != result_transform[3].z
)
{
data[index].position = glm::vec3(result_transform[3]); // Позиция из матрицы трансформации data[index].position = glm::vec3(result_transform[3]); // Позиция из матрицы трансформации
recalcVP(); // Пересчет матрицы вида-проекции для расчета теней
}
data[index].color = color; // Цвет data[index].color = color; // Цвет
// Если радиус изменился // Если радиус изменился
if (data[index].attenuation.r != radius) if (data[index].attenuation.r != radius)
@ -346,3 +354,22 @@ void Sun::recalcVP()
} }
} }
} }
// Пересчитывает по необходимости матрицу вида-проекции
void Light::recalcVP()
{
float near_plane = 0.1f;
glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), 1.0f, near_plane, radius);
data[index].vp[0] = shadowProj * glm::lookAt(position, position + glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f));
data[index].vp[1] = shadowProj * glm::lookAt(position, position + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f));
data[index].vp[2] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
data[index].vp[3] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f));
data[index].vp[4] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f));
data[index].vp[5] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f));
}
// Возвращает количество источников
int Light::getCount()
{
return count;
}

View File

@ -139,11 +139,11 @@ int main(void)
// Источники света // Источники света
Light& first = Light::getNew(); Light& first = Light::getNew();
first.e_color() = {1.0f, 0.0f, 0.0f}; // цвет first.e_color() = {1.0f, 0.0f, 0.0f}; // цвет
first.e_position() = {0.3f, 0.1f, 0.5f}; // Позиция first.e_position() = {0.3f, 0.0f, 0.6f}; // Позиция
first.e_angle() = 70.0f; first.e_angle() = 100.0f;
Light& second = Light::getNew(); Light& second = Light::getNew();
second.e_color() = {0.0f, 0.0f, 1.0f}; // цвет second.e_color() = {0.0f, 0.0f, 1.0f}; // цвет
second.e_position() = {-0.3f, -0.1f, 0.5f}; // Позиция second.e_position() = {-0.3f, 0.3f, 0.5f}; // Позиция
// Uniform-буферы // Uniform-буферы
UBO cameraUB(sizeof(CameraData), 0); UBO cameraUB(sizeof(CameraData), 0);
@ -180,7 +180,7 @@ int main(void)
lightShader.load(GL_VERTEX_SHADER, "shaders/quad.vert"); lightShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
lightShader.load(GL_FRAGMENT_SHADER, "shaders/lighting.frag"); lightShader.load(GL_FRAGMENT_SHADER, "shaders/lighting.frag");
lightShader.link(); lightShader.link();
const char* gtextures_shader_names[] = {"gPosition", "gNormal", "gDiffuseP", "gAmbientSpecular", "sunShadowDepth"}; const char* gtextures_shader_names[] = {"gPosition", "gNormal", "gDiffuseP", "gAmbientSpecular", "sunShadowDepth", "pointShadowDepth"};
lightShader.bindTextures(gtextures_shader_names, sizeof(gtextures_shader_names)/sizeof(const char*)); 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]); glUniform1fv(lightShader.getUniformLoc("camera_cascade_distances"), CAMERA_CASCADE_COUNT, &camera_cascade_distances[1]);
@ -222,6 +222,26 @@ int main(void)
sunShadowShader.load(GL_FRAGMENT_SHADER, "shaders/empty.frag"); sunShadowShader.load(GL_FRAGMENT_SHADER, "shaders/empty.frag");
sunShadowShader.link(); 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();
// Модель прямоугольника // Модель прямоугольника
Model rectangle; Model rectangle;
@ -254,8 +274,8 @@ int main(void)
rectangle.e_scale() = glm::vec3(4); rectangle.e_scale() = glm::vec3(4);
// Параметры материала // Параметры материала
rectangle.material.ka = {0.4, 0.4, 0.4}; rectangle.material.ka = {0.2, 0.2, 0.2};
rectangle.material.kd = {0.4, 0.4, 0.4}; rectangle.material.kd = {0.9, 0.9, 0.9};
// Шейдер для рисования отладочных лампочек // Шейдер для рисования отладочных лампочек
ShaderProgram bulbShader; ShaderProgram bulbShader;
@ -363,6 +383,23 @@ int main(void)
scene.render(sunShadowShader, material_data); scene.render(sunShadowShader, material_data);
rectangle.render(sunShadowShader, material_data); rectangle.render(sunShadowShader, material_data);
// Изменим размер вывода для стороны кубической карты точечного источника
glViewport(0, 0, pointShadow_resolution, pointShadow_resolution);
// Активируем буфер кадра для теней от солнца
pointShadowBuffer.use();
// Подключим шейдер для расчета теней
pointShadowShader.use();
// Очистка буфера глубины
glClear(GL_DEPTH_BUFFER_BIT);
// Для каждого источника вызывается рендер сцены
for (int i = 0; i < Light::getCount(); i++)
{
glUniform1i(pointShadowShader.getUniformLoc("light_i"), i);
// Рендерим геометрию в буфер глубины
scene.render(pointShadowShader, material_data);
rectangle.render(pointShadowShader, material_data);
}
// Изменим размер вывода для окна // Изменим размер вывода для окна
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
// Активируем базовый буфер кадра // Активируем базовый буфер кадра
@ -378,6 +415,7 @@ int main(void)
gAmbientSpecular.use(); gAmbientSpecular.use();
// Подключаем текстуры теней // Подключаем текстуры теней
sunShadowDepth.use(); sunShadowDepth.use();
pointShadowDepth.use();
// Рендерим прямоугольник с расчетом освещения // Рендерим прямоугольник с расчетом освещения
quadModel.render(); quadModel.render();