diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c6f8dcf335f..f7aab78b23b 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -180,6 +180,7 @@ set(SRC engines/workbench/workbench_state.cc engines/workbench/workbench_transparent.c engines/workbench/workbench_volume.c + engines/workbench/workbench_volume_next.cc engines/external/external_engine.c engines/gpencil/gpencil_antialiasing.c engines/gpencil/gpencil_cache_utils.c diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh index 698c7d1a8b7..e461e6ee077 100644 --- a/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh @@ -6,7 +6,7 @@ /** \name Volume shader base * \{ */ -GPU_SHADER_CREATE_INFO(workbench_volume) +GPU_SHADER_CREATE_INFO(workbench_volume_common) .vertex_in(0, Type::VEC3, "pos") .fragment_out(0, Type::VEC4, "fragColor") .sampler(0, ImageType::DEPTH_2D, "depthBuffer") @@ -16,27 +16,44 @@ GPU_SHADER_CREATE_INFO(workbench_volume) .push_constant(Type::FLOAT, "stepLength") .push_constant(Type::FLOAT, "densityScale") .vertex_source("workbench_volume_vert.glsl") - .fragment_source("workbench_volume_frag.glsl") - .additional_info("draw_object_infos"); + .fragment_source("workbench_volume_frag.glsl"); +GPU_SHADER_CREATE_INFO(workbench_volume) + .additional_info("workbench_volume_common", "draw_object_infos"); + +GPU_SHADER_CREATE_INFO(workbench_next_volume) + .define("WORKBENCH_NEXT") + .additional_info("workbench_volume_common", "draw_object_infos_new", "draw_view"); /** \} */ /* -------------------------------------------------------------------- */ /** \name Smoke variation * \{ */ -GPU_SHADER_CREATE_INFO(workbench_volume_smoke) +GPU_SHADER_CREATE_INFO(workbench_volume_smoke_common) .define("VOLUME_SMOKE") .sampler(2, ImageType::FLOAT_3D, "flameTexture") .sampler(3, ImageType::FLOAT_1D, "flameColorTexture") - .additional_info("draw_mesh", "draw_resource_id_varying"); + .additional_info("draw_resource_id_varying"); -GPU_SHADER_CREATE_INFO(workbench_volume_object) +GPU_SHADER_CREATE_INFO(workbench_volume_object_common) .define("VOLUME_OBJECT") .push_constant(Type::MAT4, "volumeTextureToObject") /* FIXME(fclem): This overflow the push_constant limit. */ .push_constant(Type::MAT4, "volumeObjectToTexture") - .additional_info("draw_volume", "draw_resource_id_varying"); + .additional_info("draw_resource_id_varying"); + +GPU_SHADER_CREATE_INFO(workbench_volume_smoke) + .additional_info("workbench_volume_smoke_common", "draw_mesh"); + +GPU_SHADER_CREATE_INFO(workbench_volume_object) + .additional_info("workbench_volume_object_common", "draw_volume"); + +GPU_SHADER_CREATE_INFO(workbench_next_volume_smoke) + .additional_info("workbench_volume_smoke_common", "draw_modelmat_new"); + +GPU_SHADER_CREATE_INFO(workbench_next_volume_object) + .additional_info("workbench_volume_object_common", "draw_volume_new"); /** \} */ @@ -111,4 +128,10 @@ GPU_SHADER_CREATE_INFO(workbench_volume_slice) WORKBENCH_VOLUME_SMOKE_VARIATIONS(workbench_volume, "workbench_volume") +#define WORKBENCH_NEXT_VOLUME_SMOKE_VARIATIONS(prefix, ...) \ + WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix##_smoke, "workbench_next_volume_smoke", __VA_ARGS__) \ + WORKBENCH_VOLUME_INTERP_VARIATIONS(prefix##_object, "workbench_next_volume_object", __VA_ARGS__) + +WORKBENCH_NEXT_VOLUME_SMOKE_VARIATIONS(workbench_next_volume, "workbench_next_volume") + /** \} */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 80190ede5c9..8d2bd08cf30 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -209,6 +209,12 @@ vec4 volume_integration(vec3 ray_ori, vec3 ray_dir, float ray_inc, float ray_max /* accumulate and also take into account the transmittance from previous steps */ final_scattering += final_transmittance * Lscat; final_transmittance *= Tr; + + if (final_transmittance <= 0.01) { + /* Early out */ + final_transmittance = 0.0; + break; + } } return vec4(final_scattering, final_transmittance); diff --git a/source/blender/draw/engines/workbench/workbench_engine.cc b/source/blender/draw/engines/workbench/workbench_engine.cc index d93106a3cca..ec897394067 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.cc +++ b/source/blender/draw/engines/workbench/workbench_engine.cc @@ -34,6 +34,7 @@ class Instance { TransparentDepthPass transparent_depth_ps; ShadowPass shadow_ps; + VolumePass volume_ps; OutlinePass outline_ps; DofPass dof_ps; AntiAliasingPass anti_aliasing_ps; @@ -75,6 +76,7 @@ class Instance { transparent_depth_ps.sync(scene_state, resources); shadow_ps.sync(); + volume_ps.sync(resources); outline_ps.sync(resources); dof_ps.sync(resources); anti_aliasing_ps.sync(resources, scene_state.resolution); @@ -85,6 +87,26 @@ class Instance { resources.material_buf.push_update(); } + Material get_material(ObjectRef ob_ref, eV3DShadingColorType color_type, int slot = 0) + { + switch (color_type) { + case V3D_SHADING_OBJECT_COLOR: + return Material(*ob_ref.object); + case V3D_SHADING_RANDOM_COLOR: + return Material(*ob_ref.object, true); + case V3D_SHADING_SINGLE_COLOR: + return scene_state.material_override; + case V3D_SHADING_VERTEX_COLOR: + return scene_state.material_attribute_color; + case V3D_SHADING_MATERIAL_COLOR: + if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, slot + 1)) { + return Material(*_mat); + } + default: + return Material(*BKE_material_default_empty()); + } + } + void object_sync(Manager &manager, ObjectRef &ob_ref) { if (scene_state.render_finished) { @@ -138,9 +160,8 @@ class Instance { if (md && BKE_modifier_is_enabled(scene_state.scene, md, eModifierMode_Realtime)) { FluidModifierData *fmd = (FluidModifierData *)md; if (fmd->domain) { -#if 0 /* TODO(@pragma37): */ - workbench_volume_cache_populate(vedata, wpd->scene, ob, md, V3D_SHADING_SINGLE_COLOR); -#endif + volume_ps.object_sync_modifier(manager, resources, scene_state, ob_ref, md); + if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { return; /* Do not draw solid in this case. */ } @@ -163,14 +184,16 @@ class Instance { #if 0 /* TODO(@pragma37): */ DRWShadingGroup *grp = workbench_material_hair_setup( wpd, ob, CURVES_MATERIAL_NR, object_state.color_type); - DRW_shgroup_curves_create_sub(ob, grp, NULL); + DRW_shgroup_curves_create_sub(ob, grp, nullptr); #endif } else if (ob->type == OB_VOLUME) { if (scene_state.shading.type != OB_WIRE) { -#if 0 /* TODO(@pragma37): */ - workbench_volume_cache_populate(vedata, wpd->scene, ob, NULL, object_state.color_type); -#endif + volume_ps.object_sync_volume(manager, + resources, + scene_state, + ob_ref, + get_material(ob_ref, object_state.color_type).base_color); } } } @@ -204,15 +227,7 @@ class Instance { continue; } - Material mat; - - if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, i + 1)) { - mat = Material(*_mat); - } - else { - mat = Material(*BKE_material_default_empty()); - } - + Material mat = get_material(ob_ref, object_state.color_type, i); has_transparent_material = has_transparent_material || mat.is_transparent(); ::Image *image = nullptr; @@ -244,24 +259,7 @@ class Instance { } if (batch) { - Material mat; - - if (object_state.color_type == V3D_SHADING_OBJECT_COLOR) { - mat = Material(*ob_ref.object); - } - else if (object_state.color_type == V3D_SHADING_RANDOM_COLOR) { - mat = Material(*ob_ref.object, true); - } - else if (object_state.color_type == V3D_SHADING_SINGLE_COLOR) { - mat = scene_state.material_override; - } - else if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) { - mat = scene_state.material_attribute_color; - } - else { - mat = Material(*BKE_material_default_empty()); - } - + Material mat = get_material(ob_ref, object_state.color_type); has_transparent_material = has_transparent_material || mat.is_transparent(); draw_mesh(ob_ref, @@ -370,8 +368,7 @@ class Instance { transparent_ps.draw(manager, view, resources, resolution); transparent_depth_ps.draw(manager, view, resources); - // volume_ps.draw_prepass(manager, view, resources.depth_tx); - + volume_ps.draw(manager, view, resources); outline_ps.draw(manager, resources); dof_ps.draw(manager, view, resources, resolution); anti_aliasing_ps.draw(manager, view, resources, resolution, depth_tx, color_tx); diff --git a/source/blender/draw/engines/workbench/workbench_private.hh b/source/blender/draw/engines/workbench/workbench_private.hh index 832e5ac5f23..5366314d073 100644 --- a/source/blender/draw/engines/workbench/workbench_private.hh +++ b/source/blender/draw/engines/workbench/workbench_private.hh @@ -324,6 +324,52 @@ class ShadowPass { bool force_fail_method); }; +class VolumePass { + bool active_ = true; + + PassMain ps_ = {"Volume"}; + Framebuffer fb_ = {"Volume"}; + + Texture dummy_shadow_tx_ = {"Volume.Dummy Shadow Tx"}; + Texture dummy_volume_tx_ = {"Volume.Dummy Volume Tx"}; + Texture dummy_coba_tx_ = {"Volume.Dummy Coba Tx"}; + + GPUShader *shaders_[2 /*slice*/][2 /*coba*/][3 /*interpolation*/][2 /*smoke*/]; + + public: + void sync(SceneResources &resources); + + void object_sync_volume(Manager &manager, + SceneResources &resources, + const SceneState &scene_state, + ObjectRef &ob_ref, + float3 color); + + void object_sync_modifier(Manager &manager, + SceneResources &resources, + const SceneState &scene_state, + ObjectRef &ob_ref, + ModifierData *md); + + void draw(Manager &manager, View &view, SceneResources &resources); + + private: + GPUShader *get_shader(bool slice, bool coba, int interpolation, bool smoke); + + void draw_slice_ps(Manager &manager, + PassMain::Sub &ps, + ObjectRef &ob_ref, + int slice_axis_enum, + float slice_depth); + + void draw_volume_ps(Manager &manager, + PassMain::Sub &ps, + ObjectRef &ob_ref, + int taa_sample, + float3 slice_count, + float3 world_size); +}; + class OutlinePass { private: bool enabled_ = false; diff --git a/source/blender/draw/engines/workbench/workbench_volume_next.cc b/source/blender/draw/engines/workbench/workbench_volume_next.cc new file mode 100644 index 00000000000..3307aa21a13 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_volume_next.cc @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "workbench_private.hh" + +#include "BKE_volume.h" +#include "BKE_volume_render.h" +#include "BLI_rand.h" +#include "DNA_fluid_types.h" +#include "DNA_modifier_types.h" + +namespace blender::workbench { + +void VolumePass::sync(SceneResources &resources) +{ + active_ = false; + ps_.init(); + ps_.bind_ubo(WB_WORLD_SLOT, resources.world_buf); + + dummy_shadow_tx_.ensure_3d(GPU_RGBA8, int3(1), GPU_TEXTURE_USAGE_SHADER_READ, float4(1)); + dummy_volume_tx_.ensure_3d(GPU_RGBA8, int3(1), GPU_TEXTURE_USAGE_SHADER_READ, float4(0)); + dummy_coba_tx_.ensure_1d(GPU_RGBA8, 1, GPU_TEXTURE_USAGE_SHADER_READ, float4(0)); +} + +void VolumePass::object_sync_volume(Manager &manager, + SceneResources &resources, + const SceneState &scene_state, + ObjectRef &ob_ref, + float3 color) +{ + Object *ob = ob_ref.object; + /* Create 3D textures. */ + Volume *volume = static_cast(ob->data); + BKE_volume_load(volume, G.main); + const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume); + if (volume_grid == nullptr) { + return; + } + + DRWVolumeGrid *grid = DRW_volume_batch_cache_get_grid(volume, volume_grid); + if (grid == nullptr) { + return; + } + + active_ = true; + + PassMain::Sub &sub_ps = ps_.sub(ob->id.name); + + const bool use_slice = (volume->display.axis_slice_method == AXIS_SLICE_SINGLE); + + sub_ps.shader_set(get_shader(use_slice, false, volume->display.interpolation_method, false)); + + const float density_scale = volume->display.density * + BKE_volume_density_scale(volume, ob->object_to_world); + + sub_ps.bind_texture("depthBuffer", &resources.depth_tx); + sub_ps.bind_texture("densityTexture", grid->texture); + /* TODO: implement shadow texture, see manta_smoke_calc_transparency. */ + sub_ps.bind_texture("shadowTexture", dummy_shadow_tx_); + sub_ps.push_constant("activeColor", color); + sub_ps.push_constant("densityScale", density_scale); + sub_ps.push_constant("volumeObjectToTexture", float4x4(grid->object_to_texture)); + sub_ps.push_constant("volumeTextureToObject", float4x4(grid->texture_to_object)); + + if (use_slice) { + draw_slice_ps( + manager, sub_ps, ob_ref, volume->display.slice_axis, volume->display.slice_depth); + } + else { + float3 world_size; + float4x4 texture_to_world = float4x4(ob->object_to_world) * float4x4(grid->texture_to_object); + math::normalize_and_get_size(float3x3(texture_to_world), world_size); + + int3 resolution; + GPU_texture_get_mipmap_size(grid->texture, 0, resolution); + float3 slice_count = float3(resolution) * 5.0f; + + draw_volume_ps(manager, sub_ps, ob_ref, scene_state.sample, slice_count, world_size); + } +} + +void VolumePass::object_sync_modifier(Manager &manager, + SceneResources &resources, + const SceneState &scene_state, + ObjectRef &ob_ref, + ModifierData *md) +{ + Object *ob = ob_ref.object; + + FluidModifierData *modifier = reinterpret_cast(md); + FluidDomainSettings &settings = *modifier->domain; + + if (!settings.fluid) { + return; + } + + bool can_load = false; + if (settings.use_coba) { + DRW_smoke_ensure_coba_field(modifier); + can_load = settings.tex_field != nullptr; + } + else if (settings.type == FLUID_DOMAIN_TYPE_GAS) { + DRW_smoke_ensure(modifier, settings.flags & FLUID_DOMAIN_USE_NOISE); + can_load = settings.tex_density != nullptr || settings.tex_color != nullptr; + } + + if (!can_load) { + return; + } + + active_ = true; + + PassMain::Sub &sub_ps = ps_.sub(ob->id.name); + + const bool use_slice = settings.axis_slice_method == AXIS_SLICE_SINGLE; + + sub_ps.shader_set(get_shader(use_slice, settings.use_coba, settings.interp_method, true)); + + if (settings.use_coba) { + const bool show_flags = settings.coba_field == FLUID_DOMAIN_FIELD_FLAGS; + const bool show_pressure = settings.coba_field == FLUID_DOMAIN_FIELD_PRESSURE; + const bool show_phi = ELEM(settings.coba_field, + FLUID_DOMAIN_FIELD_PHI, + FLUID_DOMAIN_FIELD_PHI_IN, + FLUID_DOMAIN_FIELD_PHI_OUT, + FLUID_DOMAIN_FIELD_PHI_OBSTACLE); + + sub_ps.push_constant("showFlags", show_flags); + sub_ps.push_constant("showPressure", show_pressure); + sub_ps.push_constant("showPhi", show_phi); + sub_ps.push_constant("gridScale", settings.grid_scale); + + if (show_flags) { + sub_ps.bind_texture("flagTexture", settings.tex_field); + } + else { + sub_ps.bind_texture("densityTexture", settings.tex_field); + } + + if (!show_flags && !show_pressure && !show_phi) { + sub_ps.bind_texture("transferTexture", settings.tex_coba); + } + } + else { + bool use_constant_color = ((settings.active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 && + (settings.active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0); + + sub_ps.push_constant("activeColor", + use_constant_color ? float3(settings.active_color) : float3(1)); + + sub_ps.bind_texture("densityTexture", + settings.tex_color ? settings.tex_color : settings.tex_density); + sub_ps.bind_texture("flameTexture", + settings.tex_flame ? settings.tex_flame : dummy_volume_tx_); + sub_ps.bind_texture("flameColorTexture", + settings.tex_flame ? settings.tex_flame_coba : dummy_coba_tx_); + sub_ps.bind_texture("shadowTexture", settings.tex_shadow); + } + + sub_ps.push_constant("densityScale", 10.0f * settings.display_thickness); + sub_ps.bind_texture("depthBuffer", &resources.depth_tx); + + if (use_slice) { + draw_slice_ps(manager, sub_ps, ob_ref, settings.slice_axis, settings.slice_depth); + } + else { + float3 world_size; + BKE_object_dimensions_get(ob, world_size); + + float3 slice_count = float3(settings.res) * std::max(0.001f, settings.slice_per_voxel); + + draw_volume_ps(manager, sub_ps, ob_ref, scene_state.sample, slice_count, world_size); + } +} + +void VolumePass::draw(Manager &manager, View &view, SceneResources &resources) +{ + if (!active_) { + return; + } + fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(resources.color_tx)); + fb_.bind(); + manager.submit(ps_, view); +} + +GPUShader *VolumePass::get_shader(bool slice, bool coba, int interpolation, bool smoke) +{ + GPUShader *&shader = shaders_[slice][coba][interpolation][smoke]; + + if (shader == nullptr) { + std::string create_info_name = "workbench_next_volume"; + create_info_name += (smoke) ? "_smoke" : "_object"; + switch (interpolation) { + case VOLUME_DISPLAY_INTERP_LINEAR: + create_info_name += "_linear"; + break; + case VOLUME_DISPLAY_INTERP_CUBIC: + create_info_name += "_cubic"; + break; + case VOLUME_DISPLAY_INTERP_CLOSEST: + create_info_name += "_closest"; + break; + default: + BLI_assert_unreachable(); + } + create_info_name += (coba) ? "_coba" : "_no_coba"; + create_info_name += (slice) ? "_slice" : "_no_slice"; + shader = GPU_shader_create_from_info_name(create_info_name.c_str()); + } + return shader; +} + +void VolumePass::draw_slice_ps( + Manager &manager, PassMain::Sub &ps, ObjectRef &ob_ref, int slice_axis_enum, float slice_depth) +{ + float4x4 view_mat_inv; + DRW_view_viewmat_get(nullptr, view_mat_inv.ptr(), true); + + const int axis = (slice_axis_enum == SLICE_AXIS_AUTO) ? + axis_dominant_v3_single(view_mat_inv[2]) : + slice_axis_enum - 1; + + float3 dimensions; + BKE_object_dimensions_get(ob_ref.object, dimensions); + /* 0.05f to achieve somewhat the same opacity as the full view. */ + float step_length = std::max(1e-16f, dimensions[axis] * 0.05f); + + ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL); + ps.push_constant("slicePosition", slice_depth); + ps.push_constant("sliceAxis", axis); + ps.push_constant("stepLength", step_length); + + ps.draw(DRW_cache_quad_get(), manager.resource_handle(ob_ref)); +} + +void VolumePass::draw_volume_ps(Manager &manager, + PassMain::Sub &ps, + ObjectRef &ob_ref, + int taa_sample, + float3 slice_count, + float3 world_size) +{ + double noise_offset; + BLI_halton_1d(3, 0.0, taa_sample, &noise_offset); + + int max_slice = std::max({UNPACK3(slice_count)}); + float step_length = math::length((1.0f / slice_count) * world_size); + + ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_CULL_FRONT); + ps.push_constant("samplesLen", max_slice); + ps.push_constant("stepLength", step_length); + ps.push_constant("noiseOfs", float(noise_offset)); + + ps.draw(DRW_cache_cube_get(), manager.resource_handle(ob_ref)); +} + +} // namespace blender::workbench diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh index 5f61dec7a00..0341e9d90be 100644 --- a/source/blender/draw/intern/shaders/draw_view_info.hh +++ b/source/blender/draw/intern/shaders/draw_view_info.hh @@ -126,6 +126,9 @@ GPU_SHADER_CREATE_INFO(draw_pointcloud) GPU_SHADER_CREATE_INFO(draw_volume).additional_info("draw_modelmat", "draw_resource_id_uniform"); +GPU_SHADER_CREATE_INFO(draw_volume_new) + .additional_info("draw_modelmat_new", "draw_resource_handle_new"); + GPU_SHADER_CREATE_INFO(draw_gpencil) .typedef_source("gpencil_shader_shared.h") .define("DRW_GPENCIL_INFO")