Volumes: support lower resolution in viewport

The adds a new option to simplify volumes in the viewport.
The setting can be found in the Simplify panel in the render properties.

Volume objects use OpenVDB grids, which are sparse. For rendering,
we have to convert sparse grids to dense grids (for now). Those require
significantly more memory. Therefore, it's often a good idea to reduce
the resolution of volumes in the viewport.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D9040

Ref T73201.
This commit is contained in:
Jacques Lucke 2020-10-01 17:58:43 +02:00
parent 5b8503425a
commit 365bf103d1
Notes: blender-bot 2024-02-07 10:33:33 +01:00
Referenced by issue #73201, New volume object type
Referenced by pull request #117848, Fix: Simplify "Volume Resolution" not updating for non volume objects
Referenced by commit b56192b3bb, Fix: Simplify "Volume Resolution" not updating for non volume objects
10 changed files with 221 additions and 152 deletions

View File

@ -2082,6 +2082,7 @@ class CYCLES_RENDER_PT_simplify_viewport(CyclesButtonsPanel, Panel):
col.prop(rd, "simplify_child_particles", text="Child Particles")
col.prop(cscene, "texture_limit", text="Texture Limit")
col.prop(cscene, "ao_bounces", text="AO Bounces")
col.prop(rd, "simplify_volumes", text="Volume Resolution")
class CYCLES_RENDER_PT_simplify_render(CyclesButtonsPanel, Panel):

View File

@ -656,6 +656,9 @@ class RENDER_PT_simplify_viewport(RenderButtonsPanel, Panel):
col = flow.column()
col.prop(rd, "simplify_child_particles", text="Max Child Particles")
col = flow.column()
col.prop(rd, "simplify_volumes", text="Volume Resolution")
class RENDER_PT_simplify_render(RenderButtonsPanel, Panel):
bl_label = "Render"

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 6
#define BLENDER_FILE_SUBVERSION 7
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -34,19 +34,19 @@ struct VolumeGrid;
/* Dense Voxels */
bool BKE_volume_grid_dense_bounds(const struct Volume *volume,
typedef struct DenseFloatVolumeGrid {
VolumeGridType type;
int resolution[3];
float texture_to_object[4][4];
int channels;
float *voxels;
} DenseFloatVolumeGrid;
bool BKE_volume_grid_dense_floats(const struct Volume *volume,
struct VolumeGrid *volume_grid,
int64_t min[3],
int64_t max[3]);
void BKE_volume_grid_dense_transform_matrix(const struct VolumeGrid *volume_grid,
const int64_t min[3],
const int64_t max[3],
float matrix[4][4]);
void BKE_volume_grid_dense_voxels(const struct Volume *volume,
struct VolumeGrid *volume_grid,
const int64_t min[3],
const int64_t max[3],
float *voxels);
const float resolution_factor,
DenseFloatVolumeGrid *r_dense_grid);
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid);
/* Wireframe */

View File

