From fae3ab7f41fee0345a399b599268e9a0573996be Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 19 Dec 2022 21:47:05 -0800 Subject: [PATCH] [Impeller] RRect blur improvements --- .../shader_lib/impeller/constants.glsl | 3 +++ .../shader_lib/impeller/gaussian.glsl | 20 ++++++++++--------- .../entity/contents/rrect_shadow_contents.cc | 4 ++-- impeller/entity/shaders/rrect_blur.frag | 6 +++--- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/impeller/compiler/shader_lib/impeller/constants.glsl b/impeller/compiler/shader_lib/impeller/constants.glsl index fe9367a926eae..423f4dbd735cd 100644 --- a/impeller/compiler/shader_lib/impeller/constants.glsl +++ b/impeller/compiler/shader_lib/impeller/constants.glsl @@ -16,4 +16,7 @@ const float kSqrtTwoPi = 2.50662827463; // sqrt(2) / 2 == 1 / sqrt(2) const float kHalfSqrtTwo = 0.70710678118; +// sqrt(3) +const float kSqrtThree = 1.73205080757; + #endif diff --git a/impeller/compiler/shader_lib/impeller/gaussian.glsl b/impeller/compiler/shader_lib/impeller/gaussian.glsl index 7b8b4e438dea9..62874bec96d9c 100644 --- a/impeller/compiler/shader_lib/impeller/gaussian.glsl +++ b/impeller/compiler/shader_lib/impeller/gaussian.glsl @@ -29,21 +29,23 @@ vec2 IPVec2Erf(vec2 x) { return sign(x) * (1 - 1 / (b * b * b * b)); } -/// Indefinite integral of the Gaussian function (with constant range 0->1). +/// The indefinite integral of the Gaussian function. +/// Uses a very close approximation of Erf. float IPGaussianIntegral(float x, float sigma) { // ( 1 + erf( x * (sqrt(2) / (2 * sigma) ) ) / 2 - // Because this sigmoid is always > 1, we remap it (n * 1.07 - 0.07) - // so that it always fades to zero before it reaches the blur radius. - return 0.535 * IPErf(x * (kHalfSqrtTwo / sigma)) + 0.465; + return (1 + IPErf(x * (kHalfSqrtTwo / sigma))) * 0.5; } -/// Vec2 variation for the indefinite integral of the Gaussian function (with -/// constant range 0->1). +/// Vec2 variation for the indefinite integral of the Gaussian function. +/// Uses a very close approximation of Erf. vec2 IPVec2GaussianIntegral(vec2 x, float sigma) { // ( 1 + erf( x * (sqrt(2) / (2 * sigma) ) ) / 2 - // Because this sigmoid is always > 1, we remap it (n * 1.07 - 0.07) - // so that it always fades to zero before it reaches the blur radius. - return 0.535 * IPVec2Erf(x * (kHalfSqrtTwo / sigma)) + 0.465; + return (1 + IPVec2Erf(x * (kHalfSqrtTwo / sigma))) * 0.5; +} + +/// Simpler (but less accurate) approximation of the Gaussian integral. +vec2 IPVec2FastGaussianIntegral(vec2 x, float sigma) { + return 1 / (1 + exp(-kSqrtThree / sigma * x)); } /// Simple logistic sigmoid with a domain of [-1, 1] and range of [0, 1]. diff --git a/impeller/entity/contents/rrect_shadow_contents.cc b/impeller/entity/contents/rrect_shadow_contents.cc index b1c65b9e58755..33c785a6b6bd7 100644 --- a/impeller/entity/contents/rrect_shadow_contents.cc +++ b/impeller/entity/contents/rrect_shadow_contents.cc @@ -39,7 +39,7 @@ std::optional RRectShadowContents::GetCoverage( return std::nullopt; } - Scalar radius = Radius{sigma_}.radius; + Scalar radius = sigma_.sigma * 2; auto ltrb = rect_->GetLTRB(); Rect bounds = Rect::MakeLTRB(ltrb[0] - radius, ltrb[1] - radius, @@ -59,7 +59,7 @@ bool RRectShadowContents::Render(const ContentContext& renderer, VertexBufferBuilder vtx_builder; - auto blur_radius = Radius{sigma_}.radius; + auto blur_radius = sigma_.sigma * 2; auto positive_rect = rect_->GetPositive(); { auto left = -blur_radius; diff --git a/impeller/entity/shaders/rrect_blur.frag b/impeller/entity/shaders/rrect_blur.frag index 7a82a57805e63..5b0ddff80976c 100644 --- a/impeller/entity/shaders/rrect_blur.frag +++ b/impeller/entity/shaders/rrect_blur.frag @@ -17,7 +17,7 @@ in vec2 v_position; out vec4 frag_color; -const int kSampleCount = 5; +const int kSampleCount = 4; float RRectDistance(vec2 sample_position, vec2 half_size) { vec2 space = abs(sample_position) - half_size + frag_info.corner_radius; @@ -37,8 +37,8 @@ float RRectShadowX(vec2 sample_position, vec2 half_size) { sqrt(max(0, frag_info.corner_radius * frag_info.corner_radius - space * space)); - // Map the linear distance field to the analytical Gaussian integral. - vec2 integral = IPVec2GaussianIntegral( + // Map the linear distance field to the approximate Gaussian integral. + vec2 integral = IPVec2FastGaussianIntegral( sample_position.x + vec2(-rrect_distance, rrect_distance), frag_info.blur_sigma); return integral.y - integral.x;