X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=flow.cpp;h=11f4d4c87646ba31d5c9d2c485386f848c6613d1;hb=dac59bd006c3ee244634f1bcdfdd2b1237db99d1;hp=b46e0a10c74cff067b2a5c67675baece2adfe329;hpb=c240834b6e92f10da89c1cf6f4520c2b1b641312;p=nageru diff --git a/flow.cpp b/flow.cpp index b46e0a1..11f4d4c 100644 --- a/flow.cpp +++ b/flow.cpp @@ -33,7 +33,7 @@ constexpr unsigned finest_level = 1; constexpr unsigned patch_size_pixels = 12; // Some global OpenGL objects. -GLuint nearest_sampler, linear_sampler; +GLuint nearest_sampler, linear_sampler, smoothness_sampler; GLuint vertex_vbo; string read_file(const string &filename) @@ -153,10 +153,10 @@ GLuint load_texture(const char *filename, unsigned width, unsigned height) GLuint link_program(GLuint vs_obj, GLuint fs_obj) { - GLuint program = glCreateProgram(); - glAttachShader(program, vs_obj); - glAttachShader(program, fs_obj); - glLinkProgram(program); + GLuint program = glCreateProgram(); + glAttachShader(program, vs_obj); + glAttachShader(program, fs_obj); + glLinkProgram(program); GLint success; glGetProgramiv(program, GL_LINK_STATUS, &success); if (success == GL_FALSE) { @@ -224,7 +224,7 @@ private: GLuint sobel_program; GLuint sobel_vao; - GLuint uniform_tex, uniform_image_size, uniform_inv_image_size; + GLuint uniform_tex, uniform_image_size; }; Sobel::Sobel() @@ -234,15 +234,14 @@ Sobel::Sobel() sobel_program = link_program(sobel_vs_obj, sobel_fs_obj); // Set up the VAO containing all the required position/texcoord data. - glCreateVertexArrays(1, &sobel_vao); - glBindVertexArray(sobel_vao); + glCreateVertexArrays(1, &sobel_vao); + glBindVertexArray(sobel_vao); GLint position_attrib = glGetAttribLocation(sobel_program, "position"); glEnableVertexArrayAttrib(sobel_vao, position_attrib); glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); uniform_tex = glGetUniformLocation(sobel_program, "tex"); - uniform_inv_image_size = glGetUniformLocation(sobel_program, "inv_image_size"); } void Sobel::exec(GLint tex0_view, GLint grad0_tex, int level_width, int level_height) @@ -251,7 +250,6 @@ void Sobel::exec(GLint tex0_view, GLint grad0_tex, int level_width, int level_he glBindTextureUnit(0, tex0_view); glBindSampler(0, nearest_sampler); glProgramUniform1i(sobel_program, uniform_tex, 0); - glProgramUniform2f(sobel_program, uniform_inv_image_size, 1.0f / level_width, 1.0f / level_height); GLuint grad0_fbo; // TODO: cleanup glCreateFramebuffers(1, &grad0_fbo); @@ -259,7 +257,7 @@ void Sobel::exec(GLint tex0_view, GLint grad0_tex, int level_width, int level_he glViewport(0, 0, level_width, level_height); glBindFramebuffer(GL_FRAMEBUFFER, grad0_fbo); - glBindVertexArray(sobel_vao); + glBindVertexArray(sobel_vao); glUseProgram(sobel_program); glDisable(GL_BLEND); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -288,8 +286,8 @@ MotionSearch::MotionSearch() motion_search_program = link_program(motion_vs_obj, motion_fs_obj); // Set up the VAO containing all the required position/texcoord data. - glCreateVertexArrays(1, &motion_search_vao); - glBindVertexArray(motion_search_vao); + glCreateVertexArrays(1, &motion_search_vao); + glBindVertexArray(motion_search_vao); glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo); GLint position_attrib = glGetAttribLocation(motion_search_program, "position"); @@ -322,7 +320,7 @@ void MotionSearch::exec(GLuint tex0_view, GLuint tex1_view, GLuint grad0_tex, GL glViewport(0, 0, width_patches, height_patches); glBindFramebuffer(GL_FRAMEBUFFER, flow_fbo); - glBindVertexArray(motion_search_vao); + glBindVertexArray(motion_search_vao); glUseProgram(motion_search_program); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } @@ -357,8 +355,8 @@ Densify::Densify() densify_program = link_program(densify_vs_obj, densify_fs_obj); // Set up the VAO containing all the required position/texcoord data. - glCreateVertexArrays(1, &densify_vao); - glBindVertexArray(densify_vao); + glCreateVertexArrays(1, &densify_vao); + glBindVertexArray(densify_vao); glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo); GLint position_attrib = glGetAttribLocation(densify_program, "position"); @@ -388,6 +386,8 @@ void Densify::exec(GLuint tex0_view, GLuint tex1_view, GLuint flow_tex, GLuint d float patch_spacing_x = float(level_width - patch_size_pixels) / (width_patches - 1); float patch_spacing_y = float(level_height - patch_size_pixels) / (height_patches - 1); + if (width_patches == 1) patch_spacing_x = 0.0f; // Avoid infinities. + if (height_patches == 1) patch_spacing_y = 0.0f; glProgramUniform2f(densify_program, uniform_patch_spacing, patch_spacing_x / level_width, patch_spacing_y / level_height); @@ -399,11 +399,401 @@ void Densify::exec(GLuint tex0_view, GLuint tex1_view, GLuint flow_tex, GLuint d glViewport(0, 0, level_width, level_height); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); - glBindVertexArray(densify_vao); + glBindVertexArray(densify_vao); glBindFramebuffer(GL_FRAMEBUFFER, dense_flow_fbo); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, width_patches * height_patches); } +// Warp I_1 to I_w, and then compute the mean (I) and difference (I_t) of +// I_0 and I_w. The prewarping is what enables us to solve the variational +// flow for du,dv instead of u,v. +// +// See variational_refinement.txt for more information. +class Prewarp { +public: + Prewarp(); + void exec(GLuint tex0_view, GLuint tex1_view, GLuint flow_tex, GLuint I_tex, GLuint I_t_tex, int level_width, int level_height); + +private: + GLuint prewarp_vs_obj; + GLuint prewarp_fs_obj; + GLuint prewarp_program; + GLuint prewarp_vao; + + GLuint uniform_image0_tex, uniform_image1_tex, uniform_flow_tex; +}; + +Prewarp::Prewarp() +{ + prewarp_vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER); + prewarp_fs_obj = compile_shader(read_file("prewarp.frag"), GL_FRAGMENT_SHADER); + prewarp_program = link_program(prewarp_vs_obj, prewarp_fs_obj); + + // Set up the VAO containing all the required position/texcoord data. + glCreateVertexArrays(1, &prewarp_vao); + glBindVertexArray(prewarp_vao); + glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo); + + GLint position_attrib = glGetAttribLocation(prewarp_program, "position"); + glEnableVertexArrayAttrib(prewarp_vao, position_attrib); + glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); + + 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, 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); + + GLuint prewarp_fbo; // TODO: cleanup + glCreateFramebuffers(1, &prewarp_fbo); + GLenum bufs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glNamedFramebufferDrawBuffers(prewarp_fbo, 2, bufs); + glNamedFramebufferTexture(prewarp_fbo, GL_COLOR_ATTACHMENT0, I_tex, 0); + glNamedFramebufferTexture(prewarp_fbo, GL_COLOR_ATTACHMENT1, I_t_tex, 0); + + glViewport(0, 0, level_width, level_height); + glDisable(GL_BLEND); + glBindVertexArray(prewarp_vao); + glBindFramebuffer(GL_FRAMEBUFFER, prewarp_fbo); + 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: + 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); + + GLuint derivatives_fbo; // TODO: cleanup + glCreateFramebuffers(1, &derivatives_fbo); + GLenum bufs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glNamedFramebufferDrawBuffers(derivatives_fbo, 2, bufs); + glNamedFramebufferTexture(derivatives_fbo, GL_COLOR_ATTACHMENT0, I_x_y_tex, 0); + glNamedFramebufferTexture(derivatives_fbo, GL_COLOR_ATTACHMENT1, beta_0_tex, 0); + + glViewport(0, 0, level_width, level_height); + glDisable(GL_BLEND); + glBindVertexArray(derivatives_vao); + glBindFramebuffer(GL_FRAMEBUFFER, derivatives_fbo); + 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: + GLuint smoothness_vs_obj; + GLuint smoothness_fs_obj; + GLuint smoothness_program; + GLuint smoothness_vao; + + GLuint uniform_flow_tex, uniform_diff_flow_tex; +}; + +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"); +} + +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); + + GLuint smoothness_fbo; // TODO: cleanup + glCreateFramebuffers(1, &smoothness_fbo); + GLenum bufs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glNamedFramebufferDrawBuffers(smoothness_fbo, 2, bufs); + glNamedFramebufferTexture(smoothness_fbo, GL_COLOR_ATTACHMENT0, smoothness_x_tex, 0); + glNamedFramebufferTexture(smoothness_fbo, GL_COLOR_ATTACHMENT1, smoothness_y_tex, 0); + + glViewport(0, 0, level_width, level_height); + + // Make sure the smoothness on the right and upper borders is zero. + // We could have done this by making a (W-1)x(H-1) texture instead + // (we're sampling smoothness with all-zero border color), but we'd + // have to adjust the sampling coordinates, which is annoying. + glScissor(0, 0, level_width - 1, level_height - 1); + glEnable(GL_SCISSOR_TEST); + + glDisable(GL_BLEND); + glBindVertexArray(smoothness_vao); + glBindFramebuffer(GL_FRAMEBUFFER, smoothness_fbo); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisable(GL_SCISSOR_TEST); +} + +// 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: + 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_flow_tex; + GLuint uniform_beta_0_tex; + GLuint uniform_smoothness_x_tex, uniform_smoothness_y_tex; + +}; + +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_flow_tex = glGetUniformLocation(equations_program, "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"); +} + +void SetupEquations::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) +{ + 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_flow_tex, 3, 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, smoothness_sampler); + bind_sampler(equations_program, uniform_smoothness_y_tex, 6, smoothness_y_tex, smoothness_sampler); + + GLuint equations_fbo; // TODO: cleanup + glCreateFramebuffers(1, &equations_fbo); + glNamedFramebufferTexture(equations_fbo, GL_COLOR_ATTACHMENT0, equation_tex, 0); + + glViewport(0, 0, level_width, level_height); + glDisable(GL_BLEND); + glBindVertexArray(equations_vao); + glBindFramebuffer(GL_FRAMEBUFFER, equations_fbo); + 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 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: + 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; +}; + +SOR::SOR() +{ + sor_vs_obj = compile_shader(read_file("vs.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"); +} + +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, smoothness_sampler); + bind_sampler(sor_program, uniform_smoothness_y_tex, 2, smoothness_y_tex, smoothness_sampler); + bind_sampler(sor_program, uniform_equation_tex, 3, equation_tex, nearest_sampler); + + GLuint sor_fbo; // TODO: cleanup + glCreateFramebuffers(1, &sor_fbo); + glNamedFramebufferTexture(sor_fbo, GL_COLOR_ATTACHMENT0, diff_flow_tex, 0); // NOTE: Bind to same as we render from! + + glViewport(0, 0, level_width, level_height); + glDisable(GL_BLEND); + glBindVertexArray(sor_vao); + glBindFramebuffer(GL_FRAMEBUFFER, sor_fbo); + + for (int i = 0; i < num_iterations; ++i) { + 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 diff_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: + GLuint add_flow_vs_obj; + GLuint add_flow_fs_obj; + GLuint add_flow_program; + GLuint add_flow_vao; + + GLuint uniform_base_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_base_flow_tex = glGetUniformLocation(add_flow_program, "base_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_base_flow_tex, 0, base_flow_tex, nearest_sampler); + + GLuint add_flow_fbo; // TODO: cleanup + glCreateFramebuffers(1, &add_flow_fbo); + glNamedFramebufferTexture(add_flow_fbo, GL_COLOR_ATTACHMENT0, diff_flow_tex, 0); + + glViewport(0, 0, level_width, level_height); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glBindVertexArray(add_flow_vao); + glBindFramebuffer(GL_FRAMEBUFFER, add_flow_fbo); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + int main(void) { if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { @@ -444,6 +834,16 @@ int main(void) glSamplerParameteri(linear_sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(linear_sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // The smoothness is sampled so that once we get to a smoothness involving + // a value outside the border, the diffusivity between the two becomes zero. + glCreateSamplers(1, &smoothness_sampler); + glSamplerParameteri(smoothness_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(smoothness_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(smoothness_sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glSamplerParameteri(smoothness_sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + float zero[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glSamplerParameterfv(smoothness_sampler, GL_TEXTURE_BORDER_COLOR, zero); + float vertices[] = { 0.0f, 1.0f, 0.0f, 0.0f, @@ -457,13 +857,19 @@ int main(void) // Initial flow is zero, 1x1. GLuint initial_flow_tex; glCreateTextures(GL_TEXTURE_2D, 1, &initial_flow_tex); - glTextureStorage2D(initial_flow_tex, 1, GL_RGB32F, 1, 1); + glTextureStorage2D(initial_flow_tex, 1, GL_RG16F, 1, 1); GLuint prev_level_flow_tex = initial_flow_tex; Sobel sobel; MotionSearch motion_search; Densify densify; + Prewarp prewarp; + Derivatives derivatives; + ComputeSmoothness compute_smoothness; + SetupEquations setup_equations; + SOR sor; + AddBaseFlow add_base_flow; GLuint query; glGenQueries(1, &query); @@ -516,9 +922,67 @@ int main(void) // And draw. densify.exec(tex0_view, tex1_view, flow_out_tex, dense_flow_tex, level_width, level_height, width_patches, height_patches); - // TODO: Variational refinement. + // Everything below here in the loop belongs to variational refinement. + // It is not done yet. + + // Prewarping; create I and I_t. + GLuint I_tex, I_t_tex; + glCreateTextures(GL_TEXTURE_2D, 1, &I_tex); + glCreateTextures(GL_TEXTURE_2D, 1, &I_t_tex); + glTextureStorage2D(I_tex, 1, GL_R16F, level_width, level_height); + glTextureStorage2D(I_t_tex, 1, GL_R16F, level_width, level_height); + prewarp.exec(tex0_view, tex1_view, dense_flow_tex, I_tex, I_t_tex, level_width, level_height); + + // Calculate I_x and I_y. We're only calculating first derivatives; + // the others will be taken on-the-fly in order to sample from fewer + // textures overall, since sampling from the L1 cache is cheap. + // (TODO: Verify that this is indeed faster than making separate + // double-derivative textures.) + GLuint I_x_y_tex, beta_0_tex; + glCreateTextures(GL_TEXTURE_2D, 1, &I_x_y_tex); + glCreateTextures(GL_TEXTURE_2D, 1, &beta_0_tex); + glTextureStorage2D(I_x_y_tex, 1, GL_RG16F, level_width, level_height); + glTextureStorage2D(beta_0_tex, 1, GL_R16F, level_width, level_height); + derivatives.exec(I_tex, I_x_y_tex, beta_0_tex, level_width, level_height); + + // We need somewhere to store du and dv (the flow increment, relative + // to the non-refined base flow u0 and v0). It starts at zero. + GLuint du_dv_tex; + glCreateTextures(GL_TEXTURE_2D, 1, &du_dv_tex); + glTextureStorage2D(du_dv_tex, 1, GL_RG16F, level_width, level_height); + + // And for smoothness. + GLuint smoothness_x_tex, smoothness_y_tex; + glCreateTextures(GL_TEXTURE_2D, 1, &smoothness_x_tex); + glCreateTextures(GL_TEXTURE_2D, 1, &smoothness_y_tex); + glTextureStorage2D(smoothness_x_tex, 1, GL_R16F, level_width, level_height); + glTextureStorage2D(smoothness_y_tex, 1, GL_R16F, level_width, level_height); + + // And finally for the equation set. See SetupEquations for + // the storage format. + GLuint equation_tex; + glCreateTextures(GL_TEXTURE_2D, 1, &equation_tex); + glTextureStorage2D(equation_tex, 1, GL_RGBA32UI, level_width, level_height); + + for (int outer_idx = 0; outer_idx < level + 1; ++outer_idx) { + // Calculate the smoothness terms between the neighboring pixels, + // both in x and y direction. + compute_smoothness.exec(dense_flow_tex, du_dv_tex, smoothness_x_tex, smoothness_y_tex, level_width, level_height); + + // Set up the 2x2 equation system for each pixel. + setup_equations.exec(I_x_y_tex, I_t_tex, du_dv_tex, dense_flow_tex, beta_0_tex, smoothness_x_tex, smoothness_y_tex, equation_tex, level_width, level_height); + + // Run a few SOR (or quasi-SOR, since we're not really Jacobi) iterations. + // Note that these are to/from the same texture. + sor.exec(du_dv_tex, equation_tex, smoothness_x_tex, smoothness_y_tex, level_width, level_height, 5); + } + + // Add the differential flow found by the variational refinement to the base flow, + // giving the final flow estimate for this level. + // The output is in diff_flow_tex; we don't need to make a new texture. + add_base_flow.exec(dense_flow_tex, du_dv_tex, level_width, level_height); - prev_level_flow_tex = dense_flow_tex; + prev_level_flow_tex = du_dv_tex; } glEndQuery(GL_TIME_ELAPSED);