@@ -9,3 +9,114 @@ flat varying vec4 vUvRect;
9
9
flat varying vec2 vOffsetScale;
10
10
flat varying float vSigma;
11
11
flat varying int vBlurRadius;
12
+
13
+ #ifdef WR_VERTEX_SHADER
14
+ // Applies a separable gaussian blur in one direction, as specified
15
+ // by the dir field in the blur command.
16
+
17
+ #define DIR_HORIZONTAL 0
18
+ #define DIR_VERTICAL 1
19
+
20
+ in int aBlurRenderTaskIndex;
21
+ in int aBlurSourceTaskIndex;
22
+ in int aBlurDirection;
23
+
24
+ struct BlurCommand {
25
+ int task_id;
26
+ int src_task_id;
27
+ int dir;
28
+ };
29
+
30
+ BlurCommand fetch_blur() {
31
+ BlurCommand blur;
32
+
33
+ blur.task_id = aBlurRenderTaskIndex;
34
+ blur.src_task_id = aBlurSourceTaskIndex;
35
+ blur.dir = aBlurDirection;
36
+
37
+ return blur;
38
+ }
39
+
40
+ void main(void ) {
41
+ BlurCommand cmd = fetch_blur();
42
+ RenderTaskData task = fetch_render_task(cmd.task_id);
43
+ RenderTaskData src_task = fetch_render_task(cmd.src_task_id);
44
+
45
+ vec4 local_rect = task.data0;
46
+
47
+ vec2 pos = mix (local_rect.xy,
48
+ local_rect.xy + local_rect.zw,
49
+ aPosition.xy);
50
+
51
+ vec2 texture_size = vec2 (textureSize(sCacheRGBA8, 0 ).xy);
52
+ vUv.z = src_task.data1.x;
53
+ vBlurRadius = int (task.data1.y);
54
+ vSigma = task.data1.y * 0.5 ;
55
+
56
+ switch (cmd.dir) {
57
+ case DIR_HORIZONTAL:
58
+ vOffsetScale = vec2 (1.0 / texture_size.x, 0.0 );
59
+ break ;
60
+ case DIR_VERTICAL:
61
+ vOffsetScale = vec2 (0.0 , 1.0 / texture_size.y);
62
+ break ;
63
+ }
64
+
65
+ vUvRect = vec4 (src_task.data0.xy + vec2 (0.5 ),
66
+ src_task.data0.xy + src_task.data0.zw - vec2 (0.5 ));
67
+ vUvRect /= texture_size.xyxy;
68
+
69
+ vec2 uv0 = src_task.data0.xy / texture_size;
70
+ vec2 uv1 = (src_task.data0.xy + src_task.data0.zw) / texture_size;
71
+ vUv.xy = mix (uv0, uv1, aPosition.xy);
72
+
73
+ gl_Position = uTransform * vec4 (pos, 0.0 , 1.0 );
74
+ }
75
+ #endif
76
+
77
+ #ifdef WR_FRAGMENT_SHADER
78
+ // TODO(gw): Write a fast path blur that handles smaller blur radii
79
+ // with a offset / weight uniform table and a constant
80
+ // loop iteration count!
81
+
82
+ // TODO(gw): Make use of the bilinear sampling trick to reduce
83
+ // the number of texture fetches needed for a gaussian blur.
84
+
85
+ float gauss(float x, float sigma) {
86
+ return (1.0 / sqrt (6.283185307179586 * sigma * sigma)) * exp (- (x * x) / (2.0 * sigma * sigma));
87
+ }
88
+
89
+ void main(void ) {
90
+ vec4 cache_sample = texture(sCacheRGBA8, vUv);
91
+
92
+ // TODO(gw): The gauss function gets NaNs when blur radius
93
+ // is zero. In the future, detect this earlier
94
+ // and skip the blur passes completely.
95
+ if (vBlurRadius == 0 ) {
96
+ oFragColor = cache_sample;
97
+ return ;
98
+ }
99
+
100
+ vec4 color = vec4 (cache_sample.rgb, 1.0 ) * (cache_sample.a * gauss(0.0 , vSigma));
101
+
102
+ for (int i= 1 ; i < vBlurRadius ; ++ i) {
103
+ vec2 offset = vec2 (float (i)) * vOffsetScale;
104
+
105
+ vec2 st0 = clamp (vUv.xy + offset, vUvRect.xy, vUvRect.zw);
106
+ vec4 color0 = texture(sCacheRGBA8, vec3 (st0, vUv.z));
107
+
108
+ vec2 st1 = clamp (vUv.xy - offset, vUvRect.xy, vUvRect.zw);
109
+ vec4 color1 = texture(sCacheRGBA8, vec3 (st1, vUv.z));
110
+
111
+ // Alpha must be premultiplied in order to properly blur the alpha channel.
112
+ float weight = gauss(float (i), vSigma);
113
+ color += vec4 (color0.rgb * color0.a, color0.a) * weight;
114
+ color += vec4 (color1.rgb * color1.a, color1.a) * weight;
115
+ }
116
+
117
+ // Unpremultiply the alpha.
118
+ color.rgb /= color.a;
119
+
120
+ oFragColor = dither(color);
121
+ }
122
+ #endif
0 commit comments