Volumes: Generate proper fog volume in Mesh to Volume node

Currently `Mesh to Volume` creates a volume using
`openvdb::tools::meshToVolume` which is then filled with the specified
density and the class set to Fog Volume. This is wrong because
`meshToVolume` creates a signed distance field by default that needs
to be converted to a Fog Volume with `openvdb::tools::sdfToFogVolume`
to get a proper Fog volume.

Here is the description of what that function does (from OpenVDB):

"The active and negative-valued interior half of the narrow band
becomes a linear ramp from 0 to 1; the inactive interior becomes
active with a constant value of 1; and the exterior, including the
background and the active exterior half of the narrow band, becomes
inactive with a constant value of 0. The interior, though active,
remains sparse."

This means with this commit old files will not look the same.
There is no way to version this as the options for external band width
and not filling the volume is removed (they don't make any sense for a
proper fog volume).

Pull Request: https://projects.blender.org/blender/blender/pulls/107279
This commit is contained in:
Erik Abrahamsson
2023-04-25 21:56:14 +02:00
parent 5d0595fded
commit 700d168a5c
6 changed files with 40 additions and 103 deletions

View File

@@ -47,8 +47,6 @@ VolumeGrid *fog_volume_grid_add_from_mesh(Volume *volume,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
float voxel_size,
bool fill_volume,
float exterior_band_width,
float interior_band_width,
float density);
/**

View File

@@ -10,8 +10,10 @@
#include "GEO_mesh_to_volume.hh"
#ifdef WITH_OPENVDB
# include <algorithm>
# include <openvdb/openvdb.h>
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/LevelSetUtil.h>
# include <openvdb/tools/VolumeToMesh.h>
namespace blender::geometry {
@@ -88,26 +90,28 @@ float volume_compute_voxel_size(const Depsgraph *depsgraph,
float3 bb_max;
bounds_fn(bb_min, bb_max);
/* Compute the voxel size based on the desired number of voxels and the approximated bounding
* box of the volume. */
/* Compute the diagonal of the bounding box. This is used because
* it will always be bigger than the widest side of the mesh. */
const float diagonal = math::distance(math::transform_point(transform, bb_max),
math::transform_point(transform, bb_min));
const float approximate_volume_side_length = diagonal + exterior_band_width * 2.0f;
const float voxel_size = approximate_volume_side_length / res.settings.voxel_amount /
volume_simplify;
return voxel_size;
/* To get the approximate size per voxel, first subtract the exterior band from the requested
* voxel amount, then divide the diagonal with this value if it's bigger than 1. */
const float voxel_size =
(diagonal / std::max(1.0f, float(res.settings.voxel_amount) - 2.0f * exterior_band_width));
/* Return the simplified voxel size. */
return voxel_size / volume_simplify;
}
static openvdb::FloatGrid::Ptr mesh_to_fog_volume_grid(
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
{
if (voxel_size == 0.0f) {
if (voxel_size < 1e-5f || interior_band_width <= 0.0f) {
return nullptr;
}
@@ -117,22 +121,21 @@ static openvdb::FloatGrid::Ptr mesh_to_fog_volume_grid(
mesh_to_index_space_transform.location() -= 0.5f;
OpenVDBMeshAdapter mesh_adapter{*mesh, mesh_to_index_space_transform};
const float interior = std::max(1.0f, interior_band_width / voxel_size);
/* Convert the bandwidths from object in index space. */
const float exterior = MAX2(0.001f, exterior_band_width / voxel_size);
const float interior = MAX2(0.001f, interior_band_width / voxel_size);
/* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */
openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform(
voxel_size);
openvdb::FloatGrid::Ptr new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior, fill_volume ? FLT_MAX : interior);
mesh_adapter, *transform, 1.0f, interior);
/* Give each grid cell a fixed density for now. */
openvdb::tools::foreach (
new_grid->beginValueOn(),
[density](const openvdb::FloatGrid::ValueOnIter &iter) { iter.setValue(density); });
new_grid->setGridClass(openvdb::GRID_FOG_VOLUME);
openvdb::tools::sdfToFogVolume(*new_grid);
if (density != 1.0f) {
openvdb::tools::foreach (new_grid->beginValueOn(),
[&](const openvdb::FloatGrid::ValueOnIter &iter) {
iter.modifyValue([&](float &value) { value *= density; });
});
}
return new_grid;
}
@@ -180,31 +183,12 @@ VolumeGrid *fog_volume_grid_add_from_mesh(Volume *volume,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
{
VolumeGrid *c_grid = BKE_volume_grid_add(volume, name.c_str(), VOLUME_GRID_FLOAT);
openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
BKE_volume_grid_openvdb_for_write(volume, c_grid, false));
/* Generate grid from mesh */
openvdb::FloatGrid::Ptr mesh_grid = mesh_to_fog_volume_grid(mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);
if (mesh_grid != nullptr) {
/* Merge the generated grid. Should be cheap because grid has just been created. */
grid->merge(*mesh_grid);
/* Change transform so that the index space is correctly transformed to object space. */
grid->transform().postScale(voxel_size);
}
return c_grid;
openvdb::FloatGrid::Ptr mesh_grid = mesh_to_fog_volume_grid(
mesh, mesh_to_volume_space_transform, voxel_size, interior_band_width, density);
return mesh_grid ? BKE_volume_grid_add_vdb(*volume, name, std::move(mesh_grid)) : nullptr;
}
VolumeGrid *sdf_volume_grid_add_from_mesh(Volume *volume,

View File

@@ -2333,14 +2333,7 @@ typedef struct MeshToVolumeModifierData {
* different. */
int voxel_amount;
/** If true, every cell in the enclosed volume gets a density. Otherwise, the interior_band_width
* is used. */
char fill_volume;
char _pad1[3];
/** Band widths are in object space. */
float interior_band_width;
float exterior_band_width;
float density;
char _pad2[4];

View File

@@ -7085,19 +7085,9 @@ static void rna_def_modifier_mesh_to_volume(BlenderRNA *brna)
RNA_def_property_range(prop, 0, INT_MAX);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_fill_volume", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "fill_volume", 1);
RNA_def_property_ui_text(
prop, "Fill Volume", "Initialize the density grid in every cell inside the enclosed volume");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "interior_band_width", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop, "Interior Band Width", "Width of the volume inside of the mesh");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "exterior_band_width", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop, "Exterior Band Width", "Width of the volume outside of the mesh");
RNA_def_property_ui_text(
prop, "Interior Band Width", "Width of the gradient inside of the mesh");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_update(prop, 0, "rna_Modifier_update");

View File

@@ -51,9 +51,7 @@ static void initData(ModifierData *md)
mvmd->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT;
mvmd->voxel_size = 0.1f;
mvmd->voxel_amount = 32;
mvmd->fill_volume = true;
mvmd->interior_band_width = 0.1f;
mvmd->exterior_band_width = 0.1f;
mvmd->interior_band_width = 0.2f;
mvmd->density = 1.0f;
}
@@ -89,12 +87,7 @@ static void panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "use_fill_volume", 0, nullptr, ICON_NONE);
uiItemR(col, ptr, "exterior_band_width", 0, nullptr, ICON_NONE);
uiLayout *subcol = uiLayoutColumn(col, false);
uiLayoutSetActive(subcol, !mvmd->fill_volume);
uiItemR(subcol, ptr, "interior_band_width", 0, nullptr, ICON_NONE);
uiItemR(col, ptr, "interior_band_width", 0, nullptr, ICON_NONE);
}
{
uiLayout *col = uiLayoutColumn(layout, false);
@@ -140,13 +133,13 @@ static Volume *mesh_to_volume(ModifierData *md,
resolution.mode = (MeshToVolumeModifierResolutionMode)mvmd->resolution_mode;
if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) {
resolution.settings.voxel_amount = mvmd->voxel_amount;
if (resolution.settings.voxel_amount <= 0.0f) {
if (resolution.settings.voxel_amount < 1.0f) {
return input_volume;
}
}
else if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
resolution.settings.voxel_size = mvmd->voxel_size;
if (resolution.settings.voxel_size <= 0.0f) {
if (resolution.settings.voxel_size < 1e-5f) {
return input_volume;
}
}
@@ -157,11 +150,8 @@ static Volume *mesh_to_volume(ModifierData *md,
r_max = bb->vec[6];
};
const float voxel_size = geometry::volume_compute_voxel_size(ctx->depsgraph,
bounds_fn,
resolution,
mvmd->exterior_band_width,
mesh_to_own_object_space_transform);
const float voxel_size = geometry::volume_compute_voxel_size(
ctx->depsgraph, bounds_fn, resolution, 0.0f, mesh_to_own_object_space_transform);
/* Create a new volume. */
Volume *volume;
@@ -178,8 +168,6 @@ static Volume *mesh_to_volume(ModifierData *md,
mesh,
mesh_to_own_object_space_transform,
voxel_size,
mvmd->fill_volume,
mvmd->exterior_band_width,
mvmd->interior_band_width,
mvmd->density);

View File

@@ -32,21 +32,12 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(FLT_MAX)
.subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f).max(FLT_MAX);
b.add_input<decl::Float>(N_("Exterior Band Width"))
.default_value(0.1f)
.min(0.0f)
.max(FLT_MAX)
.subtype(PROP_DISTANCE)
.description(N_("Width of the volume outside of the mesh"));
b.add_input<decl::Float>(N_("Interior Band Width"))
.default_value(0.0f)
.min(0.0f)
.default_value(0.2f)
.min(0.0001f)
.max(FLT_MAX)
.subtype(PROP_DISTANCE)
.description(N_("Width of the volume inside of the mesh"));
b.add_input<decl::Bool>(N_("Fill Volume"))
.default_value(true)
.description(N_("Initialize the density grid in every cell inside the enclosed volume"));
.description(N_("Width of the gradient inside of the mesh"));
b.add_output<decl::Geometry>(CTX_N_(BLT_I18NCONTEXT_ID_ID, "Volume"))
.translation_context(BLT_I18NCONTEXT_ID_ID);
}
@@ -86,9 +77,7 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams &para
*(const NodeGeometryMeshToVolume *)params.node().storage;
const float density = params.get_input<float>("Density");
const float exterior_band_width = params.get_input<float>("Exterior Band Width");
const float interior_band_width = params.get_input<float>("Interior Band Width");
const bool fill_volume = params.get_input<bool>("Fill Volume");
geometry::MeshToVolumeResolution resolution;
resolution.mode = (MeshToVolumeModifierResolutionMode)storage.resolution_mode;
@@ -119,11 +108,8 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams &para
r_max = max;
};
const float voxel_size = geometry::volume_compute_voxel_size(params.depsgraph(),
bounds_fn,
resolution,
exterior_band_width,
mesh_to_volume_space_transform);
const float voxel_size = geometry::volume_compute_voxel_size(
params.depsgraph(), bounds_fn, resolution, 0.0f, mesh_to_volume_space_transform);
Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr));
@@ -133,8 +119,6 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams &para
&mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);