ofxPoissonFill
Poisson filling shader for OpenFrameworks
All Classes Functions
ofxPoissonFill.hpp
1 //
2 // ofxPoissonFill.hpp
3 // Poisson filling shader for OpenFrameworks
4 // Lingdong Huang 2020
5 //
6 // Reference:
7 // Convolution Pyramids, Farbman et al., 2011
8 // (https://www.cse.huji.ac.il/labs/cglab/projects/convpyr/data/convpyr-small.pdf)
9 // Rendu (https://github.com/kosua20/Rendu)
10 //
11 
12 #ifndef poissonfill_h
13 #define poissonfill_h
14 
15 #define PF_MAX_LAYERS 12
16 class PoissonFill{public:
17  ofFbo downs[PF_MAX_LAYERS];
18  ofFbo ups [PF_MAX_LAYERS];
19 
20  ofShader shader;
21  int w;
22  int h;
23  int depth;
24 
30  void init(int _w, int _h, int _depth){
31  w = _w;
32  h = _h;
33  depth = min(PF_MAX_LAYERS,_depth);
34 
35  for (int i = 0; i < depth; i++){
36  _w /= 2;
37  _h /= 2;
38  downs[i].allocate(_w,_h,GL_RGBA);
39  }
40  for (int i = 0; i < depth; i++){
41  _w *= 2;
42  _h *= 2;
43  ups[i].allocate(_w,_h,GL_RGBA);
44  }
45  shader = shader2way();
46  }
47 
54  void init(int _w, int _h){
55  int _depth = log2(min(_w,_h))-1;
56  init(_w,_h,_depth);
57  }
58 
62  void process(ofTexture& tex){
63  int i;
64  pass(downs[0],&tex,NULL);
65  for (i = 1; i < depth; i++){
66  pass(downs[i],&(downs[i-1].getTexture()),NULL);
67  }
68  pass(ups[0],&(downs[depth-2].getTexture()),&(downs[depth-1].getTexture()));
69 
70  for (i = 1; i < depth-1; i++){
71  pass(ups[i],&(downs[depth-i-2].getTexture()),&(ups[i-1].getTexture()));
72  }
73  pass(ups[depth-1],&tex,&(ups[depth-2].getTexture()));
74  }
75 
79  ofTexture& getTexture(){
80  return ups[depth-1].getTexture();
81  }
82 
83  void pass(ofFbo& p, ofTexture* tex1, ofTexture* tex2){
84  p.begin();
85  ofClear(0,0,0,0);
86  shader.begin();
87 
88  // <!> theoratically we can do boundry testing with w/h
89  // but the result looks identical without it,
90  // so for the sake of speed this is commented out
91  // same with the w/h uniforms/if statement in the shader
92  //
93  // shader.setUniform1i("w",tex1->getWidth() );
94  // shader.setUniform1i("h",tex1->getHeight());
95 
96  shader.setUniformTexture("unf",*tex1,1);
97  if (tex2 != NULL){
98  shader.setUniformTexture("fil",*tex2,2);
99  }
100  shader.setUniform1i("isup",tex2 != NULL);
101  ofSetColor(255);
102  ofDrawRectangle(0,0,p.getWidth(),p.getHeight());
103  shader.end();
104  p.end();
105  }
106 
107 
108  ofShader shader2way(){
109  ofShader sh;
110  sh.setupShaderFromSource(GL_FRAGMENT_SHADER, R"(
111  #version 120
112  uniform sampler2DRect unf;
113  uniform sampler2DRect fil;
114  // uniform int w;
115  // uniform int h;
116  uniform bool isup;
117 
118  float h1(int i){
119  if (i == 0 || i == 4){
120  return 0.1507;
121  }
122  if (i == 1 || i == 3){
123  return 0.6836;
124  }
125  return 1.0334;
126  }
127 
128  float G(int i){
129  if (i == 0 || i == 2){
130  return 0.0312;
131  }
132  return 0.7753;
133  }
134 
135  void main(){
136 
137  int i = int(gl_FragCoord.y-0.5);
138  int j = int(gl_FragCoord.x-0.5);
139 
140  if (!isup){
141 
142  int x = j * 2;
143  int y = i * 2;
144 
145  vec4 acc = vec4(0.0,0.0,0.0,0.0);
146  for (int dy = -2; dy <= 2; dy++) {
147  for (int dx = -2; dx <= 2; dx++) {
148  int nx = x + dx;
149  int ny = y + dy;
150 
151  // if (nx < 0 || nx >= w || ny < 0 || ny >= h){
152  // continue;
153  // }
154 
155  vec4 col = texture2DRect(unf, vec2(float(nx)+1.0, float(ny)+1.0));
156 
157  acc.r += h1(dx+2) * h1(dy+2) * col.r;
158  acc.g += h1(dx+2) * h1(dy+2) * col.g;
159  acc.b += h1(dx+2) * h1(dy+2) * col.b;
160  acc.a += h1(dx+2) * h1(dy+2) * col.a;
161  }
162  }
163  if (acc.a == 0.0){
164  gl_FragColor = acc;
165  }else{
166  gl_FragColor = vec4(acc.r/acc.a,acc.g/acc.a,acc.b/acc.a,1.0);
167  }
168 
169  }else{
170  float h2 = 0.0270;
171 
172  vec4 acc = vec4(0.0,0.0,0.0,0.0);
173  for (int dy = -1; dy <= 1; dy++) {
174  for (int dx = -1; dx <= 1; dx++) {
175  int nx = j + dx;
176  int ny = i + dy;
177 
178  // if (nx < 0 || nx >= w || ny < 0 || ny >= h){
179  // continue;
180  // }
181 
182  vec4 col = texture2DRect(unf, vec2(float(nx)+1.0, float(ny)+1.0));
183 
184  acc.r += G(dx+1) * G(dy+1) * col.r;
185  acc.g += G(dx+1) * G(dy+1) * col.g;
186  acc.b += G(dx+1) * G(dy+1) * col.b;
187  acc.a += G(dx+1) * G(dy+1) * col.a;
188  }
189  }
190  for (int dy = -2; dy <= 2; dy++) {
191  for (int dx = -2; dx <= 2; dx++) {
192  int nx = j + dx;
193  int ny = i + dy;
194  nx /= 2;
195  ny /= 2;
196  // if (nx < 0 || nx >= w/2 || ny < 0 || ny >= h/2){
197  // continue;
198  // }
199  vec4 col = texture2DRect(fil, vec2(float(nx), float(ny)));
200 
201  acc.r += h2 * h1(dx+2) * h1(dy+2) * col.r;
202  acc.g += h2 * h1(dx+2) * h1(dy+2) * col.g;
203  acc.b += h2 * h1(dx+2) * h1(dy+2) * col.b;
204  acc.a += h2 * h1(dx+2) * h1(dy+2) * col.a;
205  }
206  }
207  if (acc.a == 0.0){
208  gl_FragColor = acc;
209  }else{
210  gl_FragColor = vec4(acc.r/acc.a,acc.g/acc.a,acc.b/acc.a,1.0);
211  }
212  }
213  }
214  )");
215  sh.linkProgram();
216  return sh;
217  }
218 
219 
220 };
221 
222 #endif /* poissonfill_h */
Definition: ofxPoissonFill.hpp:16
ofTexture & getTexture()
Get the processed output.
Definition: ofxPoissonFill.hpp:79
void init(int _w, int _h, int _depth)
Initialize Poisson filler and allocate necessary datastructures.
Definition: ofxPoissonFill.hpp:30
void process(ofTexture &tex)
Process a texture (RGBA)
Definition: ofxPoissonFill.hpp:62
void init(int _w, int _h)
Initialize Poisson filler and allocate necessary datastructures.
Definition: ofxPoissonFill.hpp:54