gaussian_blur_generator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +72 -0
- data/bin/generate +28 -0
- data/lib/gaussian_blur_generator.rb +4 -0
- data/lib/gaussian_blur_generator/base.rb +51 -0
- data/lib/gaussian_blur_generator/glsl.rb +47 -0
- metadata +48 -0
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,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: []
|