+ uniform_image0_tex = glGetUniformLocation(prewarp_program, "image0_tex");
+ uniform_image1_tex = glGetUniformLocation(prewarp_program, "image1_tex");
+ uniform_flow_tex = glGetUniformLocation(prewarp_program, "flow_tex");
+}
+
+void Prewarp::exec(GLuint tex0_view, GLuint tex1_view, GLuint flow_tex, GLuint I_tex, GLuint I_t_tex, GLuint normalized_flow_tex, int level_width, int level_height)
+{
+ glUseProgram(prewarp_program);
+
+ bind_sampler(prewarp_program, uniform_image0_tex, 0, tex0_view, nearest_sampler);
+ bind_sampler(prewarp_program, uniform_image1_tex, 1, tex1_view, linear_sampler);
+ bind_sampler(prewarp_program, uniform_flow_tex, 2, flow_tex, nearest_sampler);
+
+ glViewport(0, 0, level_width, level_height);
+ glDisable(GL_BLEND);
+ glBindVertexArray(prewarp_vao);
+ fbos.render_to(I_tex, I_t_tex, normalized_flow_tex);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+// From I, calculate the partial derivatives I_x and I_y. We use a four-tap
+// central difference filter, since apparently, that's tradition (I haven't
+// measured quality versus a more normal 0.5 (I[x+1] - I[x-1]).)
+// The coefficients come from
+//
+// https://en.wikipedia.org/wiki/Finite_difference_coefficient
+//
+// Also computes β_0, since it depends only on I_x and I_y.
+class Derivatives {
+public:
+ Derivatives();
+ void exec(GLuint input_tex, GLuint I_x_y_tex, GLuint beta_0_tex, int level_width, int level_height);
+
+private:
+ PersistentFBOSet<2> fbos;
+
+ GLuint derivatives_vs_obj;
+ GLuint derivatives_fs_obj;
+ GLuint derivatives_program;
+ GLuint derivatives_vao;
+
+ GLuint uniform_tex;
+};
+
+Derivatives::Derivatives()
+{
+ derivatives_vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
+ derivatives_fs_obj = compile_shader(read_file("derivatives.frag"), GL_FRAGMENT_SHADER);
+ derivatives_program = link_program(derivatives_vs_obj, derivatives_fs_obj);
+
+ // Set up the VAO containing all the required position/texcoord data.
+ glCreateVertexArrays(1, &derivatives_vao);
+ glBindVertexArray(derivatives_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo);
+
+ GLint position_attrib = glGetAttribLocation(derivatives_program, "position");
+ glEnableVertexArrayAttrib(derivatives_vao, position_attrib);
+ glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+
+ uniform_tex = glGetUniformLocation(derivatives_program, "tex");
+}
+
+void Derivatives::exec(GLuint input_tex, GLuint I_x_y_tex, GLuint beta_0_tex, int level_width, int level_height)
+{
+ glUseProgram(derivatives_program);
+
+ bind_sampler(derivatives_program, uniform_tex, 0, input_tex, nearest_sampler);
+
+ glViewport(0, 0, level_width, level_height);
+ glDisable(GL_BLEND);
+ glBindVertexArray(derivatives_vao);
+ fbos.render_to(I_x_y_tex, beta_0_tex);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+// Calculate the smoothness constraints between neighboring pixels;
+// s_x(x,y) stores smoothness between pixel (x,y) and (x+1,y),
+// and s_y(x,y) stores between (x,y) and (x,y+1). We'll sample with
+// border color (0,0) later, so that there's zero diffusion out of
+// the border.
+//
+// See variational_refinement.txt for more information.
+class ComputeSmoothness {
+public:
+ ComputeSmoothness();
+ void exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height);
+
+private:
+ PersistentFBOSet<2> fbos;
+
+ GLuint smoothness_vs_obj;
+ GLuint smoothness_fs_obj;
+ GLuint smoothness_program;
+ GLuint smoothness_vao;
+
+ GLuint uniform_flow_tex, uniform_diff_flow_tex;
+ GLuint uniform_alpha;
+};
+
+ComputeSmoothness::ComputeSmoothness()
+{
+ smoothness_vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
+ smoothness_fs_obj = compile_shader(read_file("smoothness.frag"), GL_FRAGMENT_SHADER);
+ smoothness_program = link_program(smoothness_vs_obj, smoothness_fs_obj);
+
+ // Set up the VAO containing all the required position/texcoord data.
+ glCreateVertexArrays(1, &smoothness_vao);
+ glBindVertexArray(smoothness_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo);
+
+ GLint position_attrib = glGetAttribLocation(smoothness_program, "position");
+ glEnableVertexArrayAttrib(smoothness_vao, position_attrib);
+ glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+
+ uniform_flow_tex = glGetUniformLocation(smoothness_program, "flow_tex");
+ uniform_diff_flow_tex = glGetUniformLocation(smoothness_program, "diff_flow_tex");
+ uniform_alpha = glGetUniformLocation(smoothness_program, "alpha");
+}
+
+void ComputeSmoothness::exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height)
+{
+ glUseProgram(smoothness_program);
+
+ bind_sampler(smoothness_program, uniform_flow_tex, 0, flow_tex, nearest_sampler);
+ bind_sampler(smoothness_program, uniform_diff_flow_tex, 1, diff_flow_tex, nearest_sampler);
+ glProgramUniform1f(smoothness_program, uniform_alpha, vr_alpha);
+
+ glViewport(0, 0, level_width, level_height);
+
+ glDisable(GL_BLEND);
+ glBindVertexArray(smoothness_vao);
+ fbos.render_to(smoothness_x_tex, smoothness_y_tex);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Make sure the smoothness on the right and upper borders is zero.
+ // We could have done this by making (W-1)xH and Wx(H-1) textures instead
+ // (we're sampling smoothness with all-zero border color), but we'd
+ // have to adjust the sampling coordinates, which is annoying.
+ glClearTexSubImage(smoothness_x_tex, 0, level_width - 1, 0, 0, 1, level_height, 1, GL_RED, GL_FLOAT, nullptr);
+ glClearTexSubImage(smoothness_y_tex, 0, 0, level_height - 1, 0, level_width, 1, 1, GL_RED, GL_FLOAT, nullptr);
+}
+
+// Set up the equations set (two equations in two unknowns, per pixel).
+// We store five floats; the three non-redundant elements of the 2x2 matrix (A)
+// as 32-bit floats, and the two elements on the right-hand side (b) as 16-bit
+// floats. (Actually, we store the inverse of the diagonal elements, because
+// we only ever need to divide by them.) This fits into four u32 values;
+// R, G, B for the matrix (the last element is symmetric) and A for the two b values.
+// All the values of the energy term (E_I, E_G, E_S), except the smoothness
+// terms that depend on other pixels, are calculated in one pass.
+//
+// See variational_refinement.txt for more information.
+class SetupEquations {
+public:
+ SetupEquations();
+ void exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint flow_tex, GLuint beta_0_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height);
+
+private:
+ PersistentFBOSet<1> fbos;
+
+ GLuint equations_vs_obj;
+ GLuint equations_fs_obj;
+ GLuint equations_program;
+ GLuint equations_vao;
+
+ GLuint uniform_I_x_y_tex, uniform_I_t_tex;
+ GLuint uniform_diff_flow_tex, uniform_base_flow_tex;
+ GLuint uniform_beta_0_tex;
+ GLuint uniform_smoothness_x_tex, uniform_smoothness_y_tex;
+ GLuint uniform_gamma, uniform_delta;
+};
+
+SetupEquations::SetupEquations()
+{
+ equations_vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
+ equations_fs_obj = compile_shader(read_file("equations.frag"), GL_FRAGMENT_SHADER);
+ equations_program = link_program(equations_vs_obj, equations_fs_obj);
+
+ // Set up the VAO containing all the required position/texcoord data.
+ glCreateVertexArrays(1, &equations_vao);
+ glBindVertexArray(equations_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo);
+
+ GLint position_attrib = glGetAttribLocation(equations_program, "position");
+ glEnableVertexArrayAttrib(equations_vao, position_attrib);
+ glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+
+ uniform_I_x_y_tex = glGetUniformLocation(equations_program, "I_x_y_tex");
+ uniform_I_t_tex = glGetUniformLocation(equations_program, "I_t_tex");
+ uniform_diff_flow_tex = glGetUniformLocation(equations_program, "diff_flow_tex");
+ uniform_base_flow_tex = glGetUniformLocation(equations_program, "base_flow_tex");
+ uniform_beta_0_tex = glGetUniformLocation(equations_program, "beta_0_tex");
+ uniform_smoothness_x_tex = glGetUniformLocation(equations_program, "smoothness_x_tex");
+ uniform_smoothness_y_tex = glGetUniformLocation(equations_program, "smoothness_y_tex");
+ uniform_gamma = glGetUniformLocation(equations_program, "gamma");
+ uniform_delta = glGetUniformLocation(equations_program, "delta");
+}
+
+void SetupEquations::exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint base_flow_tex, GLuint beta_0_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height)
+{
+ glUseProgram(equations_program);
+
+ bind_sampler(equations_program, uniform_I_x_y_tex, 0, I_x_y_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_I_t_tex, 1, I_t_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_diff_flow_tex, 2, diff_flow_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_base_flow_tex, 3, base_flow_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_beta_0_tex, 4, beta_0_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_smoothness_x_tex, 5, smoothness_x_tex, zero_border_sampler);
+ bind_sampler(equations_program, uniform_smoothness_y_tex, 6, smoothness_y_tex, zero_border_sampler);
+ glProgramUniform1f(equations_program, uniform_delta, vr_delta);
+ glProgramUniform1f(equations_program, uniform_gamma, vr_gamma);
+
+ glViewport(0, 0, level_width, level_height);
+ glDisable(GL_BLEND);
+ glBindVertexArray(equations_vao);
+ fbos.render_to(equation_tex);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+// Actually solve the equation sets made by SetupEquations, by means of
+// successive over-relaxation (SOR).
+//
+// See variational_refinement.txt for more information.
+class SOR {
+public:
+ SOR();
+ void exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, int num_iterations);
+
+private:
+ PersistentFBOSet<1> fbos;
+
+ GLuint sor_vs_obj;
+ GLuint sor_fs_obj;
+ GLuint sor_program;
+ GLuint sor_vao;
+
+ GLuint uniform_diff_flow_tex;
+ GLuint uniform_equation_tex;
+ GLuint uniform_smoothness_x_tex, uniform_smoothness_y_tex;
+ GLuint uniform_phase;
+};
+
+SOR::SOR()
+{
+ sor_vs_obj = compile_shader(read_file("sor.vert"), GL_VERTEX_SHADER);
+ sor_fs_obj = compile_shader(read_file("sor.frag"), GL_FRAGMENT_SHADER);
+ sor_program = link_program(sor_vs_obj, sor_fs_obj);
+
+ // Set up the VAO containing all the required position/texcoord data.
+ glCreateVertexArrays(1, &sor_vao);
+ glBindVertexArray(sor_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo);
+
+ GLint position_attrib = glGetAttribLocation(sor_program, "position");
+ glEnableVertexArrayAttrib(sor_vao, position_attrib);
+ glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+
+ uniform_diff_flow_tex = glGetUniformLocation(sor_program, "diff_flow_tex");
+ uniform_equation_tex = glGetUniformLocation(sor_program, "equation_tex");
+ uniform_smoothness_x_tex = glGetUniformLocation(sor_program, "smoothness_x_tex");
+ uniform_smoothness_y_tex = glGetUniformLocation(sor_program, "smoothness_y_tex");
+ uniform_phase = glGetUniformLocation(sor_program, "phase");
+}
+
+void SOR::exec(GLuint diff_flow_tex, GLuint equation_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height, int num_iterations)
+{
+ glUseProgram(sor_program);
+
+ bind_sampler(sor_program, uniform_diff_flow_tex, 0, diff_flow_tex, nearest_sampler);
+ bind_sampler(sor_program, uniform_smoothness_x_tex, 1, smoothness_x_tex, zero_border_sampler);
+ bind_sampler(sor_program, uniform_smoothness_y_tex, 2, smoothness_y_tex, zero_border_sampler);
+ bind_sampler(sor_program, uniform_equation_tex, 3, equation_tex, nearest_sampler);
+
+ // NOTE: We bind to the texture we are rendering from, but we never write any value
+ // that we read in the same shader pass (we call discard for red values when we compute
+ // black, and vice versa), and we have barriers between the passes, so we're fine
+ // as per the spec.
+ glViewport(0, 0, level_width, level_height);
+ glDisable(GL_BLEND);
+ glBindVertexArray(sor_vao);
+ fbos.render_to(diff_flow_tex);
+
+ for (int i = 0; i < num_iterations; ++i) {
+ glProgramUniform1i(sor_program, uniform_phase, 0);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ glTextureBarrier();
+ glProgramUniform1i(sor_program, uniform_phase, 1);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ if (i != num_iterations - 1) {
+ glTextureBarrier();
+ }
+ }
+}
+
+// Simply add the differential flow found by the variational refinement to the base flow.
+// The output is in base_flow_tex; we don't need to make a new texture.
+class AddBaseFlow {
+public:
+ AddBaseFlow();
+ void exec(GLuint base_flow_tex, GLuint diff_flow_tex, int level_width, int level_height);
+
+private:
+ PersistentFBOSet<1> fbos;
+
+ GLuint add_flow_vs_obj;
+ GLuint add_flow_fs_obj;
+ GLuint add_flow_program;
+ GLuint add_flow_vao;
+
+ GLuint uniform_diff_flow_tex;
+};
+
+AddBaseFlow::AddBaseFlow()
+{
+ add_flow_vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
+ add_flow_fs_obj = compile_shader(read_file("add_base_flow.frag"), GL_FRAGMENT_SHADER);
+ add_flow_program = link_program(add_flow_vs_obj, add_flow_fs_obj);
+
+ // Set up the VAO containing all the required position/texcoord data.
+ glCreateVertexArrays(1, &add_flow_vao);
+ glBindVertexArray(add_flow_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo);
+
+ GLint position_attrib = glGetAttribLocation(add_flow_program, "position");
+ glEnableVertexArrayAttrib(add_flow_vao, position_attrib);
+ glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+
+ uniform_diff_flow_tex = glGetUniformLocation(add_flow_program, "diff_flow_tex");
+}
+
+void AddBaseFlow::exec(GLuint base_flow_tex, GLuint diff_flow_tex, int level_width, int level_height)
+{
+ glUseProgram(add_flow_program);
+
+ bind_sampler(add_flow_program, uniform_diff_flow_tex, 0, diff_flow_tex, nearest_sampler);