/* by korenkonder GitHub/GitLab: korenkonder */ #include "blur.hpp" #include "../fbo.hpp" #include "../gl_state.hpp" #include "../post_process.hpp" #include "../render_context.hpp" #include "../shader_ft.hpp" struct gaussian_coef_shader_data { vec4 g_coef[8]; }; static void post_process_blur_radius_calculate(post_process_blur* blur); static void post_process_blur_radius_calculate_gaussian_kernel(float_t* gaussian_kernel, float_t radius, int32_t stride, int32_t offset); post_process_blur::post_process_blur() : data(), width(), height(), width_down(), height_down(), tex_down(), count_down() { reduce_width[0] = 256; reduce_height[0] = 144; reduce_width[1] = 128; reduce_height[1] = 72; reduce_width[2] = 64; reduce_height[2] = 36; reduce_width[3] = 32; reduce_height[3] = 18; reduce_width[4] = 8; reduce_height[4] = 8; for (int32_t i = 0; i < 5; i++) reduce_texture[i].Init(reduce_width[i], reduce_height[i], 0, GL_RGBA16F, 0); downsample_texture.Init(reduce_width[0], reduce_height[0], 0, GL_RGBA16F, 0); gaussian_coef_ubo.Create(sizeof(gaussian_coef_shader_data)); } post_process_blur:: ~post_process_blur() { if (!this) return; gaussian_coef_ubo.Destroy(); for (int32_t i = 0; i < count_down; i++) tex_down[i].Free(); free_def(tex_down); free_def(height_down); free_def(width_down); } void post_process_blur::downsample(RenderTexture* rt) { int32_t i = 0; if (count_down > 0) { uniform_value[U_REDUCE] = 1; shaders_ft.set(SHADER_FT_REDUCE); for (; i < count_down; i++) { glViewport(0, 0, width_down[i], height_down[i]); tex_down[i].Bind(); gl_state_active_bind_texture_2d(0, (i ? tex_down[i - 1] : *rt).GetColorTex()); RenderTexture::DrawQuad(&shaders_ft, width_down[i], height_down[i], 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); } i--; } uniform_value[U_REDUCE] = 3; shaders_ft.set(SHADER_FT_REDUCE); glViewport(0, 0, reduce_width[0], reduce_height[0]); reduce_texture[0].Bind(); gl_state_active_bind_texture_2d(0, (count_down > 0 ? tex_down[i] : *rt).GetColorTex()); RenderTexture::DrawQuad(&shaders_ft, width, height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.1f, 1.1f, 1.1f, 0.0f); downsample_texture.Bind(); gl_state_active_bind_texture_2d(0, reduce_texture[0].GetColorTex()); for (int32_t i = 1; i < 5; i++) { float_t scale; if (i == 4) { scale = 1.0f; uniform_value[U_EXPOSURE] = 0; shaders_ft.set(SHADER_FT_EXPOSURE); } else { scale = 0.75; uniform_value[U_REDUCE] = 1; shaders_ft.set(SHADER_FT_REDUCE); } glViewport(0, 0, reduce_width[i], reduce_height[i]); RenderTexture::DrawQuad(&shaders_ft, reduce_width[i - 1], reduce_height[i - 1], 1.0f, 1.0f, 0.0f, 0.0f, scale, 1.0f, 1.0f, 1.0f, 1.0f); gl_state_active_bind_texture_2d(0, reduce_texture[i].GetColorTex()); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, reduce_width[i], reduce_height[i]); } } void post_process_blur::get_blur() { uniform_value[U_ALPHA_MASK] = 0; vec3 intensity = data.intensity; vec3* gauss = data.gauss; gaussian_coef_shader_data gaussian_coef = {}; for (int32_t i = 0; i < POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE && i < 8; i++) { *(vec3*)&gaussian_coef.g_coef[i] = gauss[i] * intensity; gaussian_coef.g_coef[i].w = 0.0f; } gaussian_coef_ubo.WriteMemory(gaussian_coef); uniform_value[U_GAUSS] = 0; shaders_ft.set(SHADER_FT_GAUSS); gaussian_coef_ubo.Bind(1); downsample_texture.Bind(); for (int32_t i = 1; i < 4; i++) { glViewport(0, 0, reduce_width[i], reduce_height[i]); gl_state_active_bind_texture_2d(0, reduce_texture[i].GetColorTex()); RenderTexture::DrawQuad(&shaders_ft, reduce_width[i], reduce_height[i], 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, reduce_width[i], reduce_height[i]); } for (int32_t i = 1; i < 4; i++) { glViewport(0, 0, reduce_width[i], reduce_height[i]); gl_state_active_bind_texture_2d(0, reduce_texture[i].GetColorTex()); RenderTexture::DrawQuad(&shaders_ft, reduce_width[i], reduce_height[i], 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, reduce_width[i], reduce_height[i]); } uniform_value[U_GAUSS] = 1; shaders_ft.set(SHADER_FT_GAUSS); glViewport(0, 0, reduce_width[0], reduce_height[0]); gl_state_active_bind_texture_2d(0, reduce_texture[0].GetColorTex()); RenderTexture::DrawQuad(&shaders_ft, reduce_width[0], reduce_height[0], 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, data.intensity.x * 0.5f, data.intensity.y * 0.5f, data.intensity.z * 0.5f, 1.0f); uniform_value[U_REDUCE] = 7; shaders_ft.set(SHADER_FT_REDUCE); glViewport(0, 0, reduce_width[0], reduce_height[0]); reduce_texture[0].Bind(); gl_state_active_bind_texture_2d(0, downsample_texture.GetColorTex()); gl_state_active_bind_texture_2d(1, reduce_texture[1].GetColorTex()); gl_state_active_bind_texture_2d(2, reduce_texture[2].GetColorTex()); gl_state_active_bind_texture_2d(3, reduce_texture[3].GetColorTex()); RenderTexture::DrawQuad(&shaders_ft, reduce_width[3], reduce_height[3], 1.0f, 1.0f, 0.0f, 0.0f, 0.25f, 0.15f, 0.25f, 0.25f, 0.25f); } void post_process_blur::init_fbo(int32_t width, int32_t height) { if (!this || (this->width == width && this->height == height)) return; this->width = width > 1 ? width : 1; this->height = height > 1 ? height : 1; int32_t i = 0; width = this->width; height = this->height; while (width > 512 && height > 288) { width /= 2; height /= 2; i++; } RenderTexture* tex_down = this->tex_down; int32_t* width_down = this->width_down; int32_t* height_down = this->height_down; if (!tex_down) tex_down = force_malloc(i); else if (count_down < i) { RenderTexture* temp = force_malloc(i); memcpy(temp, tex_down, sizeof(RenderTexture) * count_down); free_def(tex_down); tex_down = temp; } if (!width_down) width_down = force_malloc(i); else if (count_down < i) { int32_t* temp = force_malloc(i); memcpy(temp, width_down, sizeof(int32_t) * count_down); free_def(width_down); width_down = temp; } if (!height_down) height_down = force_malloc(i); else if (count_down < i) { int32_t* temp = force_malloc(i); memcpy(temp, height_down, sizeof(int32_t) * count_down); free_def(height_down); height_down = temp; } count_down = i; i = 0; width = this->width; height = this->height; while (width > 512 && height > 288) { width_down[i] = width /= 2; height_down[i] = height /= 2; i++; } for (i = 0; i < count_down; i++) tex_down[i].Init(width_down[i], height_down[i], 0, GL_RGBA16F, 0); gl_state_bind_framebuffer(0); this->tex_down = tex_down; this->width_down = width_down; this->height_down = height_down; } void post_process_blur::initialize_data(const vec3& radius, const vec3& intensity) { data.radius = vec3::clamp(radius, 1.0f, 3.0f); data.intensity = vec3::clamp(intensity, 0.0f, 2.0f); post_process_blur_radius_calculate(this); } vec3 post_process_blur::get_intensity() { return data.intensity; } void post_process_blur::set_intensity(const vec3& value) { data.intensity = vec3::clamp(value, 0.0f, 2.0f); } vec3 post_process_blur::get_radius() { return data.radius; } void post_process_blur::set_radius(const vec3& value) { vec3 temp = vec3::clamp(value, 1.0f, 3.0f); if (temp != data.radius) { data.radius = temp; post_process_blur_radius_calculate(this); } } static void post_process_blur_radius_calculate(post_process_blur* blur) { float_t radius_scale = 0.8f; vec3 radius = blur->data.radius; post_process_blur_radius_calculate_gaussian_kernel((float_t*)blur->data.gauss, radius.x * radius_scale, 3, 0); post_process_blur_radius_calculate_gaussian_kernel((float_t*)blur->data.gauss, radius.y * radius_scale, 3, 1); post_process_blur_radius_calculate_gaussian_kernel((float_t*)blur->data.gauss, radius.z * radius_scale, 3, 2); } static void post_process_blur_radius_calculate_gaussian_kernel(float_t* gaussian_kernel, float_t radius, int32_t stride, int32_t offset) { if (stride < 1) stride = 1; if (offset < 0) offset = 0; gaussian_kernel[0 * stride + offset] = 1.0f; for (int32_t i = 1; i < POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE; i++) gaussian_kernel[i * stride + offset] = 0.0f; double_t temp_gaussian_kernel[POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE]; double_t s = radius; s = -1.0 / (2.0 * s * s); double_t sum = 0.5; temp_gaussian_kernel[0] = 1.0; for (size_t i = 1; i < POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE; i++) sum += temp_gaussian_kernel[i] = exp(i * i * s); sum = 0.5 / sum; for (size_t i = 0; i < POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE; i++) gaussian_kernel[i * stride + offset] = (float_t)(temp_gaussian_kernel[i] * sum); }