Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brecht@blender.org>2021-12-01 19:30:46 +0300
committerBrecht Van Lommel <brecht@blender.org>2021-12-16 22:54:04 +0300
commit35b1e9fc3acd6db565e7e54252a4a4152d8343d9 (patch)
tree8599df3b2be192d1fe19d30a8afb7e8d8729499e /intern/cycles/device/optix/device_impl.cpp
parent2229179faa44e2c685b4eabd0c51d4c7d5c1f193 (diff)
Cycles: pointcloud rendering
This add support for rendering of the point cloud object in Blender, as a native geometry type in Cycles that is more memory and time efficient than instancing sphere meshes. This can be useful for rendering sand, water splashes, particles, motion graphics, etc. Points are currently always rendered as spheres, with backface culling. More shapes are likely to be added later, but this is the most important one and can be customized with shaders. For CPU rendering the Embree primitive is used, for GPU there is our own intersection code. Motion blur is suppored. Volumes inside points are not currently supported. Implemented with help from: * Kévin Dietrich: Alembic procedural integration * Patrick Mourse: OptiX integration * Josh Whelchel: update for cycles-x changes Ref T92573 Differential Revision: https://developer.blender.org/D9887
Diffstat (limited to 'intern/cycles/device/optix/device_impl.cpp')
-rw-r--r--intern/cycles/device/optix/device_impl.cpp120
1 files changed, 119 insertions, 1 deletions
diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp
index 11786f4a6e2..38cc3330ebd 100644
--- a/intern/cycles/device/optix/device_impl.cpp
+++ b/intern/cycles/device/optix/device_impl.cpp
@@ -28,6 +28,7 @@
# include "scene/mesh.h"
# include "scene/object.h"
# include "scene/pass.h"
+# include "scene/pointcloud.h"
# include "scene/scene.h"
# include "util/debug.h"
@@ -242,6 +243,9 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
else
pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM;
}
+ if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
+ pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM;
+ }
/* Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds
* This is necessary since objects may be reported to have motion if the Vector pass is
@@ -372,6 +376,18 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
}
}
+ /* Pointclouds */
+ if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
+ group_descs[PG_HITD_POINTCLOUD] = group_descs[PG_HITD];
+ group_descs[PG_HITD_POINTCLOUD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
+ group_descs[PG_HITD_POINTCLOUD].hitgroup.moduleIS = optix_module;
+ group_descs[PG_HITD_POINTCLOUD].hitgroup.entryFunctionNameIS = "__intersection__point";
+ group_descs[PG_HITS_POINTCLOUD] = group_descs[PG_HITS];
+ group_descs[PG_HITS_POINTCLOUD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
+ group_descs[PG_HITS_POINTCLOUD].hitgroup.moduleIS = optix_module;
+ group_descs[PG_HITS_POINTCLOUD].hitgroup.entryFunctionNameIS = "__intersection__point";
+ }
+
if (kernel_features & (KERNEL_FEATURE_SUBSURFACE | KERNEL_FEATURE_NODE_RAYTRACE)) {
/* Add hit group for local intersections. */
group_descs[PG_HITL].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
@@ -419,6 +435,10 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH);
trace_css = std::max(trace_css,
stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH);
+ trace_css = std::max(
+ trace_css, stack_size[PG_HITD_POINTCLOUD].cssIS + stack_size[PG_HITD_POINTCLOUD].cssAH);
+ trace_css = std::max(
+ trace_css, stack_size[PG_HITS_POINTCLOUD].cssIS + stack_size[PG_HITS_POINTCLOUD].cssAH);
OptixPipelineLinkOptions link_options = {};
link_options.maxTraceDepth = 1;
@@ -444,6 +464,10 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
pipeline_groups.push_back(groups[PG_HITD_MOTION]);
pipeline_groups.push_back(groups[PG_HITS_MOTION]);
}
+ if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
+ pipeline_groups.push_back(groups[PG_HITD_POINTCLOUD]);
+ pipeline_groups.push_back(groups[PG_HITS_POINTCLOUD]);
+ }
pipeline_groups.push_back(groups[PG_CALL_SVM_AO]);
pipeline_groups.push_back(groups[PG_CALL_SVM_BEVEL]);
@@ -483,6 +507,10 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
pipeline_groups.push_back(groups[PG_HITD_MOTION]);
pipeline_groups.push_back(groups[PG_HITS_MOTION]);
}
+ if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
+ pipeline_groups.push_back(groups[PG_HITD_POINTCLOUD]);
+ pipeline_groups.push_back(groups[PG_HITS_POINTCLOUD]);
+ }
optix_assert(optixPipelineCreate(context,
&pipeline_options,
@@ -1378,6 +1406,86 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
progress.set_error("Failed to build OptiX acceleration structure");
}
}
+ else if (geom->geometry_type == Geometry::POINTCLOUD) {
+ /* Build BLAS for points primitives. */
+ PointCloud *const pointcloud = static_cast<PointCloud *const>(geom);
+ const size_t num_points = pointcloud->num_points();
+ if (num_points == 0) {
+ return;
+ }
+
+ size_t num_motion_steps = 1;
+ Attribute *motion_points = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+ if (motion_blur && pointcloud->get_use_motion_blur() && motion_points) {
+ num_motion_steps = pointcloud->get_motion_steps();
+ }
+
+ device_vector<OptixAabb> aabb_data(this, "optix temp aabb data", MEM_READ_ONLY);
+ aabb_data.alloc(num_points * num_motion_steps);
+
+ /* Get AABBs for each motion step. */
+ for (size_t step = 0; step < num_motion_steps; ++step) {
+ /* The center step for motion vertices is not stored in the attribute. */
+ const float3 *points = pointcloud->get_points().data();
+ const float *radius = pointcloud->get_radius().data();
+ size_t center_step = (num_motion_steps - 1) / 2;
+ if (step != center_step) {
+ size_t attr_offset = (step > center_step) ? step - 1 : step;
+ /* Technically this is a float4 array, but sizeof(float3) == sizeof(float4). */
+ points = motion_points->data_float3() + attr_offset * num_points;
+ }
+
+ for (size_t i = 0; i < num_points; ++i) {
+ const PointCloud::Point point = pointcloud->get_point(i);
+ BoundBox bounds = BoundBox::empty;
+ point.bounds_grow(points, radius, bounds);
+
+ const size_t index = step * num_points + i;
+ aabb_data[index].minX = bounds.min.x;
+ aabb_data[index].minY = bounds.min.y;
+ aabb_data[index].minZ = bounds.min.z;
+ aabb_data[index].maxX = bounds.max.x;
+ aabb_data[index].maxY = bounds.max.y;
+ aabb_data[index].maxZ = bounds.max.z;
+ }
+ }
+
+ /* Upload AABB data to GPU. */
+ aabb_data.copy_to_device();
+
+ vector<device_ptr> aabb_ptrs;
+ aabb_ptrs.reserve(num_motion_steps);
+ for (size_t step = 0; step < num_motion_steps; ++step) {
+ aabb_ptrs.push_back(aabb_data.device_pointer + step * num_points * sizeof(OptixAabb));
+ }
+
+ /* Disable visibility test any-hit program, since it is already checked during
+ * intersection. Those trace calls that require anyhit can force it with a ray flag.
+ * For those, force a single any-hit call, so shadow record-all behavior works correctly. */
+ unsigned int build_flags = OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT |
+ OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;
+ OptixBuildInput build_input = {};
+ build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES;
+# if OPTIX_ABI_VERSION < 23
+ build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data();
+ build_input.aabbArray.numPrimitives = num_points;
+ build_input.aabbArray.strideInBytes = sizeof(OptixAabb);
+ build_input.aabbArray.flags = &build_flags;
+ build_input.aabbArray.numSbtRecords = 1;
+ build_input.aabbArray.primitiveIndexOffset = pointcloud->prim_offset;
+# else
+ build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data();
+ build_input.customPrimitiveArray.numPrimitives = num_points;
+ build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb);
+ build_input.customPrimitiveArray.flags = &build_flags;
+ build_input.customPrimitiveArray.numSbtRecords = 1;
+ build_input.customPrimitiveArray.primitiveIndexOffset = pointcloud->prim_offset;
+# endif
+
+ if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
+ progress.set_error("Failed to build OptiX acceleration structure");
+ }
+ }
}
else {
unsigned int num_instances = 0;
@@ -1461,12 +1569,22 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
}
}
+ else if (ob->get_geometry()->geometry_type == Geometry::POINTCLOUD) {
+ /* Use the hit group that has an intersection program for point clouds. */
+ instance.sbtOffset = PG_HITD_POINTCLOUD - PG_HITD;
+
+ /* Also skip point clouds in local trace calls. */
+ instance.visibilityMask |= 4;
+ }
+
# if OPTIX_ABI_VERSION < 55
/* Cannot disable any-hit program for thick curves, since it needs to filter out end-caps. */
else
# endif
{
- /* Can disable __anyhit__kernel_optix_visibility_test by default.
+ /* Can disable __anyhit__kernel_optix_visibility_test by default (except for thick curves,
+ * since it needs to filter out end-caps there).
+
* It is enabled where necessary (visibility mask exceeds 8 bits or the other any-hit
* programs like __anyhit__kernel_optix_shadow_all_hit) via OPTIX_RAY_FLAG_ENFORCE_ANYHIT.
*/