@ -34,136 +34,181 @@
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/tools/Dense.h>
# include <openvdb/tools/GridTransformer.h>
#endif
/* Dense Voxels */
bool BKE_volume_grid_dense_bounds(const Volume *volume,
VolumeGrid *volume_grid,
int64_t min[3],
int64_t max[3])
{
#ifdef WITH_OPENVDB
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox();
if (!bbox.empty()) {
/* OpenVDB bbox is inclusive, so add 1 to convert. */
min[0] = bbox.min().x();
min[1] = bbox.min().y();
min[2] = bbox.min().z();
max[0] = bbox.max().x() + 1;
max[1] = bbox.max().y() + 1;
max[2] = bbox.max().z() + 1;
return true;
/**
* Returns a grid of the same type as the input, but with more/less resolution. If
* resolution_factor is 1/2, the resolution on each axis is halved. The transform of the returned
* grid is adjusted to match the original grid. */
template<typename GridType>
static typename GridType::Ptr create_grid_with_changed_resolution(
const openvdb::GridBase &old_grid, const float resolution_factor)
{
BLI_assert(resolution_factor > 0.0f);
BLI_assert(old_grid.isType<GridType>());
openvdb::Mat4R xform;
xform.setToScale(openvdb::Vec3d(resolution_factor));
openvdb::tools::GridTransformer transformer{xform};
typename GridType::Ptr new_grid = GridType::create();
transformer.transformGrid<openvdb::tools::BoxSampler>(static_cast<const GridType &>(old_grid),
*new_grid);
new_grid->transform() = old_grid.transform();
new_grid->transform().preScale(1.0f / resolution_factor);
return new_grid;
}
static openvdb::GridBase::Ptr create_grid_with_changed_resolution(
const VolumeGridType grid_type,
const openvdb::GridBase &old_grid,
const float resolution_factor)
{
switch (grid_type) {
case VOLUME_GRID_BOOLEAN:
return create_grid_with_changed_resolution<openvdb::BoolGrid>(old_grid, resolution_factor);
case VOLUME_GRID_FLOAT:
return create_grid_with_changed_resolution<openvdb::FloatGrid>(old_grid, resolution_factor);
case VOLUME_GRID_DOUBLE:
return create_grid_with_changed_resolution<openvdb::DoubleGrid>(old_grid, resolution_factor);
case VOLUME_GRID_INT:
return create_grid_with_changed_resolution<openvdb::Int32Grid>(old_grid, resolution_factor);
case VOLUME_GRID_INT64:
return create_grid_with_changed_resolution<openvdb::Int64Grid>(old_grid, resolution_factor);
case VOLUME_GRID_MASK:
return create_grid_with_changed_resolution<openvdb::MaskGrid>(old_grid, resolution_factor);
case VOLUME_GRID_VECTOR_FLOAT:
return create_grid_with_changed_resolution<openvdb::Vec3fGrid>(old_grid, resolution_factor);
case VOLUME_GRID_VECTOR_DOUBLE:
return create_grid_with_changed_resolution<openvdb::Vec3dGrid>(old_grid, resolution_factor);
case VOLUME_GRID_VECTOR_INT:
return create_grid_with_changed_resolution<openvdb::Vec3IGrid>(old_grid, resolution_factor);
case VOLUME_GRID_STRING:
case VOLUME_GRID_POINTS:
case VOLUME_GRID_UNKNOWN:
/* Can't do this. */
break;
}
#else
UNUSED_VARS(volume, volume_grid);
return {};
}
template<typename GridType, typename VoxelType>
static void extract_dense_voxels(const openvdb::GridBase &grid,
const openvdb::CoordBBox bbox,
VoxelType *r_voxels)
{
BLI_assert(grid.isType<GridType>());
openvdb::tools::Dense<VoxelType, openvdb::tools::LayoutXYZ> dense(bbox, r_voxels);
openvdb::tools::copyToDense(static_cast<const GridType &>(grid), dense);
}
static void extract_dense_float_voxels(const VolumeGridType grid_type,
const openvdb::GridBase &grid,
const openvdb::CoordBBox &bbox,
float *r_voxels)
{
switch (grid_type) {
case VOLUME_GRID_BOOLEAN:
return extract_dense_voxels<openvdb::BoolGrid, float>(grid, bbox, r_voxels);
case VOLUME_GRID_FLOAT:
return extract_dense_voxels<openvdb::FloatGrid, float>(grid, bbox, r_voxels);
case VOLUME_GRID_DOUBLE:
return extract_dense_voxels<openvdb::DoubleGrid, float>(grid, bbox, r_voxels);
case VOLUME_GRID_INT:
return extract_dense_voxels<openvdb::Int32Grid, float>(grid, bbox, r_voxels);
case VOLUME_GRID_INT64:
return extract_dense_voxels<openvdb::Int64Grid, float>(grid, bbox, r_voxels);
case VOLUME_GRID_MASK:
return extract_dense_voxels<openvdb::MaskGrid, float>(grid, bbox, r_voxels);
case VOLUME_GRID_VECTOR_FLOAT:
return extract_dense_voxels<openvdb::Vec3fGrid, openvdb::Vec3f>(
grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
case VOLUME_GRID_VECTOR_DOUBLE:
return extract_dense_voxels<openvdb::Vec3dGrid, openvdb::Vec3f>(
grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
case VOLUME_GRID_VECTOR_INT:
return extract_dense_voxels<openvdb::Vec3IGrid, openvdb::Vec3f>(
grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels));
case VOLUME_GRID_STRING:
case VOLUME_GRID_POINTS:
case VOLUME_GRID_UNKNOWN:
/* Zero channels to copy. */
break;
}
return;
}
static void create_texture_to_object_matrix(const openvdb::Mat4d &grid_transform,
const openvdb::CoordBBox &bbox,
float r_texture_to_object[4][4])
{
float index_to_object[4][4];
memcpy(index_to_object, openvdb::Mat4s(grid_transform).asPointer(), sizeof(index_to_object));
float texture_to_index[4][4];
const openvdb::Vec3f loc = bbox.min().asVec3s();
const openvdb::Vec3f size = bbox.dim().asVec3s();
size_to_mat4(texture_to_index, size.asV());
copy_v3_v3(texture_to_index[3], loc.asV());
mul_m4_m4m4(r_texture_to_object, index_to_object, texture_to_index);
}
#endif
min[0] = 0;
min[1] = 0;
min[2] = 0;
max[0] = 0;
max[1] = 0;
max[2] = 0;
bool BKE_volume_grid_dense_floats(const Volume *volume,
VolumeGrid *volume_grid,
const float resolution_factor,
DenseFloatVolumeGrid *r_dense_grid)
{
#ifdef WITH_OPENVDB
const VolumeGridType grid_type = BKE_volume_grid_type(volume_grid);
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
if (resolution_factor != 1.0f) {
grid = create_grid_with_changed_resolution(grid_type, *grid, resolution_factor);
}
const openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox();
if (bbox.empty()) {
return false;
}
const openvdb::Vec3i resolution = bbox.dim().asVec3i();
const int64_t num_voxels = static_cast<int64_t>(resolution[0]) *
static_cast<int64_t>(resolution[1]) *
static_cast<int64_t>(resolution[2]);
const int channels = BKE_volume_grid_channels(volume_grid);
const int elem_size = sizeof(float) * channels;
float *voxels = static_cast<float *>(MEM_malloc_arrayN(num_voxels, elem_size, __func__));
if (voxels == NULL) {
return false;
}
extract_dense_float_voxels(grid_type, *grid, bbox, voxels);
create_texture_to_object_matrix(grid->transform().baseMap()->getAffineMap()->getMat4(),
bbox,
r_dense_grid->texture_to_object);
r_dense_grid->voxels = voxels;
r_dense_grid->channels = channels;
copy_v3_v3_int(r_dense_grid->resolution, resolution.asV());
return true;
#endif
UNUSED_VARS(volume, volume_grid, resolution_factor, r_dense_grid);
return false;
}
/* Transform matrix from unit cube to object space, for 3D texture sampling. */
void BKE_volume_grid_dense_transform_matrix(const VolumeGrid *volume_grid,
const int64_t min[3],
const int64_t max[3],
float mat[4][4])
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid)
{
#ifdef WITH_OPENVDB
float index_to_world[4][4];
BKE_volume_grid_transform_matrix(volume_grid, index_to_world);
float texture_to_index[4][4];
float loc[3] = {(float)min[0], (float)min[1], (float)min[2]};
float size[3] = {(float)(max[0] - min[0]), (float)(max[1] - min[1]), (float)(max[2] - min[2])};
size_to_mat4(texture_to_index, size);
copy_v3_v3(texture_to_index[3], loc);
mul_m4_m4m4(mat, index_to_world, texture_to_index);
#else
UNUSED_VARS(volume_grid, min, max);
unit_m4(mat);
#endif
}
void BKE_volume_grid_dense_voxels(const Volume *volume,
VolumeGrid *volume_grid,
const int64_t min[3],
const int64_t max[3],
float *voxels)
{
#ifdef WITH_OPENVDB
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
/* Convert to OpenVDB inclusive bbox with -1. */
openvdb::CoordBBox bbox(min[0], min[1], min[2], max[0] - 1, max[1] - 1, max[2] - 1);
switch (BKE_volume_grid_type(volume_grid)) {
case VOLUME_GRID_BOOLEAN: {
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense);
break;
}
case VOLUME_GRID_FLOAT: {
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense);
break;
}
case VOLUME_GRID_DOUBLE: {
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense);
break;
}
case VOLUME_GRID_INT: {
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense);
break;
}
case VOLUME_GRID_INT64: {
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense);
break;
}
case VOLUME_GRID_MASK: {
openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense);
break;
}
case VOLUME_GRID_VECTOR_FLOAT: {
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
bbox, (openvdb::Vec3f *)voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense);
break;
}
case VOLUME_GRID_VECTOR_DOUBLE: {
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
bbox, (openvdb::Vec3f *)voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense);
break;
}
case VOLUME_GRID_VECTOR_INT: {
openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
bbox, (openvdb::Vec3f *)voxels);
openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense);
break;
}
case VOLUME_GRID_STRING:
case VOLUME_GRID_POINTS:
case VOLUME_GRID_UNKNOWN: {
/* Zero channels to copy. */
break;
}
if (dense_grid->voxels != NULL) {
MEM_freeN(dense_grid->voxels);
}
#else
UNUSED_VARS(volume, volume_grid, min, max, voxels);
#endif
}
/* Wireframe */

