gaussian_blur_generator 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4c08daccf74b7ab85e80ee4ade6521684f0ffbe2276b7a8b465578be3aad3703
4
+ data.tar.gz: 783e49eb582aa2bdffc954bda03ffcc4a888e290b14f89614dfdc0b216518d96
5
+ SHA512:
6
+ metadata.gz: a84070cf8fcc524d4da0be924258e278e444280edfb63bf45c9efcfae1efea02d12a5e7302042305ba9368ac95b80b60ab9f0a28f00c8d9592b87a9bd245ad32
7
+ data.tar.gz: ded31a9a5b06cb81647411e31cdbf1a393abddd0b6892466e87c6b982891950595a51c9a095cb94524d46f30f26d78e4c848786fde21120677a96fe8e5a9884a
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ ## Gaussian Blur Generator
2
+
3
+ Generates fragment shaders that apply a Gaussian blur in an efficient manner
4
+ based on
5
+ [this article](http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/).
6
+
7
+ ### Overview
8
+
9
+ Normally, a blur shader would require N^2 texture reads for a kernel of size N.
10
+ By splitting the shader in two (horizontal, then vertical) and applying a linear
11
+ sampling technique, we can reduce the number of texture reads to approximately
12
+ N, vastly improving the performance of a Gaussian blur. This can be reduced even
13
+ further by ignoring values at the edges of kernels that virtually no difference
14
+ to the output image.
15
+
16
+ ### What is this gem?
17
+
18
+ This gem applies the technique from the article but generates many different
19
+ shaders with different kernel sizes. It writes these shaders to files in the
20
+ output directory, for example:
21
+
22
+ - [output/blur/x/17.frag](output/blur/x/17.frag)
23
+ - [output/blue/y/17.frag](output/blue/y/17.frag)
24
+
25
+ You can run `./bin/generate` yourself by cloning the repository or use one of
26
+ the pre-generated shaders in the [`output/blur`](output/blur) directory. These
27
+ have generated with an epsilon value of 0.05 which roughly corresponds to the
28
+ article, but you can set this yourself if you'd like:
29
+
30
+ ```sh
31
+ $ ./bin/generate 0.07
32
+ ```
33
+
34
+ This would set the threshold a little higher than the default 0.05.
35
+
36
+ ### How do I use it?
37
+
38
+ 1. Copy the x and y fragment shaders into your project for the kernel size you want.
39
+ 2. Compile a shader program for each of the fragment shaders
40
+ 3. Set `u_texture` to the input texture you want to blur
41
+ 4. Set `u_dimensions` to the pixel size of the output: `[width, height, 1/width, 1/height]`
42
+ 5. Draw your scene to a framebuffer/texture instead of directly to the screen
43
+ 6. Apply the first shader program, then draw to the screen with the second
44
+
45
+ You can apply the shader to regions of the texture if you'd wish, but the
46
+ simplest approach is to apply it to a full-screen quad. In which case, a simple
47
+ vertex shader like this one will suffice:
48
+
49
+ ```glsl
50
+ attribute vec4 a_position;
51
+
52
+ void main() {
53
+ gl_Position = a_position;
54
+ }
55
+ ```
56
+
57
+ You may need to edit the [`glsl.rb` file](lib/gaussian_blur_generator/glsl.rb)
58
+ to produce shader code that's more appropriate for your project.
59
+
60
+ ### Multiple passes?
61
+
62
+ You may find it's more efficient to apply multiple passes of a smaller blur
63
+ shader than a single pass of a larger one. This depends on a number of factors
64
+ including GPU memory, fill rate, etc. In general, two blurs of size N is
65
+ equivalent to a single blur of size `sqrt(2) * N`.
66
+
67
+ Also, since Gaussian blur is a low-pass filter, it works very well with
68
+ downsampling. See the article above for more information.
69
+
70
+ ### License
71
+
72
+ MIT
data/bin/generate ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ LIB = File.expand_path(File.join(__dir__, "..", "lib"))
4
+ $LOAD_PATH.unshift(LIB)
5
+
6
+ EPSILON = Float(ARGV[0] || 0.05)
7
+
8
+ require "gaussian_blur_generator"
9
+ require "fileutils"
10
+
11
+ FileUtils.mkdir_p("output/blur/x")
12
+ FileUtils.mkdir_p("output/blur/y")
13
+
14
+ GaussianBlurGenerator.generate(EPSILON) do |values|
15
+ ["x", "y"].each do |axis|
16
+ glsl = GaussianBlurGenerator.glsl(values, axis)
17
+ path = "output/blur/#{axis}/#{values.texture_reads}.frag"
18
+
19
+ File.write(path, glsl)
20
+ end
21
+
22
+ puts "Written output/blur/{x,y}/#{values.texture_reads}.frag"
23
+ puts " discarded values < #{EPSILON} of peak"
24
+ puts " number of texture reads = #{values.texture_reads}"
25
+ puts " effective kernel size = #{values.effective_size}"
26
+ puts " pascal's triangle row = #{values.row_number}"
27
+ puts
28
+ end
@@ -0,0 +1,4 @@
1
+ require "bigdecimal/util"
2
+
3
+ require "gaussian_blur_generator/base"
4
+ require "gaussian_blur_generator/glsl"
@@ -0,0 +1,51 @@
1
+ class GaussianBlurGenerator
2
+ Values = Struct.new(:offsets_d, :weights_d, :offsets_l, :weights_l, :texture_reads, :effective_size, :row_number)
3
+
4
+ def self.generate(epsilon)
5
+ row_number = 0
6
+ last_size = 0
7
+
8
+ loop do
9
+ row_number += 2
10
+
11
+ row = pascal(row_number)
12
+ sum = row.sum
13
+ peak = row[row.size / 2].to_d / sum
14
+
15
+ # Remove weights that make negligible difference to the output image.
16
+ row = row.reject { |n| n.to_d / sum < peak * epsilon }
17
+ sum = row.sum
18
+
19
+ # If we've already seen a row of this size, skip those that are more truncated.
20
+ next if row.size == last_size
21
+ last_size = row.size
22
+
23
+ midpoint = row.size / 2
24
+ next if midpoint.odd?
25
+
26
+ weights_d = row.map { |n| n.to_d / sum }[midpoint..]
27
+ offsets_d = (0..midpoint).map(&:to_d)
28
+
29
+ break if weights_d[0] == 0 # Precision limit reached
30
+
31
+ weights_l = weights_d[1..].each_slice(2).map(&:sum)
32
+ offsets_l = offsets_d[1..].each_slice(2).map do |t1, t2|
33
+ numerator = offsets_d[t1] * weights_d[t1] + offsets_d[t2] * weights_d[t2]
34
+ denominator = weights_d[t1] + weights_d[t2]
35
+
36
+ numerator / denominator
37
+ end
38
+
39
+ weights_l.unshift(weights_d.first)
40
+ offsets_l.unshift(0.0)
41
+ texture_reads = offsets_l.size * 2 - 1
42
+
43
+ yield Values.new(offsets_d, weights_d, offsets_l, weights_l, texture_reads, row.size, row_number)
44
+ end
45
+ end
46
+
47
+ def self.pascal(n)
48
+ @cache ||= {}
49
+ @cache[n] ||= (n == 0 ? [1] : [1, *pascal(n - 1).each_cons(2).map(&:sum), 1])
50
+ end
51
+ end
@@ -0,0 +1,47 @@
1
+ class GaussianBlurGenerator
2
+ def self.glsl(values, axis)
3
+ offsets = values.offsets_l[1..].map.with_index do |offset, index|
4
+ "float offset#{index + 1} = one_pixel * #{offset.to_f};"
5
+ end
6
+
7
+ if axis == "x"
8
+ template = "coords.x + offset@, coords.y"
9
+ else
10
+ template = "coords.x, coords.y + offset@"
11
+ end
12
+
13
+ positives = values.weights_l[1..].map.with_index do |weight, index|
14
+ "#{weight.to_f} * texture2D(u_texture, vec2(#{template.sub("@", (index + 1).to_s)}))"
15
+ end
16
+
17
+ negatives = positives.map { |s| s.sub("+", "-") }
18
+
19
+ summation = negatives.reverse + [
20
+ "#{values.weights_l[0].to_f} * texture2D(u_texture, coords)"
21
+ ] + positives
22
+
23
+ <<~GLSL
24
+ #ifdef GL_FRAGMENT_PRECISION_HIGH
25
+ precision highp float;
26
+ #else
27
+ precision mediump float;
28
+ #endif
29
+
30
+ uniform sampler2D u_texture;
31
+ uniform vec4 u_dimensions;
32
+
33
+ // Generated by https://github.com/tuzz/gaussian_blur_generator
34
+
35
+ void main() {
36
+ vec2 coords = gl_FragCoord.xy / u_dimensions.xy;
37
+
38
+ float one_pixel = u_dimensions.#{axis == "x" ? "z" : "w"};
39
+ #{offsets.join("\n ")}
40
+
41
+ gl_FragColor = (
42
+ #{summation.join(" +\n ")}
43
+ );
44
+ }
45
+ GLSL
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gaussian_blur_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Patuzzo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Generates fragment shaders that apply a Gaussian blur in an efficient
14
+ manner.
15
+ email: chris@patuzzo.co.uk
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - bin/generate
22
+ - lib/gaussian_blur_generator.rb
23
+ - lib/gaussian_blur_generator/base.rb
24
+ - lib/gaussian_blur_generator/glsl.rb
25
+ homepage: https://github.com/tuzz/gaussian_blur_generator
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubygems_version: 3.1.2
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Gaussian Blur Generator
48
+ test_files: []