Files
test2/source/blender/draw/engines/workbench/workbench_volume.cc
Clément Foucault 28ad3736e8 DRW: Move cube batch generation to GPU module
Avoid access on global DRWShapeCache and still
share the code for batch creation.
Each module is then responsible owner of their
own batch.
2025-02-17 12:36:34 +01:00

254 lines
8.9 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "draw_cache.hh"
#include "draw_common_c.hh"
#include "workbench_private.hh"
#include "BKE_volume.hh"
#include "BKE_volume_render.hh"
#include "BLI_math_geom.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<Volume *>(ob->data);
BKE_volume_load(volume, G.main);
const bke::VolumeGridData *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("Volume Object SubPass");
const bool use_slice = (volume->display.axis_slice_method == AXIS_SLICE_SINGLE);
sub_ps.shader_set(ShaderCache::get().volume_get(
false, volume->display.interpolation_method, false, use_slice));
sub_ps.push_constant("do_depth_test", scene_state.shading.type >= OB_SOLID);
const float density_scale = volume->display.density *
BKE_volume_density_scale(volume, ob->object_to_world().ptr());
sub_ps.bind_texture("depthBuffer", &resources.depth_tx);
sub_ps.bind_texture("stencil_tx", &stencil_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,
resources,
sub_ps,
ob_ref,
volume->display.slice_axis,
volume->display.slice_depth);
}
else {
float4x4 texture_to_world = ob->object_to_world() * float4x4(grid->texture_to_object);
float3 world_size = math::to_scale(texture_to_world);
int3 resolution;
GPU_texture_get_mipmap_size(grid->texture, 0, resolution);
float3 slice_count = float3(resolution) * 5.0f;
draw_volume_ps(
manager, resources, 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<FluidModifierData *>(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("Volume Modifier SubPass");
const bool use_slice = settings.axis_slice_method == AXIS_SLICE_SINGLE;
sub_ps.shader_set(
ShaderCache::get().volume_get(true, settings.interp_method, settings.use_coba, use_slice));
sub_ps.push_constant("do_depth_test", scene_state.shading.type >= OB_SOLID);
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);
sub_ps.bind_texture("stencil_tx", &stencil_tx_);
if (use_slice) {
draw_slice_ps(manager, resources, 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, resources, sub_ps, ob_ref, scene_state.sample, slice_count, world_size);
}
}
void VolumePass::draw(Manager &manager, View &view, SceneResources &resources)
{
if (!active_) {
return;
}
stencil_tx_ = resources.stencil_view.extract(manager, resources.depth_tx);
fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(resources.color_tx));
fb_.bind();
manager.submit(ps_, view);
}
void VolumePass::draw_slice_ps(Manager &manager,
SceneResources &resources,
PassMain::Sub &ps,
ObjectRef &ob_ref,
int slice_axis_enum,
float slice_depth)
{
float4x4 view_mat_inv = blender::draw::View::default_get().viewinv();
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 | DRW_STATE_CULL_FRONT);
ps.push_constant("slicePosition", slice_depth);
ps.push_constant("sliceAxis", axis);
ps.push_constant("stepLength", step_length);
ps.draw(resources.volume_cube_batch, manager.resource_handle(ob_ref));
}
void VolumePass::draw_volume_ps(Manager &manager,
SceneResources &resources,
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(resources.volume_cube_batch, manager.resource_handle(ob_ref));
}
} // namespace blender::workbench