volviewer

Volumetric Data Toy Viewer
git clone anongit@rnpnr.xyz:volviewer.git
Log | Files | Refs | Feed | LICENSE

Commit: 719cb1e490abf88c1acd38bbc08ca540a9dd60ca
Parent: e509d444b697072fc122f7b12d3b0eee5d75cec2
Author: Randy Palamar
Date:   Fri, 23 May 2025 20:59:00 -0600

render to separate render target

this will be needed to export a video with a specific resolution
independent of the current window resolution.

Diffstat:
Mcommon.c | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Rrender.frag.glsl -> render_model.frag.glsl | 0
Arender_overlay.frag.glsl | 16++++++++++++++++
Mutil.c | 9+++++++++
Mutil.h | 11++++++++++-
5 files changed, 192 insertions(+), 48 deletions(-)

diff --git a/common.c b/common.c @@ -3,7 +3,14 @@ #include "glad/gl.h" #include "GLFW/glfw3.h" -#define static_path_join(a, b) (a OS_PATH_SEPARATOR b) +#define BG_CLEAR_COLOUR (v4){{0.12, 0.1, 0.1, 1}} +#define RENDER_TARGET_SIZE 720, 720 + +/* NOTE(rnp): for video output we will render a full rotation in this much time at the + * the specified frame rate */ +#define OUTPUT_TIME_SECONDS 8.0f +#define OUTPUT_FRAME_RATE 30.0f +#define OUTPUT_BG_CLEAR_COLOUR (v4){{0.0, 0.2, 0.0, 1}} #define VIEWER_RENDER_DYNAMIC_RANGE_LOC (0) #define VIEWER_RENDER_THRESHOLD_LOC (1) @@ -114,50 +121,25 @@ load_shader(OS *os, Arena arena, str8 vs_text, str8 fs_text, str8 info_name, str return result; } -function FILE_WATCH_CALLBACK_FN(reload_render_shader) -{ - RenderContext *ctx = (typeof(ctx))user_data; - - local_persist str8 vertex = str8("" - "#version 460 core\n" - "\n" - "layout(location = 0) in vec3 v_normal;\n" - "layout(location = 1) in vec3 v_position;\n" - "layout(location = 2) in vec3 v_texture_coordinate;\n" - "layout(location = 3) in vec3 v_colour;\n" - "layout(location = 4) in uint v_flags;\n" - "\n" - "layout(location = 0) out vec3 f_texture_coordinate;\n" - "layout(location = 1) out vec3 f_colour;\n" - "\n" - "void main()\n" - "{\n" - "\tf_texture_coordinate = v_texture_coordinate;\n" - "\tf_colour = v_colour;\n" - "\tgl_Position = vec4(v_position, 1);\n" - "}\n"); - - str8 header = push_str8(&tmp, str8("" - "#version 460 core\n\n" - "layout(location = 0) in vec3 texture_coordinate;\n" - "layout(location = 1) in vec3 colour;\n" - "layout(location = 0) out vec4 out_colour;\n\n" - "layout(location = " str(VIEWER_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" - "layout(location = " str(VIEWER_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" - "layout(location = " str(VIEWER_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n" - "layout(location = " str(VIEWER_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" - "\n#line 1\n")); +typedef struct { + RenderContext *render_context; + str8 vertex_text; + str8 fragment_header; +} ShaderReloadContext; +function FILE_WATCH_CALLBACK_FN(reload_shader) +{ + ShaderReloadContext *ctx = (typeof(ctx))user_data; + str8 header = push_str8(&tmp, ctx->fragment_header); str8 fragment = os->read_whole_file(&tmp, (c8 *)path.data); fragment.data -= header.len; fragment.len += header.len; assert(fragment.data == header.data); - u32 new_program = load_shader(os, tmp, vertex, fragment, path, str8("Render Shader")); + u32 new_program = load_shader(os, tmp, ctx->vertex_text, fragment, path, path); if (new_program) { - glDeleteProgram(ctx->shader); - ctx->shader = new_program; + glDeleteProgram(ctx->render_context->shader); + ctx->render_context->shader = new_program; } - return 1; } @@ -174,7 +156,6 @@ fb_callback(GLFWwindow *window, s32 w, s32 h) { ViewerContext *ctx = glfwGetWindowUserPointer(window); ctx->window_size = (sv2){.w = w, .h = h}; - glViewport(0, 0, w, h); } function void @@ -207,7 +188,7 @@ init_viewer(ViewerContext *ctx) glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); #endif - RenderContext *rc = &ctx->render_context; + RenderContext *rc = &ctx->model_render_context; Vertex vertices[] = { {.position = {{-0.5, -0.5, 0.0}}, .colour = {{1.0, 0.0, 0.0}}}, @@ -232,20 +213,149 @@ init_viewer(ViewerContext *ctx) glEnableVertexAttribArray(3); glEnableVertexAttribArray(4); - str8 render = str8("render.frag.glsl"); - reload_render_shader(&ctx->os, render, (sptr)rc, ctx->arena); - os_add_file_watch(&ctx->os, &ctx->arena, render, reload_render_shader, (sptr)rc); + RenderTarget *rt = &ctx->output_target; + glCreateTextures(GL_TEXTURE_2D, countof(rt->textures), rt->textures); + rt->size = (sv2){{RENDER_TARGET_SIZE}}; + glTextureStorage2D(rt->textures[0], 1, GL_RGBA8, RENDER_TARGET_SIZE); + glTextureStorage2D(rt->textures[1], 1, GL_DEPTH_COMPONENT24, RENDER_TARGET_SIZE); + + glCreateFramebuffers(1, &rt->fb); + glNamedFramebufferTexture(rt->fb, GL_COLOR_ATTACHMENT0, rt->textures[0], 0); + glNamedFramebufferTexture(rt->fb, GL_DEPTH_ATTACHMENT, rt->textures[1], 0); + + ShaderReloadContext *model_rc = push_struct(&ctx->arena, ShaderReloadContext); + model_rc->render_context = rc; + model_rc->vertex_text = str8("" + "#version 460 core\n" + "\n" + "layout(location = 0) in vec3 v_normal;\n" + "layout(location = 1) in vec3 v_position;\n" + "layout(location = 2) in vec3 v_texture_coordinate;\n" + "layout(location = 3) in vec3 v_colour;\n" + "layout(location = 4) in uint v_flags;\n" + "\n" + "layout(location = 0) out vec3 f_texture_coordinate;\n" + "layout(location = 1) out vec3 f_colour;\n" + "\n" + "void main()\n" + "{\n" + "\tf_texture_coordinate = v_texture_coordinate;\n" + "\tf_colour = v_colour;\n" + "\tgl_Position = vec4(v_position, 1);\n" + "}\n"); + + model_rc->fragment_header = str8("" + "#version 460 core\n\n" + "layout(location = 0) in vec3 texture_coordinate;\n" + "layout(location = 1) in vec3 colour;\n" + "layout(location = 0) out vec4 out_colour;\n\n" + "layout(location = " str(VIEWER_RENDER_DYNAMIC_RANGE_LOC) ") uniform float u_db_cutoff = 60;\n" + "layout(location = " str(VIEWER_RENDER_THRESHOLD_LOC) ") uniform float u_threshold = 40;\n" + "layout(location = " str(VIEWER_RENDER_GAMMA_LOC) ") uniform float u_gamma = 1;\n" + "layout(location = " str(VIEWER_RENDER_LOG_SCALE_LOC) ") uniform bool u_log_scale;\n" + "\n#line 1\n"); + + str8 render_model = str8("render_model.frag.glsl"); + reload_shader(&ctx->os, render_model, (sptr)model_rc, ctx->arena); + os_add_file_watch(&ctx->os, &ctx->arena, render_model, reload_shader, (sptr)model_rc); + + rc = &ctx->overlay_render_context; + ShaderReloadContext *overlay_rc = push_struct(&ctx->arena, ShaderReloadContext); + overlay_rc->render_context = rc; + overlay_rc->vertex_text = str8("" + "#version 460 core\n" + "\n" + "layout(location = 0) in vec2 v_position;\n" + "layout(location = 1) in vec2 v_texture_coordinate;\n" + "layout(location = 2) in vec3 v_colour;\n" + "layout(location = 3) in uint v_flags;\n" + "\n" + "layout(location = 0) out vec2 f_texture_coordinate;\n" + "layout(location = 1) out vec3 f_colour;\n" + "layout(location = 2) out uint f_flags;\n" + "\n" + "layout(location = 0) uniform ivec2 u_screen_size;\n" + "\n" + "void main()\n" + "{\n" + "\tf_texture_coordinate = 1 - v_texture_coordinate;\n" + "\tf_colour = v_colour;\n" + "\tf_flags = v_flags;\n" + "\tgl_Position = vec4(v_position, 0, 1);\n" + //"\tgl_Position = vec4(2 * (v_position / vec2(u_screen_size)) - 1, 0, 1);\n" + "}\n"); + + overlay_rc->fragment_header = str8("" + "#version 460 core\n\n" + "layout(location = 0) in vec2 texture_coordinate;\n" + "layout(location = 1) in vec3 colour;\n" + "layout(location = 0) out vec4 out_colour;\n" + "\n#line 1\n"); + + f32 overlay_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, + }; + glGenVertexArrays(1, &rc->vao); + glBindVertexArray(rc->vao); + glGenBuffers(1, &rc->vbo); + glBindBuffer(GL_ARRAY_BUFFER, rc->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(overlay_vertices), overlay_vertices, GL_STATIC_DRAW); + glVertexAttribPointer(0, 2, GL_FLOAT, 0, 4 * sizeof(f32), 0); + glVertexAttribPointer(1, 2, GL_FLOAT, 0, 4 * sizeof(f32), (void *)(2 * sizeof(f32))); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glBindVertexArray(0); + + str8 render_overlay = str8("render_overlay.frag.glsl"); + reload_shader(&ctx->os, render_overlay, (sptr)overlay_rc, ctx->arena); + os_add_file_watch(&ctx->os, &ctx->arena, render_overlay, reload_shader, (sptr)overlay_rc); } function void viewer_frame_step(ViewerContext *ctx) { - glClear(GL_COLOR_BUFFER_BIT); - - glUseProgram(ctx->render_context.shader); - glBindVertexArray(ctx->render_context.vao); + ////////////// + // 3D Models + glBindFramebuffer(GL_FRAMEBUFFER, ctx->output_target.fb); + glClearNamedFramebufferfv(ctx->output_target.fb, GL_COLOR, 0, OUTPUT_BG_CLEAR_COLOUR.E); + glViewport(0, 0, ctx->output_target.size.w, ctx->output_target.size.h); + + glUseProgram(ctx->model_render_context.shader); + glBindVertexArray(ctx->model_render_context.vao); glDrawArrays(GL_TRIANGLES, 0, 3); + //////////////// + // UI Overlay + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearNamedFramebufferfv(0, GL_COLOR, 0, BG_CLEAR_COLOUR.E); + + f32 aspect_ratio = (f32)ctx->output_target.size.w / (f32)ctx->output_target.size.h; + sv2 target_size = ctx->window_size; + if (aspect_ratio > 1) target_size.h = target_size.w / aspect_ratio; + else target_size.w = target_size.h * aspect_ratio; + + if (target_size.w > ctx->window_size.w) { + target_size.w = ctx->window_size.w; + target_size.h = ctx->window_size.w / aspect_ratio; + } else if (target_size.h > ctx->window_size.h) { + target_size.h = ctx->window_size.h; + target_size.w = target_size.h * aspect_ratio; + } + + sv2 size_delta = sv2_sub(ctx->window_size, target_size); + + glViewport(size_delta.x / 2 + 0.5, size_delta.y / 2 + 0.5, target_size.w, target_size.h); + + glUseProgram(ctx->overlay_render_context.shader); + glBindTextureUnit(0, ctx->output_target.textures[0]); + glBindVertexArray(ctx->overlay_render_context.vao); + glDrawArrays(GL_TRIANGLES, 0, 6); + glfwSwapBuffers(ctx->window); glfwPollEvents(); } diff --git a/render.frag.glsl b/render_model.frag.glsl diff --git a/render_overlay.frag.glsl b/render_overlay.frag.glsl @@ -0,0 +1,16 @@ +/* See LICENSE for license details. */ +layout(binding = 0) uniform sampler2D u_texture; + +/* 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() +{ + out_colour = vec4(texture(u_texture, texture_coordinate).xyz, 1); +} diff --git a/util.c b/util.c @@ -407,6 +407,15 @@ parse_f64(str8 s) return result; } +function sv2 +sv2_sub(sv2 a, sv2 b) +{ + sv2 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + return result; +} + function FileWatchDirectory * lookup_file_watch_directory(FileWatchContext *ctx, u64 hash) { diff --git a/util.h b/util.h @@ -280,10 +280,19 @@ typedef struct { } RenderContext; typedef struct { + u32 fb; + u32 textures[2]; + sv2 size; +} RenderTarget; + +typedef struct { Arena arena; OS os; - RenderContext render_context; + RenderContext model_render_context; + RenderContext overlay_render_context; + + RenderTarget output_target; sv2 window_size;