ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 5ecd2554b77675bad84f81a6c9fb7e60907764bb
Parent: 5d94a74e8838d1ad6445ef4a6c9362c99a875010
Author: Randy Palamar
Date:   Sat,  5 Jul 2025 17:38:15 -0600

ui: remove render_2d.frag

render_3d.frag can do the same thing but also 3D

Diffstat:
Mbeamformer.c | 2+-
Mbeamformer.h | 4+---
Mbeamformer_parameters.h | 3+--
Mmath.c | 42++++++++++++++++++++++++++++++++++++------
Dshaders/render_2d.frag.glsl | 34----------------------------------
Mstatic.c | 57+--------------------------------------------------------
Mui.c | 73++++++++++++++++++++++++++++++++++++++++++++-----------------------------
7 files changed, 84 insertions(+), 131 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -486,7 +486,7 @@ DEBUG_EXPORT BEAMFORMER_RELOAD_SHADER_FN(beamformer_reload_shader) if (new_program) { glDeleteProgram(*src->shader); *src->shader = new_program; - if (src->kind == BeamformerShaderKind_Render2D) ctx->frame_view_render_context.updated = 1; + if (src->kind == BeamformerShaderKind_Render3D) ctx->frame_view_render_context.updated = 1; } return new_program != 0; } diff --git a/beamformer.h b/beamformer.h @@ -75,11 +75,9 @@ typedef struct { #define FRAME_VIEW_RENDER_TARGET_SIZE 1024, 1024 typedef struct { - u32 shaders[2]; /* [0] -> 2D render, [1] -> 3D render */ + u32 shader; u32 framebuffers[2]; /* [0] -> multisample target, [1] -> normal target for resolving */ u32 renderbuffers[2]; /* only used for 3D views, size is fixed */ - u32 vao; - u32 vbo; b32 updated; } FrameViewRenderContext; diff --git a/beamformer_parameters.h b/beamformer_parameters.h @@ -23,11 +23,10 @@ typedef enum { #define X(e, n, s, h, pn) BeamformerShaderKind_##e = n, COMPUTE_SHADERS #undef X - BeamformerShaderKind_Render2D, BeamformerShaderKind_Render3D, BeamformerShaderKind_Count, - BeamformerShaderKind_ComputeCount = BeamformerShaderKind_Render2D, + BeamformerShaderKind_ComputeCount = BeamformerShaderKind_Render3D, } BeamformerShaderKind; typedef struct { diff --git a/math.c b/math.c @@ -336,6 +336,17 @@ v4_lerp(v4 a, v4 b, f32 t) return result; } +function m4 +m4_identity(void) +{ + m4 result; + result.c[0] = (v4){{1, 0, 0, 0}}; + result.c[1] = (v4){{0, 1, 0, 0}}; + result.c[2] = (v4){{0, 0, 1, 0}}; + result.c[3] = (v4){{0, 0, 0, 1}}; + return result; +} + function v4 m4_row(m4 a, u32 row) { @@ -412,6 +423,17 @@ m4_translation(v3 delta) } function m4 +m4_scale(v3 scale) +{ + m4 result; + result.c[0] = (v4){{scale.x, 0, 0, 0}}; + result.c[1] = (v4){{0, scale.y, 0, 0}}; + result.c[2] = (v4){{0, 0, scale.z, 0}}; + result.c[3] = (v4){{0, 0, 0, 1}}; + return result; +} + +function m4 m4_rotation_about_y(f32 turns) { f32 sa = sin_f32(turns * 2 * PI); @@ -427,12 +449,7 @@ m4_rotation_about_y(f32 turns) function m4 y_aligned_volume_transform(v3 extent, v3 translation, f32 rotation_turns) { - m4 S; - S.c[0] = (v4){{extent.x, 0, 0, 0}}; - S.c[1] = (v4){{0, extent.y, 0, 0}}; - S.c[2] = (v4){{0, 0, extent.z, 0}}; - S.c[3] = (v4){{0, 0, 0, 1}}; - + m4 S = m4_scale(extent); m4 R = m4_rotation_about_y(rotation_turns); m4 T = m4_translation(translation); m4 result = m4_mul(m4_mul(R, S), T); @@ -451,6 +468,19 @@ m4_mul_v4(m4 a, v4 v) } function m4 +orthographic_projection(f32 n, f32 f, f32 t, f32 r) +{ + m4 result; + f32 a = -2 / (f - n); + f32 b = - (f + n) / (f - n); + result.c[0] = (v4){{1 / r, 0, 0, 0}}; + result.c[1] = (v4){{0, 1 / t, 0, 0}}; + result.c[2] = (v4){{0, 0, a, 0}}; + result.c[3] = (v4){{0, 0, b, 1}}; + return result; +} + +function m4 perspective_projection(f32 n, f32 f, f32 fov, f32 aspect) { m4 result; diff --git a/shaders/render_2d.frag.glsl b/shaders/render_2d.frag.glsl @@ -1,34 +0,0 @@ -/* See LICENSE for license details. */ - -/* input: h [0,360] | s,v [0, 1] * - * output: rgb [0,1] */ -vec3 hsv2rgb(vec3 hsv) -{ - vec3 k = mod(vec3(5, 3, 1) + hsv.x / 60, 6); - k = max(min(min(k, 4 - k), 1), 0); - return hsv.z - hsv.z * hsv.y * k; -} - -void main() -{ - ivec3 out_data_dim = textureSize(u_texture, 0); - - //vec2 min_max = texelFetch(u_out_data_tex, ivec3(0), textureQueryLevels(u_out_data_tex) - 1).xy; - - /* TODO(rnp): select between x and y and specify slice */ - vec3 tex_coord = vec3(texture_coordinate.x, 0.5, texture_coordinate.y); - float smp = length(texture(u_texture, tex_coord).xy); - float threshold_val = pow(10.0f, u_threshold / 20.0f); - smp = clamp(smp, 0.0f, threshold_val); - smp = smp / threshold_val; - smp = pow(smp, u_gamma); - - if (u_log_scale) { - smp = 20 * log(smp) / log(10); - smp = clamp(smp, -u_db_cutoff, 0) / -u_db_cutoff; - smp = 1 - smp; - } - - //v_out_colour = vec4(hsv2rgb(vec3(360 * smp, 0.8, 0.95)), 1); - v_out_colour = vec4(smp, smp, smp, 1); -} diff --git a/static.c b/static.c @@ -392,68 +392,13 @@ setup_beamformer(BeamformerCtx *ctx, BeamformerInput *input, Arena *memory) glNamedRenderbufferStorageMultisample(fvr->renderbuffers[1], msaa_samples, GL_DEPTH_COMPONENT24, FRAME_VIEW_RENDER_TARGET_SIZE); - f32 vertices[] = { - -1, 1, 0, 0, - -1, -1, 0, 1, - 1, -1, 1, 1, - -1, 1, 0, 0, - 1, -1, 1, 1, - 1, 1, 1, 0, - }; - glCreateVertexArrays(1, &fvr->vao); - glCreateBuffers(1, &fvr->vbo); - - glNamedBufferData(fvr->vbo, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glEnableVertexArrayAttrib(fvr->vao, 0); - glEnableVertexArrayAttrib(fvr->vao, 1); - glVertexArrayVertexBuffer(fvr->vao, 0, fvr->vbo, 0, 4 * sizeof(f32)); - glVertexArrayVertexBuffer(fvr->vao, 1, fvr->vbo, 2 * sizeof(f32), 4 * sizeof(f32)); - glVertexArrayAttribFormat(fvr->vao, 0, 2, GL_FLOAT, 0, 0); - glVertexArrayAttribFormat(fvr->vao, 1, 2, GL_FLOAT, 0, 2 * sizeof(f32)); - glVertexArrayAttribBinding(fvr->vao, 0, 0); - glVertexArrayAttribBinding(fvr->vao, 1, 0); - - ShaderReloadContext *render_2d = push_struct(memory, typeof(*render_2d)); - render_2d->beamformer_context = ctx; - render_2d->path = s8(static_path_join("shaders", "render_2d.frag.glsl")); - render_2d->name = s8("shaders/render_2d.glsl"); - render_2d->gl_type = GL_FRAGMENT_SHADER; - render_2d->kind = BeamformerShaderKind_Render2D; - render_2d->shader = fvr->shaders + 0; - render_2d->header = s8("" - "layout(location = 0) in vec2 texture_coordinate;\n" - "layout(location = 0) out vec4 v_out_colour;\n\n" - "layout(location = " str(FRAME_VIEW_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" - "layout(location = " str(FRAME_VIEW_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" - "layout(location = " str(FRAME_VIEW_GAMMA_LOC) ") uniform float u_gamma = 1;\n" - "layout(location = " str(FRAME_VIEW_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" - "\n" - "layout(binding = 0) uniform sampler3D u_texture;\n"); - render_2d->link = push_struct(memory, typeof(*render_2d)); - render_2d->link->gl_type = GL_VERTEX_SHADER; - render_2d->link->link = render_2d; - render_2d->link->header = s8("" - "layout(location = 0) in vec2 v_position;\n" - "layout(location = 1) in vec2 v_texture_coordinate;\n" - "\n" - "layout(location = 0) out vec2 f_texture_coordinate;\n" - "\n" - "void main()\n" - "{\n" - "\tf_texture_coordinate = v_texture_coordinate;\n" - "\tgl_Position = vec4(v_position, 0, 1);\n" - "}\n"); - reload_shader(&ctx->os, render_2d->path, (iptr)render_2d, *memory); - os_add_file_watch(&ctx->os, memory, render_2d->path, reload_shader, (iptr)render_2d); - ShaderReloadContext *render_3d = push_struct(memory, typeof(*render_3d)); render_3d->beamformer_context = ctx; render_3d->path = s8(static_path_join("shaders", "render_3d.frag.glsl")); render_3d->name = s8("shaders/render_3d.glsl"); render_3d->gl_type = GL_FRAGMENT_SHADER; render_3d->kind = BeamformerShaderKind_Render3D; - render_3d->shader = fvr->shaders + 1; + render_3d->shader = &fvr->shader; render_3d->header = s8("" "layout(location = 0) in vec3 normal;\n" "layout(location = 1) in vec3 texture_coordinate;\n\n" diff --git a/ui.c b/ui.c @@ -1,7 +1,6 @@ /* See LICENSE for license details. */ /* TODO(rnp): * [ ]: live parameters control panel - * [ ]: refactor: render_2d.frag should be merged into render_3d.frag * [ ]: refactor: ui should be in its own thread and that thread should only be concerned with the ui * [ ]: refactor: remove all the excessive measure_texts (cell drawing, hover_interaction in params table) * [ ]: refactor: move remaining fragment shader stuff into ui @@ -1384,15 +1383,21 @@ ui_copy_frame(BeamformerUI *ui, Variable *view, RegionSplitDirection direction) } function v3 -x_plane_size(BeamformerUI *ui) -{ - v3 min = v4_from_f32_array(ui->params.output_min_coordinate).xyz; - v3 max = v4_from_f32_array(ui->params.output_max_coordinate).xyz; - v3 result = v3_sub(max, min); - swap(result.y, result.z); - result.x = MAX(1e-3, result.x); - result.y = MAX(1e-3, result.y); - result.z = MAX(1e-3, result.z); +beamformer_frame_view_plane_size(BeamformerUI *ui, BeamformerFrameView *view) +{ + v3 result; + if (view->kind == BeamformerFrameViewKind_3DXPlane) { + v3 min = v4_from_f32_array(ui->params.output_min_coordinate).xyz; + v3 max = v4_from_f32_array(ui->params.output_max_coordinate).xyz; + result = v3_sub(max, min); + swap(result.y, result.z); + result.x = MAX(1e-3, result.x); + result.y = MAX(1e-3, result.y); + result.z = MAX(1e-3, result.z); + } else { + v2 size = v2_sub(XZ(view->max_coordinate), XZ(view->min_coordinate)); + result = (v3){.x = size.x, .y = size.y}; + } return result; } @@ -1438,7 +1443,7 @@ offset_x_plane_position(BeamformerUI *ui, BeamformerFrameView *view, BeamformerV function v3 camera_for_x_plane_view(BeamformerUI *ui, BeamformerFrameView *view) { - v3 size = x_plane_size(ui); + v3 size = beamformer_frame_view_plane_size(ui, view); v3 target = x_plane_position(ui); f32 dist = v2_magnitude(XY(size)); v3 result = v3_add(target, (v3){{dist, -0.5f * size.y * tan_f32(50.0f * PI / 180.0f), dist}}); @@ -1501,7 +1506,7 @@ render_single_xplane(BeamformerUI *ui, BeamformerFrameView *view, Variable *x_pl if (ui->latest_plane[tag]) texture = ui->latest_plane[tag]->texture; - v3 scale = x_plane_size(ui); + v3 scale = beamformer_frame_view_plane_size(ui, view); m4 model_transform = y_aligned_volume_transform(scale, translate, rotation_turns); v4 colour = v4_lerp(FG_COLOUR, HOVERED_COLOUR, x_plane_shift->hover_t); @@ -1533,8 +1538,6 @@ render_single_xplane(BeamformerUI *ui, BeamformerFrameView *view, Variable *x_pl function void render_3D_xplane(BeamformerUI *ui, BeamformerFrameView *view, u32 program) { - glBindVertexArray(ui->unit_cube_model.vao); - if (view->demo->bool32) { view->rotation += dt_for_frame * 0.125f; if (view->rotation > 1.0f) view->rotation -= 1.0f; @@ -1546,6 +1549,7 @@ render_3D_xplane(BeamformerUI *ui, BeamformerFrameView *view, u32 program) glProgramUniformMatrix4fv(program, FRAME_VIEW_VIEW_MATRIX_LOC, 1, 0, view_m.E); glProgramUniformMatrix4fv(program, FRAME_VIEW_PROJ_MATRIX_LOC, 1, 0, projection.E); + glProgramUniform1f(program, FRAME_VIEW_BB_FRACTION_LOC, FRAME_VIEW_BB_FRACTION); v3 model_translate = offset_x_plane_position(ui, view, BeamformerViewPlaneTag_XZ); render_single_xplane(ui, view, view->x_plane_shifts + 0, program, @@ -1558,6 +1562,24 @@ render_3D_xplane(BeamformerUI *ui, BeamformerFrameView *view, u32 program) model_translate, BeamformerViewPlaneTag_YZ); } +function void +render_2D_plane(BeamformerUI *ui, BeamformerFrameView *view, u32 program) +{ + m4 view_m = m4_identity(); + v3 size = beamformer_frame_view_plane_size(ui, view); + m4 model = m4_scale(size); + m4 projection = orthographic_projection(0, 1, size.y / 2, size.x / 2); + + glProgramUniformMatrix4fv(program, FRAME_VIEW_MODEL_MATRIX_LOC, 1, 0, model.E); + glProgramUniformMatrix4fv(program, FRAME_VIEW_VIEW_MATRIX_LOC, 1, 0, view_m.E); + glProgramUniformMatrix4fv(program, FRAME_VIEW_PROJ_MATRIX_LOC, 1, 0, projection.E); + + glProgramUniform1f(program, FRAME_VIEW_BB_FRACTION_LOC, 0); + glBindTextureUnit(0, view->frame->texture); + glDrawElements(GL_TRIANGLES, ui->unit_cube_model.elements, GL_UNSIGNED_SHORT, + (void *)ui->unit_cube_model.elements_offset); +} + function b32 frame_view_ready_to_present(BeamformerUI *ui, BeamformerFrameView *view) { @@ -1602,24 +1624,19 @@ function void update_frame_views(BeamformerUI *ui, Rect window) { FrameViewRenderContext *ctx = ui->frame_view_render_context; - b32 fbo_bound = 0, shader_3d = 0; + b32 fbo_bound = 0; for (BeamformerFrameView *view = ui->views; view; view = view->next) { if (view_update(ui, view)) { if (!fbo_bound) { fbo_bound = 1; glBindFramebuffer(GL_FRAMEBUFFER, ctx->framebuffers[0]); - glUseProgram(ctx->shaders[0]); + glUseProgram(ctx->shader); + glBindVertexArray(ui->unit_cube_model.vao); glEnable(GL_DEPTH_TEST); } - if (view->kind == BeamformerFrameViewKind_3DXPlane) { - if (!shader_3d) { glUseProgram(ctx->shaders[1]); shader_3d = 1; } - } else { - if ( shader_3d) { glUseProgram(ctx->shaders[0]); shader_3d = 0; } - } - u32 fb = ctx->framebuffers[0]; - u32 program = ctx->shaders[shader_3d]; + u32 program = ctx->shader; glViewport(0, 0, view->texture_dim.w, view->texture_dim.h); glProgramUniform1f(program, FRAME_VIEW_THRESHOLD_LOC, view->threshold.real32); glProgramUniform1f(program, FRAME_VIEW_DYNAMIC_RANGE_LOC, view->dynamic_range.real32); @@ -1631,7 +1648,7 @@ update_frame_views(BeamformerUI *ui, Rect window) glNamedFramebufferRenderbuffer(fb, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ctx->renderbuffers[1]); glClearNamedFramebufferfv(fb, GL_COLOR, 0, (f32 []){0, 0, 0, 0}); glClearNamedFramebufferfv(fb, GL_DEPTH, 0, (f32 []){1}); - render_3D_xplane(ui, view, ctx->shaders[1]); + render_3D_xplane(ui, view, program); /* NOTE(rnp): resolve multisampled scene */ glNamedFramebufferTexture(ctx->framebuffers[1], GL_COLOR_ATTACHMENT0, view->textures[0], 0); glBlitNamedFramebuffer(fb, ctx->framebuffers[1], 0, 0, FRAME_VIEW_RENDER_TARGET_SIZE, @@ -1641,9 +1658,7 @@ update_frame_views(BeamformerUI *ui, Rect window) glNamedFramebufferTexture(fb, GL_DEPTH_ATTACHMENT, view->textures[1], 0); glClearNamedFramebufferfv(fb, GL_COLOR, 0, (f32 []){0, 0, 0, 0}); glClearNamedFramebufferfv(fb, GL_DEPTH, 0, (f32 []){1}); - glBindVertexArray(ctx->vao); - glBindTextureUnit(0, view->frame->texture); - glDrawArrays(GL_TRIANGLES, 0, 6); + render_2D_plane(ui, view, program); } glGenerateTextureMipmap(view->textures[0]); view->dirty = 0; @@ -2227,7 +2242,7 @@ draw_3D_xplane_frame_view(BeamformerUI *ui, Arena arena, Variable *var, Rect dis i32 id = -1; if (hover_interaction(ui, mouse, auto_interaction(vr, var))) { ray mouse_ray = ray_for_x_plane_view(ui, view, normalized_p_in_rect(vr, mouse, 0)); - v3 x_size = v3_scale(x_plane_size(ui), 0.5f); + v3 x_size = v3_scale(beamformer_frame_view_plane_size(ui, view), 0.5f); f32 rotation = x_plane_rotation_for_view_plane(view, BeamformerViewPlaneTag_XZ); m4 x_rotation = m4_rotation_about_y(rotation); @@ -2326,7 +2341,7 @@ draw_beamformer_frame_view(BeamformerUI *ui, Arena a, Variable *var, Rect displa .y = pixels_per_meter.y * (frame->max_coordinate.z - max.z), }; - Rectangle tex_r = {texture_start.x, texture_start.y, texture_points.x, -texture_points.y}; + Rectangle tex_r = {texture_start.x, texture_start.y, texture_points.x, texture_points.y}; NPatchInfo tex_np = { tex_r, 0, 0, 0, 0, NPATCH_NINE_PATCH }; DrawTextureNPatch(make_raylib_texture(view), tex_np, vr.rl, (Vector2){0}, 0, WHITE);