22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
44
5+ #include < impeller/ gaussian.glsl>
6+
57uniform FragInfo {
68 vec4 color;
7- float blur_radius ;
9+ float blur_sigma ;
810 vec2 rect_size;
911 float corner_radius;
1012}
@@ -14,21 +16,63 @@ in vec2 v_position;
1416
1517out vec4 frag_color;
1618
17- // Simple logistic sigmoid with a domain of [-1, 1] and range of [0, 1].
18- float Sigmoid(float x) {
19- return 1.03731472073 / (1 + exp (- 4 * x)) - 0.0186573603638 ;
20- }
19+ const int kSampleCount = 5 ;
2120
22- float RRectDistance(vec2 sample_position, vec2 rect_size, float corner_radius ) {
23- vec2 space = abs (sample_position) - rect_size + corner_radius;
21+ float RRectDistance(vec2 sample_position, vec2 half_size ) {
22+ vec2 space = abs (sample_position) - half_size + frag_info. corner_radius;
2423 return length (max (space, 0.0 )) + min (max (space.x, space.y), 0.0 ) -
25- corner_radius;
24+ frag_info.corner_radius;
25+ }
26+
27+ // / Closed form unidirectional rounded rect blur mask solution using the
28+ // / analytical Gaussian integral (with approximated erf).
29+ float RRectShadowX(vec2 sample_position, vec2 half_size) {
30+ // Compute the X direction distance field (not incorporating the Y distance)
31+ // for the rounded rect.
32+ float space =
33+ min (0 , half_size.y - frag_info.corner_radius - abs (sample_position.y));
34+ float rrect_distance =
35+ half_size.x - frag_info.corner_radius +
36+ sqrt (max (0 , frag_info.corner_radius * frag_info.corner_radius -
37+ space * space));
38+
39+ // Map the linear distance field to the analytical Gaussian integral.
40+ vec2 integral = IPVec2GaussianIntegral(
41+ sample_position.x + vec2 (- rrect_distance, rrect_distance),
42+ frag_info.blur_sigma);
43+ return integral.y - integral.x;
44+ }
45+
46+ float RRectShadow(vec2 sample_position, vec2 half_size) {
47+ // Limit the sampling range to 3 standard deviations in the Y direction from
48+ // the kernel center to incorporate 99.7% of the color contribution.
49+ float half_sampling_range = frag_info.blur_sigma * 3 ;
50+
51+ float begin_y = max (- half_sampling_range, sample_position.y - half_size.y);
52+ float end_y = min (half_sampling_range, sample_position.y + half_size.y);
53+ float interval = (end_y - begin_y) / kSampleCount;
54+
55+ // Sample the X blur kSampleCount times, weighted by the Gaussian function.
56+ float result = 0 ;
57+ for (int sample_i = 0 ; sample_i < kSampleCount; sample_i++ ) {
58+ float y = begin_y + interval * (sample_i + 0.5 );
59+ result += RRectShadowX(vec2 (sample_position.x, sample_position.y - y),
60+ half_size) *
61+ IPGaussian(y, frag_info.blur_sigma) * interval;
62+ }
63+
64+ return result;
2665}
2766
2867void main() {
29- vec2 center = v_position - frag_info.rect_size / 2.0 ;
30- float dist =
31- RRectDistance(center, frag_info.rect_size / 2.0 , frag_info.corner_radius);
32- float shadow_mask = Sigmoid(- dist / frag_info.blur_radius);
33- frag_color = frag_info.color * shadow_mask;
68+ frag_color = frag_info.color;
69+
70+ vec2 half_size = frag_info.rect_size * 0.5 ;
71+ vec2 sample_position = v_position - half_size;
72+
73+ if (frag_info.blur_sigma > 0 ) {
74+ frag_color *= RRectShadow(sample_position, half_size);
75+ } else {
76+ frag_color *= - RRectDistance(sample_position, half_size);
77+ }
3478}
0 commit comments