View File

@ -762,6 +762,12 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 291, 7)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->r.simplify_volumes = 1.0f;
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -42,6 +42,8 @@
#include "GPU_batch.h"
#include "GPU_texture.h"
#include "DEG_depsgraph_query.h"
#include "DRW_render.h"
#include "draw_cache.h" /* own include */
@ -260,6 +262,8 @@ static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
VolumeGrid *grid,
VolumeBatchCache *cache)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const char *name = BKE_volume_grid_name(grid);
/* Return cached grid. */
@ -289,35 +293,32 @@ static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
const bool was_loaded = BKE_volume_grid_is_loaded(grid);
BKE_volume_grid_load(volume, grid);
/* Compute dense voxel grid size. */
int64_t dense_min[3], dense_max[3], resolution[3] = {0};
if (BKE_volume_grid_dense_bounds(volume, grid, dense_min, dense_max)) {
resolution[0] = dense_max[0] - dense_min[0];
resolution[1] = dense_max[1] - dense_min[1];
resolution[2] = dense_max[2] - dense_min[2];
float resolution_factor = 1.0f;
if (DEG_get_mode(draw_ctx->depsgraph) != DAG_EVAL_RENDER) {
if (draw_ctx->scene->r.mode & R_SIMPLIFY) {
resolution_factor = draw_ctx->scene->r.simplify_volumes;
}
}
if (resolution_factor == 0.0f) {
return cache_grid;
}
size_t num_voxels = resolution[0] * resolution[1] * resolution[2];
size_t elem_size = sizeof(float) * channels;
/* Allocate and load voxels. */
float *voxels = (num_voxels > 0) ? MEM_malloc_arrayN(num_voxels, elem_size, __func__) : NULL;
if (voxels != NULL) {
BKE_volume_grid_dense_voxels(volume, grid, dense_min, dense_max, voxels);
DenseFloatVolumeGrid dense_grid;
if (BKE_volume_grid_dense_floats(volume, grid, resolution_factor, &dense_grid)) {
copy_m4_m4(cache_grid->texture_to_object, dense_grid.texture_to_object);
invert_m4_m4(cache_grid->object_to_texture, dense_grid.texture_to_object);
/* Create GPU texture. */
eGPUTextureFormat format = (channels == 3) ? GPU_RGB16F : GPU_R16F;
cache_grid->texture = GPU_texture_create_3d(
"volume_grid", UNPACK3(resolution), 1, format, GPU_DATA_FLOAT, voxels);
cache_grid->texture = GPU_texture_create_3d("volume_grid",
UNPACK3(dense_grid.resolution),
1,
format,
GPU_DATA_FLOAT,
dense_grid.voxels);
GPU_texture_swizzle_set(cache_grid->texture, (channels == 3) ? "rgb1" : "rrr1");
GPU_texture_wrap_mode(cache_grid->texture, false, false);
MEM_freeN(voxels);
/* Compute transform matrices. */
BKE_volume_grid_dense_transform_matrix(
grid, dense_min, dense_max, cache_grid->texture_to_object);
invert_m4_m4(cache_grid->object_to_texture, cache_grid->texture_to_object);
BKE_volume_dense_float_grid_clear(&dense_grid);
}
/* Free grid from memory if it wasn't previously loaded. */

View File

@ -128,6 +128,7 @@
\
.simplify_subsurf = 6, \
.simplify_particles = 1.0f, \
.simplify_volumes = 1.0f, \
\
.border.xmin = 0.0f, \
.border.ymin = 0.0f, \

View File

@ -731,7 +731,7 @@ typedef struct RenderData {
char seq_rend_type;
/** Flag use for sequence render/draw. */
char seq_flag;
char _pad5[7];
char _pad5[3];
/* render simplify */
short simplify_subsurf;
@ -739,6 +739,7 @@ typedef struct RenderData {
short simplify_gpencil;
float simplify_particles;
float simplify_particles_render;
float simplify_volumes;
/* Freestyle line thickness options */
int line_thickness_mode;

View File

@ -44,6 +44,7 @@
#include "BKE_armature.h"
#include "BKE_editmesh.h"
#include "BKE_paint.h"
#include "BKE_volume.h"
#include "ED_gpencil.h"
#include "ED_object.h"
@ -1888,6 +1889,10 @@ static void object_simplify_update(Object *ob)
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
if (ob->type == OB_VOLUME) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
}
static void rna_Scene_use_simplify_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
@ -6473,6 +6478,12 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop, "Simplify Child Particles", "Global child particles percentage during rendering");
RNA_def_property_update(prop, 0, "rna_Scene_simplify_update");
prop = RNA_def_property(srna, "simplify_volumes", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0, 1.0f);
RNA_def_property_ui_text(
prop, "Simplify Volumes", "Resolution percentage of volume objects in viewport");
RNA_def_property_update(prop, 0, "rna_Scene_simplify_update");
/* Grease Pencil - Simplify Options */
prop = RNA_def_property(srna, "simplify_gpencil", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_ENABLE);