diff --git a/libs/cgv_gl/glsl/volume.glfs b/libs/cgv_gl/glsl/volume.glfs index c0fe39e08..0caddfa7b 100644 --- a/libs/cgv_gl/glsl/volume.glfs +++ b/libs/cgv_gl/glsl/volume.glfs @@ -43,6 +43,11 @@ mat3 get_inverse_normal_matrix(); #define ISOSURFACE_MODE 0 #define ISOSURFACE_COLOR_MODE 0 +// 0 - Disabled +// 1 - Opaque +// 2 - Transparent +#define SLICE_MODE 0 + // automatic defines #define TRANSFER_FUNCTION_SAMPLER_DIMENSIONS 1 @@ -77,12 +82,12 @@ layout (binding = 3) uniform sampler3D gradient_tex; layout (binding = 4) uniform sampler2D depth_tex; #endif -uniform vec2 viewport_dims; -uniform vec2 noise_offset; +uniform vec2 viewport_dims; +uniform vec2 noise_offset; uniform float scale_adjustment_factor; -uniform bool light_local_to_eye; -uniform vec3 light_direction; +uniform bool light_local_to_eye; +uniform vec3 light_direction; uniform float ambient_strength; uniform float diffuse_strength; uniform float specular_strength; @@ -92,10 +97,14 @@ uniform float specular_color_mix; uniform float gradient_lambda; uniform float isovalue; -uniform vec3 isosurface_color; +uniform vec3 isosurface_color; + +uniform int slice_axis; +uniform float slice_coordinate; +uniform float slice_opacity; -uniform vec3 clip_box_min; -uniform vec3 clip_box_max; +uniform vec3 clip_box_min; +uniform vec3 clip_box_max; // in/out parameters in vec3 position_object; @@ -382,6 +391,9 @@ void main() { // initial color with full transparency for front-to-back blending vec4 color = vec4(0.0, 0.0, 0.0, 1.0); // step count (needed later for averaging the density if using average compositing mode) +#if SLICE_MODE > 0 + bool initial_side_slice = mix(clip_box_min, clip_box_max, ray.origin)[slice_axis] > slice_coordinate; +#endif int istep = 0; for(istep; istep < NUM_STEPS; ++istep) { float t = istep * step_size; @@ -411,7 +423,6 @@ void main() { // apply the transfer function vec4 color_in = apply_transfer_function(density); - #if ISOSURFACE_MODE > 0 #if ISOSURFACE_MODE == 1 bool is_isosurface = density >= isovalue; @@ -446,7 +457,6 @@ void main() { #if ENABLE_GRADIENT_MODULATION == 1 opacity_modulation = pow(gradient_length, gradient_lambda); #endif - #if ISOSURFACE_MODE > 0 if(is_isosurface) { #if ISOSURFACE_COLOR_MODE == 0 @@ -463,22 +473,35 @@ void main() { break; } #endif - + #if SLICE_MODE > 0 + bool at_slice = ((uvw[slice_axis] > slice_coordinate) != initial_side_slice); + if (at_slice) { + #if SLICE_MODE == 1 + color_in.a = 1.0; + #else + color_in.a = slice_opacity; + #endif + initial_side_slice = !initial_side_slice; + } + else { + #endif #if ENABLE_LIGHTING == 1 - color_in.rgb = apply_lighting(color_in.rgb, illumination); + color_in.rgb = apply_lighting(color_in.rgb, illumination); #endif - // apply opacity modulation as determined by gradient length if enabled - color_in.a *= opacity_modulation; + // apply opacity modulation as determined by gradient length if enabled + color_in.a *= opacity_modulation; - color_in.a = -exp(-step_size * scale_adjustment_factor * color_in.a) + 1.0; - + color_in.a = -exp(-step_size * scale_adjustment_factor * color_in.a) + 1.0; + #if SLICE_MODE > 0 + } + #endif // premultiply alpha color_in.rgb *= color_in.a; // blend front-to-back - color.rgb = color.a*color_in.rgb + color.rgb; - color.a = (1.0 - color_in.a)*color.a; + color.rgb += color.a*color_in.rgb; + color.a *= (1.0 - color_in.a); // early termination when transparency threshold is reached #if ENABLE_ISOSURFACE == 0 @@ -487,7 +510,11 @@ void main() { break; } #endif - + #if SLICE_MODE == 1 + if (at_slice) { + break; + } + #endif #endif } diff --git a/libs/cgv_gl/volume_renderer.cxx b/libs/cgv_gl/volume_renderer.cxx index a7912ce22..8dc01e291 100644 --- a/libs/cgv_gl/volume_renderer.cxx +++ b/libs/cgv_gl/volume_renderer.cxx @@ -48,6 +48,11 @@ namespace cgv { isosurface_color = rgb(0.7f); isosurface_color_from_transfer_function = false; + slice_mode = SM_DISABLED; + slice_axis = 2; + slice_coordinate = 0.5f; + slice_opacity = 0.5f; + clip_box = box3(vec3(0.0f), vec3(1.0f)); } @@ -114,6 +119,8 @@ namespace cgv { shader_code::set_define(defines, "ISOSURFACE_MODE", vrs.isosurface_mode, volume_render_style::IM_NONE); shader_code::set_define(defines, "ISOSURFACE_COLOR_MODE", vrs.isosurface_color_from_transfer_function, false); + shader_code::set_define(defines, "SLICE_MODE", vrs.slice_mode, volume_render_style::SM_DISABLED); + shader_code::set_define(defines, "COMPOSITING_MODE", vrs.compositing_mode, volume_render_style::CM_BLEND); if(transfer_function_texture) shader_code::set_define(defines, "TRANSFER_FUNCTION_SAMPLER_DIMENSIONS", transfer_function_texture->get_nr_dimensions(), 1u); @@ -218,6 +225,10 @@ namespace cgv { ref_prog().set_uniform(ctx, "isovalue", vrs.isovalue); ref_prog().set_uniform(ctx, "isosurface_color", vrs.isosurface_color); + ref_prog().set_uniform(ctx, "slice_axis", vrs.slice_axis); + ref_prog().set_uniform(ctx, "slice_coordinate", vrs.slice_coordinate); + ref_prog().set_uniform(ctx, "slice_opacity", vrs.slice_opacity); + ref_prog().set_uniform(ctx, "clip_box_min", vrs.clip_box.get_min_pnt()); ref_prog().set_uniform(ctx, "clip_box_max", vrs.clip_box.get_max_pnt()); @@ -321,6 +332,15 @@ namespace cgv { p->align("\b"); p->end_tree_node(vrs_ptr->enable_gradient_modulation); } + if (p->begin_tree_node("Orthogonal Slicing", vrs_ptr->slice_mode, false)) { + p->align("\a"); + p->add_member_control(b, "Mode", vrs_ptr->slice_mode, "dropdown", "enums='Disabled,Opaque,Transparent'"); + p->add_member_control(b, "Axis", vrs_ptr->slice_axis, "value_slider", "min=0;max=2;ticks=true"); + p->add_member_control(b, "Coordinate", vrs_ptr->slice_coordinate, "value_slider", "min=0;max=1;ticks=true"); + p->add_member_control(b, "Opacity", vrs_ptr->slice_opacity, "value_slider", "min=0;max=1;ticks=true"); + p->align("\b"); + p->end_tree_node(vrs_ptr->slice_mode); + } if(p->begin_tree_node("Isosurface (Blend only)", vrs_ptr->isosurface_mode, false)) { p->align("\a"); diff --git a/libs/cgv_gl/volume_renderer.h b/libs/cgv_gl/volume_renderer.h index de678ba24..79331ccc0 100644 --- a/libs/cgv_gl/volume_renderer.h +++ b/libs/cgv_gl/volume_renderer.h @@ -91,6 +91,19 @@ namespace cgv { // @< /// whether to color the isosurface based on the transfer function bool isosurface_color_from_transfer_function; + /// mode of slice rendering + enum SliceMode { + SM_DISABLED = 0, // no slice + SM_OPAQUE = 1, // opaque slice rendering + SM_TRANSPARENT = 2 // transparent slice rendering + } slice_mode; + /// coordinate axis orthogonal to which slice is rendered + int slice_axis; + /// coordinate value along axis defining slice in range [0,1] + float slice_coordinate; + /// in case of transparent mode, slice opacity + float slice_opacity; + /// a bounding box used to define a subspace of the volume to be visualized box3 clip_box; diff --git a/plugins/examples/volume_rendering.cxx b/plugins/examples/volume_rendering.cxx index 4f6029109..4fdb5b4ae 100644 --- a/plugins/examples/volume_rendering.cxx +++ b/plugins/examples/volume_rendering.cxx @@ -18,7 +18,7 @@ namespace cgv { } } -volume_viewer::volume_viewer() : application_plugin("Volume Viewer") +volume_viewer::volume_viewer() : application_plugin("Volume Viewer"), depth_tex("[D]") { // setup volume bounding box as unit cube centered around origin volume_bounding_box = box3(vec3(-0.5f), vec3(0.5f)); @@ -32,7 +32,11 @@ volume_viewer::volume_viewer() : application_plugin("Volume Viewer") volume_tex.set_wrap_r(cgv::render::TW_CLAMP_TO_BORDER); volume_tex.set_border_color(0.0f, 0.0f, 0.0f, 0.0f); - vstyle.enable_depth_test = false; + // an extra depth texture is used to enable mixing of opaque geometry and the volume + depth_tex.set_min_filter(cgv::render::TF_NEAREST); + depth_tex.set_mag_filter(cgv::render::TF_NEAREST); + + vstyle.enable_depth_test = true; show_box = true; @@ -174,17 +178,32 @@ void volume_viewer::init_frame(cgv::render::context& ctx) { transfer_function_legend_ptr->set_color_map(ctx, transfer_function); } } + if (depth_tex.is_created() && (ctx.get_width() != depth_tex.get_width() || ctx.get_height() != depth_tex.get_height())) + depth_tex.destruct(ctx); + + if (!depth_tex.is_created()) + depth_tex.create(ctx, cgv::render::TT_2D, ctx.get_width(), ctx.get_height()); } -void volume_viewer::draw(cgv::render::context& ctx) +void volume_viewer::draw(cgv::render::context& ctx) { // default render style for the bounding box static const cgv::render::box_wire_render_style box_rs; // render the wireframe bounding box if enabled - if(show_box) + if (show_box) box_rd.render(ctx, cgv::render::ref_box_wire_renderer(ctx), box_rs); +} + +void volume_viewer::after_finish(cgv::render::context & ctx) +{ + if (!view_ptr) + return; + + // copy the contents of the depth buffer from the opaque geometry into the extra depth texture + depth_tex.replace_from_buffer(ctx, 0, 0, 0, 0, ctx.get_width(), ctx.get_height()); + // render the volume auto& vr = cgv::render::ref_volume_renderer(ctx); vr.set_render_style(vstyle); @@ -192,8 +211,8 @@ void volume_viewer::draw(cgv::render::context& ctx) vr.set_transfer_function_texture(&transfer_function.ref_texture()); // get the texture from the transfer function color map to transform scalar volume values into RGBA colors // set the volume bounding box and enable transform to automatically place and size the volume to the defined bounds vr.set_bounding_box(volume_bounding_box); + vr.set_depth_texture(&depth_tex); vr.transform_to_bounding_box(true); - vr.render(ctx, 0, 0); } @@ -640,4 +659,4 @@ void volume_viewer::create_histogram() { #include -cgv::base::factory_registration volume_viewer_fac("New/Render/Volume Rendering"); +cgv::base::factory_registration volume_viewer_fac("Volume Rendering", "shortcut='Ctrl-Alt-V';menu_text='New/Render/Volume Rendering'", true); diff --git a/plugins/examples/volume_rendering.h b/plugins/examples/volume_rendering.h index 46e6d89c7..152b22026 100644 --- a/plugins/examples/volume_rendering.h +++ b/plugins/examples/volume_rendering.h @@ -25,7 +25,7 @@ class volume_viewer : cgv::app::color_map_editor_ptr transfer_function_editor_ptr; cgv::app::color_map_legend_ptr transfer_function_legend_ptr; - /// resolution of the volume + /// resolution of the volume uvec3 vres; /// spacing of the voxels vec3 vspacing; @@ -36,7 +36,8 @@ class volume_viewer : std::vector vol_data; box3 volume_bounding_box; cgv::render::texture volume_tex; - + cgv::render::texture depth_tex; + // Render members /// store a pointer to the view @@ -88,6 +89,7 @@ class volume_viewer : bool init(cgv::render::context& ctx); void init_frame(cgv::render::context& ctx); void draw(cgv::render::context& ctx); + void after_finish(cgv::render::context& ctx); // interface of provider void create_gui();