Cycles: Refactor lights to be objects

This is an intermediate steps towards making lights actual geometry.
Light is now a subclass of Geometry, which simplifies some code.

The geometry is not added to the BVH yet, which would be the next
step and improve light intersection performance with many lights.

This makes object attributes work on lights.

Co-authored-by: Lukas Stockner <lukas@lukasstockner.de>
Pull Request: https://projects.blender.org/blender/blender/pulls/134846
This commit is contained in:
Brecht Van Lommel
2025-02-24 23:44:14 +01:00
committed by Lukas Stockner
parent 92c0eb5e66
commit e813e46327
57 changed files with 425 additions and 613 deletions

View File

@@ -4,6 +4,7 @@
#include "scene/curves.h"
#include "scene/hair.h"
#include "scene/light.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
@@ -18,6 +19,10 @@ CCL_NAMESPACE_BEGIN
static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair)
{
if (b_ob_info.object_data.is_a(&RNA_Light)) {
return Geometry::LIGHT;
}
if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) {
return Geometry::HAIR;
}
@@ -38,12 +43,17 @@ static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_parti
array<Node *> BlenderSync::find_used_shaders(BL::Object &b_ob)
{
array<Node *> used_shaders;
if (b_ob.type() == BL::Object::type_LIGHT) {
find_shader(b_ob.data(), used_shaders, scene->default_light);
return used_shaders;
}
BL::Material material_override = view_layer.material_override;
Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume :
scene->default_surface;
array<Node *> used_shaders;
for (BL::MaterialSlot &b_slot : b_ob.material_slots) {
if (material_override) {
find_shader(material_override, used_shaders, default_shader);
@@ -95,7 +105,10 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
bool sync = true;
if (geom == nullptr) {
/* Add new geometry if it did not exist yet. */
if (geom_type == Geometry::HAIR) {
if (geom_type == Geometry::LIGHT) {
geom = scene->create_node<Light>();
}
else if (geom_type == Geometry::HAIR) {
geom = scene->create_node<Hair>();
}
else if (geom_type == Geometry::VOLUME) {
@@ -115,6 +128,11 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
}
if (!sync) {
/* Need to determine this every sync. */
if (geom->is_light() && static_cast<const Light *>(geom)->get_is_portal()) {
world_use_portal = true;
}
/* If transform was applied to geometry, need full update. */
if (object_updated && geom->transform_applied) {
;
@@ -156,7 +174,11 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
progress.set_sync_status("Synchronizing object", b_ob_info.real_object.name());
if (geom_type == Geometry::HAIR) {
if (geom_type == Geometry::LIGHT) {
Light *light = static_cast<Light *>(geom);
sync_light(b_depsgraph, b_ob_info, light);
}
else if (geom_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair(b_depsgraph, b_ob_info, hair);
}
@@ -209,6 +231,11 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
return;
}
/* Nothing to do for lights. */
if (geom->is_light()) {
return;
}
/* If the geometry already has motion blur from a velocity attribute, don't
* set the geometry motion steps again.
*

View File

@@ -4,42 +4,16 @@
#include "scene/light.h"
#include "blender/light_linking.h"
#include "blender/sync.h"
#include "blender/util.h"
#include "util/hash.h"
#include "scene/object.h"
CCL_NAMESPACE_BEGIN
void BlenderSync::sync_light(BL::Object &b_parent,
int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
BObjectInfo &b_ob_info,
const int random_id,
Transform &tfm,
bool *use_portal)
void BlenderSync::sync_light(BL::Depsgraph /*b_depsgraph*/, BObjectInfo &b_ob_info, Light *light)
{
/* test if we need to sync */
const ObjectKey key(b_parent, persistent_id, b_ob_info.real_object, false);
BL::Light b_light(b_ob_info.object_data);
Light *light = light_map.find(key);
/* Check if the transform was modified, in case a linked collection is moved we do not get a
* specific depsgraph update (#88515). This also mimics the behavior for Objects. */
const bool tfm_updated = (light && light->get_tfm() != tfm);
/* Update if either object or light data changed. */
if (!light_map.add_or_update(&light, b_ob_info.real_object, b_parent, key) && !tfm_updated) {
Shader *shader;
if (!shader_map.add_or_update(&shader, b_light)) {
if (light->get_is_portal()) {
*use_portal = true;
}
return;
}
}
light->name = b_light.name().c_str();
/* type */
@@ -104,14 +78,6 @@ void BlenderSync::sync_light(BL::Object &b_parent,
const float3 strength = get_float3(b_light.color()) * BL::PointLight(b_light).energy();
light->set_strength(strength);
/* location and (inverted!) direction */
light->set_tfm(tfm);
/* shader */
array<Node *> used_shaders;
find_shader(b_light, used_shaders, scene->default_light);
light->set_shader(static_cast<Shader *>(used_shaders[0]));
/* shadow */
PointerRNA clight = RNA_pointer_get(&b_light.ptr, "cycles");
light->set_cast_shadow(b_light.use_shadow());
@@ -122,13 +88,6 @@ void BlenderSync::sync_light(BL::Object &b_parent,
light->set_max_bounces(get_int(clight, "max_bounces"));
if (b_ob_info.real_object != b_ob_info.iter_object) {
light->set_random_id(random_id);
}
else {
light->set_random_id(hash_uint2(hash_string(b_ob_info.real_object.name().c_str()), 0));
}
if (light->get_light_type() == LIGHT_AREA) {
light->set_is_portal(get_boolean(clight, "is_portal"));
}
@@ -137,7 +96,7 @@ void BlenderSync::sync_light(BL::Object &b_parent,
}
if (light->get_is_portal()) {
*use_portal = true;
world_use_portal = true;
}
/* visibility */
@@ -149,22 +108,11 @@ void BlenderSync::sync_light(BL::Object &b_parent,
light->set_use_scatter((visibility & PATH_RAY_VOLUME_SCATTER) != 0);
light->set_is_shadow_catcher(b_ob_info.real_object.is_shadow_catcher());
/* Light group and linking. */
string lightgroup = b_ob_info.real_object.lightgroup();
if (lightgroup.empty()) {
lightgroup = b_parent.lightgroup();
}
light->set_lightgroup(ustring(lightgroup));
light->set_light_set_membership(
BlenderLightLink::get_light_set_membership(PointerRNA_NULL, b_ob_info.real_object));
light->set_shadow_set_membership(
BlenderLightLink::get_shadow_set_membership(PointerRNA_NULL, b_ob_info.real_object));
/* tag */
light->tag_update(scene);
}
void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d)
{
BL::World b_world = view_layer.world_override ? view_layer.world_override : b_scene.world();
@@ -176,14 +124,29 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC);
const bool sample_as_light = (sampling_method != SAMPLING_NONE);
if (sample_as_light || use_portal) {
/* test if we need to sync */
Light *light;
const ObjectKey key(b_world, nullptr, b_world, false);
if (sample_as_light || world_use_portal) {
/* Create object. */
Object *object;
const ObjectKey object_key(b_world, nullptr, b_world, false);
bool update = object_map.add_or_update(&object, b_world, b_world, object_key);
/* Create geometry. */
const GeometryKey geom_key{b_world.ptr.data, Geometry::LIGHT};
Geometry *geom = geometry_map.find(geom_key);
if (geom) {
update |= geometry_map.update(geom, b_world);
}
else {
geom = scene->create_node<Light>();
geometry_map.add(geom_key, geom);
object->set_geometry(geom);
update = true;
}
if (update || world_recalc || b_world.ptr.data != world_map) {
/* Initialize light geometry. */
Light *light = static_cast<Light *>(geom);
if (light_map.add_or_update(&light, b_world, b_world, key) || world_recalc ||
b_world.ptr.data != world_map)
{
light->set_light_type(LIGHT_BACKGROUND);
if (sampling_method == SAMPLING_MANUAL) {
light->set_map_resolution(get_int(cworld, "sample_map_resolution"));
@@ -191,7 +154,9 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
else {
light->set_map_resolution(0);
}
light->set_shader(scene->default_background);
array<Node *> used_shaders;
used_shaders.push_back_slow(scene->default_background);
light->set_used_shaders(used_shaders);
light->set_use_mis(sample_as_light);
light->set_max_bounces(get_int(cworld, "max_bounces"));
@@ -202,7 +167,7 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
light->set_use_caustics(get_boolean(cworld, "is_caustics_light"));
light->tag_update(scene);
light_map.set_recalc(b_world);
geometry_map.set_recalc(b_world);
}
}
}

View File

@@ -62,9 +62,9 @@ bool BlenderSync::object_is_geometry(BObjectInfo &b_ob_info)
const BL::Object::type_enum type = b_ob_info.iter_object.type();
if (type == BL::Object::type_VOLUME || type == BL::Object::type_CURVES ||
type == BL::Object::type_POINTCLOUD)
type == BL::Object::type_POINTCLOUD || type == BL::Object::type_LIGHT)
{
/* Will be exported attached to mesh. */
/* Will be exported as geometry. */
return true;
}
@@ -152,7 +152,6 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
bool use_particle_hair,
bool show_lights,
BlenderObjectCulling &culling,
bool *use_portal,
TaskPool *geom_task_pool)
{
const bool is_instance = b_instance.is_instance();
@@ -173,36 +172,18 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
}
/* light is handled separately */
if (!motion && object_is_light(b_ob)) {
if (!show_lights) {
return nullptr;
}
/* TODO: don't use lights for excluded layers used as mask layer,
* when dynamic overrides are back. */
#if 0
if (!((layer_flag & view_layer.holdout_layer) && (layer_flag & view_layer.exclude_layer)))
#endif
{
sync_light(b_parent,
persistent_id,
b_ob_info,
is_instance ? b_instance.random_id() : 0,
tfm,
use_portal);
}
return nullptr;
}
/* only interested in object that we can create geometry from */
if (!object_is_geometry(b_ob_info)) {
return nullptr;
}
/* Perform object culling. */
if (culling.test(scene, b_ob, tfm)) {
if (object_is_light(b_ob)) {
if (!show_lights) {
return nullptr;
}
}
else if (culling.test(scene, b_ob, tfm)) {
return nullptr;
}
@@ -544,7 +525,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
if (!motion) {
/* prepare for sync */
light_map.pre_sync();
geometry_map.pre_sync();
object_map.pre_sync();
procedural_map.pre_sync();
@@ -555,6 +535,8 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
geometry_motion_synced.clear();
}
world_use_portal = false;
if (!motion) {
/* Object to geometry instance mapping is built for the reference time, as other
* times just look up the corresponding geometry. */
@@ -566,7 +548,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
/* object loop */
bool cancel = false;
bool use_portal = false;
const bool show_lights = BlenderViewportParameters(b_v3d, use_developer_ui).use_scene_lights;
BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
@@ -623,7 +604,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
false,
show_lights,
culling,
&use_portal,
sync_hair ? nullptr : &geom_task_pool);
}
}
@@ -637,7 +617,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
true,
show_lights,
culling,
&use_portal,
&geom_task_pool);
}
@@ -649,12 +628,12 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
progress.set_sync_status("");
if (!cancel && !motion) {
sync_background_light(b_v3d, use_portal);
/* After object for world_use_portal. */
sync_background_light(b_v3d);
/* Handle removed data and modified pointers, as this may free memory, delete Nodes in the
* right order to ensure that dependent data is freed after their users. Objects should be
* freed before particle systems and geometries. */
light_map.post_sync();
object_map.post_sync();
geometry_map.post_sync();
particle_system_map.post_sync();

View File

@@ -30,7 +30,9 @@ using ProxyMap = map<string, ConvertNode *>;
/* Find */
void BlenderSync::find_shader(BL::ID &id, array<Node *> &used_shaders, Shader *default_shader)
void BlenderSync::find_shader(const BL::ID &id,
array<Node *> &used_shaders,
Shader *default_shader)
{
Shader *synced_shader = (id) ? shader_map.find(id) : nullptr;
Shader *shader = (synced_shader) ? synced_shader : default_shader;

View File

@@ -53,7 +53,6 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
object_map(scene),
procedural_map(scene),
geometry_map(scene),
light_map(scene),
particle_system_map(scene),
world_map(nullptr),
world_recalc(false),
@@ -206,11 +205,11 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
else if (is_light) {
if (b_update.is_updated_transform() || b_update.is_updated_shading()) {
object_map.set_recalc(b_ob);
light_map.set_recalc(b_ob);
geometry_map.set_recalc(b_ob);
}
if (updated_geometry) {
light_map.set_recalc(b_ob);
geometry_map.set_recalc(b_ob);
}
}
}

View File

@@ -144,7 +144,6 @@ class BlenderSync {
bool use_particle_hair,
bool show_lights,
BlenderObjectCulling &culling,
bool *use_portal,
TaskPool *geom_task_pool);
void sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob, Object *object);
@@ -206,13 +205,8 @@ class BlenderSync {
TaskPool *task_pool);
/* Light */
void sync_light(BL::Object &b_parent,
int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
BObjectInfo &b_ob_info,
const int random_id,
Transform &tfm,
bool *use_portal);
void sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal);
void sync_light(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Light *light);
void sync_background_light(BL::SpaceView3D &b_v3d);
/* Particles */
bool sync_dupli_particle(BL::Object &b_ob,
@@ -223,7 +217,7 @@ class BlenderSync {
void sync_images();
/* util */
void find_shader(BL::ID &id, array<Node *> &used_shaders, Shader *default_shader);
void find_shader(const BL::ID &id, array<Node *> &used_shaders, Shader *default_shader);
bool BKE_object_is_modified(BL::Object &b_ob);
bool object_is_geometry(BObjectInfo &b_ob_info);
bool object_can_have_geometry(BL::Object &b_ob);
@@ -242,7 +236,6 @@ class BlenderSync {
id_map<ObjectKey, Object> object_map;
id_map<void *, Procedural> procedural_map;
id_map<GeometryKey, Geometry> geometry_map;
id_map<ObjectKey, Light> light_map;
id_map<ParticleSystemKey, ParticleSystem> particle_system_map;
set<Geometry *> geometry_synced;
set<Geometry *> geometry_motion_synced;
@@ -252,6 +245,7 @@ class BlenderSync {
set<float> motion_times;
void *world_map;
bool world_recalc;
bool world_use_portal = false;
BlenderViewportParameters viewport_parameters;
Scene *scene;

View File

@@ -6,11 +6,13 @@
#include "hydra/light.h"
#include "hydra/session.h"
#include "scene/light.h"
#include "scene/object.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
#include "util/hash.h"
#include "util/transform.h"
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/usd/sdf/assetPath.h>
@@ -64,7 +66,7 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate,
sceneDelegate->GetLightParamValue(id, HdTokens->transform)
.Get<GfMatrix4d>());
#endif
_light->set_tfm(tfm);
_object->set_tfm(tfm);
}
if (*dirtyBits & DirtyBits::DirtyParams) {
@@ -175,8 +177,8 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate,
PopulateShaderGraph(sceneDelegate);
}
// Need to update shader graph when transform changes in case transform was baked into it
else if (_light->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight ||
_light->get_shader()->has_surface_spatial_varying))
else if (_object->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight ||
_light->get_shader()->has_surface_spatial_varying))
{
PopulateShaderGraph(sceneDelegate);
}
@@ -266,7 +268,7 @@ void HdCyclesLight::PopulateShaderGraph(HdSceneDelegate *sceneDelegate)
}
TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
coordNode->set_ob_tfm(_light->get_tfm());
coordNode->set_ob_tfm(_object->get_tfm());
coordNode->set_use_transform(true);
IESLightNode *iesNode = graph->create_node<IESLightNode>();
@@ -287,7 +289,7 @@ void HdCyclesLight::PopulateShaderGraph(HdSceneDelegate *sceneDelegate)
ImageSlotTextureNode *textureNode = nullptr;
if (_lightType == HdPrimTypeTokens->domeLight) {
Transform tfm = _light->get_tfm();
Transform tfm = _object->get_tfm();
transform_set_column(&tfm, 3, zero_float3()); // Remove translation
TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
@@ -352,9 +354,11 @@ void HdCyclesLight::Finalize(HdRenderParam *renderParam)
if (!keep_nodes) {
lock.scene->delete_node(_light);
lock.scene->delete_node(_object);
}
_light = nullptr;
_object = nullptr;
}
void HdCyclesLight::Initialize(HdRenderParam *renderParam)
@@ -365,10 +369,14 @@ void HdCyclesLight::Initialize(HdRenderParam *renderParam)
const SceneLock lock(renderParam);
_object = lock.scene->create_node<Object>();
_object->name = GetId().GetString();
_light = lock.scene->create_node<Light>();
_light->name = GetId().GetString();
_object->set_geometry(_light);
_light->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0));
_object->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0));
if (_lightType == HdPrimTypeTokens->domeLight) {
_light->set_light_type(LIGHT_BACKGROUND);
@@ -395,7 +403,9 @@ void HdCyclesLight::Initialize(HdRenderParam *renderParam)
_light->set_use_camera(false);
Shader *const shader = lock.scene->create_node<Shader>();
_light->set_shader(shader);
array<Node *> used_shaders;
used_shaders.push_back_slow(shader);
_light->set_used_shaders(used_shaders);
// Create default shader graph
PopulateShaderGraph(nullptr);

View File

@@ -29,6 +29,7 @@ class HdCyclesLight final : public PXR_NS::HdLight {
void PopulateShaderGraph(PXR_NS::HdSceneDelegate *sceneDelegate);
CCL_NS::Object *_object = nullptr;
CCL_NS::Light *_light = nullptr;
PXR_NS::TfToken _lightType;
};

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/session.h"
#include "scene/object.h"
#include "scene/shader.h"
// Have to include shader.h before background.h so that 'set_shader' uses the correct 'set'
// overload taking a 'Node *', rather than the one taking a 'bool'
@@ -99,7 +100,15 @@ void HdCyclesSession::UpdateScene()
// Update background depending on presence of a background light
if (scene->light_manager->need_update()) {
Light *background_light = nullptr;
for (Light *light : scene->lights) {
bool have_lights = false;
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
have_lights = true;
Light *light = static_cast<Light *>(object->get_geometry());
if (light->get_light_type() == LIGHT_BACKGROUND) {
background_light = light;
break;
@@ -115,7 +124,7 @@ void HdCyclesSession::UpdateScene()
for (ShaderNode *node : scene->default_background->graph->nodes) {
if (node->is_a(BackgroundNode::get_node_type())) {
BackgroundNode *bgNode = static_cast<BackgroundNode *>(node);
bgNode->set_color((scene->lights.empty()) ? make_float3(0.5f) : zero_float3());
bgNode->set_color((have_lights) ? zero_float3() : make_float3(0.5f));
}
}
}

View File

@@ -260,10 +260,6 @@ ccl_device_inline bool intersection_skip_self_local(const ccl_ray_data RaySelfPr
ccl_device_inline uint64_t
ray_get_shadow_set_membership(KernelGlobals kg, const ccl_ray_data RaySelfPrimitives &self)
{
if (self.light != LAMP_NONE) {
return kernel_data_fetch(lights, self.light).shadow_set_membership;
}
if (self.light_object != OBJECT_NONE) {
return kernel_data_fetch(objects, self.light_object).shadow_set_membership;
}

View File

@@ -85,9 +85,8 @@ ccl_device T curve_attribute(KernelGlobals kg,
}
# endif
if (desc.element & (ATTR_ELEMENT_CURVE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
const int offset = (desc.element == ATTR_ELEMENT_CURVE) ? desc.offset + sd->prim : desc.offset;
return attribute_data_fetch<T>(kg, offset);
if (desc.element == ATTR_ELEMENT_CURVE) {
return attribute_data_fetch<T>(kg, desc.offset + sd->prim);
}
return make_zero<T>();
}

View File

@@ -40,16 +40,6 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals kg,
return kernel_data_fetch(objects, object).tfm;
}
/* Lamp to world space transformation */
ccl_device_inline Transform lamp_fetch_transform(KernelGlobals kg, const int lamp, bool inverse)
{
if (inverse) {
return kernel_data_fetch(lights, lamp).itfm;
}
return kernel_data_fetch(lights, lamp).tfm;
}
/* Object to world space transformation for motion vectors */
ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals kg,
@@ -131,6 +121,13 @@ ccl_device_inline Transform object_get_inverse_transform(KernelGlobals kg,
return object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
#endif
}
ccl_device_inline Transform lamp_get_inverse_transform(KernelGlobals kg,
const ccl_global KernelLight *klight)
{
return object_fetch_transform(kg, klight->object_id, OBJECT_INVERSE_TRANSFORM);
}
/* Transform position from object to world space */
ccl_device_inline void object_position_transform(KernelGlobals kg,
@@ -173,7 +170,7 @@ ccl_device_inline void object_inverse_normal_transform(KernelGlobals kg,
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
if ((sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP)) {
if (sd->object != OBJECT_NONE) {
*N = safe_normalize(transform_direction_transposed_auto(&sd->ob_tfm_motion, *N));
}
return;
@@ -184,10 +181,6 @@ ccl_device_inline void object_inverse_normal_transform(KernelGlobals kg,
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
*N = safe_normalize(transform_direction_transposed(&tfm, *N));
}
else if (sd->type == PRIMITIVE_LAMP) {
const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false);
*N = safe_normalize(transform_direction_transposed(&tfm, *N));
}
}
/* Transform normal from object to world space */
@@ -207,10 +200,6 @@ ccl_device_inline void object_normal_transform(KernelGlobals kg,
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
*N = normalize(transform_direction_transposed(&tfm, *N));
}
else if (sd->type == PRIMITIVE_LAMP) {
const Transform tfm = lamp_fetch_transform(kg, sd->lamp, true);
*N = normalize(transform_direction_transposed(&tfm, *N));
}
}
ccl_device_inline bool object_negative_scale_applied(const int object_flag)
@@ -304,17 +293,6 @@ ccl_device_inline float object_pass_id(KernelGlobals kg, const int object)
return kernel_data_fetch(objects, object).pass_id;
}
/* Light-group of lamp. */
ccl_device_inline int lamp_lightgroup(KernelGlobals kg, const int lamp)
{
if (lamp == LAMP_NONE) {
return LIGHTGROUP_NONE;
}
return kernel_data_fetch(lights, lamp).lightgroup;
}
/* Light-group of object. */
ccl_device_inline int object_lightgroup(KernelGlobals kg, const int object)
@@ -326,17 +304,6 @@ ccl_device_inline int object_lightgroup(KernelGlobals kg, const int object)
return kernel_data_fetch(objects, object).lightgroup;
}
/* Per lamp random number for shader variation */
ccl_device_inline float lamp_random_number(KernelGlobals kg, const int lamp)
{
if (lamp == LAMP_NONE) {
return 0.0f;
}
return kernel_data_fetch(lights, lamp).random;
}
/* Per object random number for shader variation */
ccl_device_inline float object_random_number(KernelGlobals kg, const int object)

View File

@@ -36,6 +36,17 @@ ccl_device_forceinline T primitive_surface_attribute(KernelGlobals kg,
ccl_private T *dfdx,
ccl_private T *dfdy)
{
if (desc.element & (ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
if (dfdx) {
*dfdx = make_zero<T>();
}
if (dfdy) {
*dfdy = make_zero<T>();
}
return attribute_data_fetch<T>(kg, desc.offset);
}
if (sd->type & PRIMITIVE_TRIANGLE) {
if (subd_triangle_patch(kg, sd->prim) == ~0) {
return triangle_attribute<T>(kg, sd, desc, dfdx, dfdy);

View File

@@ -53,7 +53,6 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg,
sd->object = isect->object;
sd->object_flag = kernel_data_fetch(object_flag, sd->object);
sd->prim = isect->prim;
sd->lamp = LAMP_NONE;
sd->flag = 0;
/* Read matrices and time. */
@@ -139,8 +138,8 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
const float v,
const float t,
const float time,
bool object_space,
const int lamp)
const bool object_space,
const bool is_lamp)
{
/* vectors */
sd->P = P;
@@ -148,7 +147,7 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
sd->Ng = Ng;
sd->wi = I;
sd->shader = shader;
if (lamp != LAMP_NONE) {
if (is_lamp) {
sd->type = PRIMITIVE_LAMP;
}
else if (prim != PRIM_NONE) {
@@ -160,7 +159,6 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
/* primitive */
sd->object = object;
sd->lamp = LAMP_NONE;
/* Currently no access to bvh prim index for strand sd->prim. */
sd->prim = prim;
sd->u = u;
@@ -213,9 +211,6 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
}
}
else {
if (lamp != LAMP_NONE) {
sd->lamp = lamp;
}
#ifdef __DPDU__
sd->dPdu = zero_float3();
sd->dPdv = zero_float3();
@@ -278,7 +273,7 @@ ccl_device void shader_setup_from_displace(KernelGlobals kg,
0.0f,
0.5f,
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED),
LAMP_NONE);
false);
}
/* ShaderData setup for point on curve. */
@@ -293,7 +288,6 @@ ccl_device void shader_setup_from_curve(KernelGlobals kg,
{
/* Primitive */
sd->type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE_THICK, segment);
sd->lamp = LAMP_NONE;
sd->prim = prim;
sd->u = u;
sd->v = 0.0f;
@@ -381,7 +375,6 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals kg,
sd->ray_length = FLT_MAX;
sd->object = OBJECT_NONE;
sd->lamp = LAMP_NONE;
sd->prim = PRIM_NONE;
sd->type = PRIMITIVE_NONE;
sd->u = 0.0f;
@@ -424,7 +417,6 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg,
/* TODO: fill relevant fields for texture coordinates. */
sd->object = object;
sd->lamp = LAMP_NONE;
sd->prim = PRIM_NONE;
sd->type = PRIMITIVE_VOLUME;

View File

@@ -253,16 +253,6 @@ ccl_device_noinline T subd_triangle_attribute(KernelGlobals kg,
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) {
if (dfdx) {
*dfdx = make_zero<T>();
}
if (dfdy) {
*dfdy = make_zero<T>();
}
return attribute_data_fetch<T>(kg, desc.offset);
}
if (dfdx) {
*dfdx = make_zero<T>();

View File

@@ -275,9 +275,8 @@ ccl_device T triangle_attribute(KernelGlobals kg,
}
#endif
if (desc.element & (ATTR_ELEMENT_FACE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
const int offset = (desc.element == ATTR_ELEMENT_FACE) ? desc.offset + sd->prim : desc.offset;
return attribute_data_fetch<T>(kg, offset);
if (desc.element == ATTR_ELEMENT_FACE) {
return attribute_data_fetch<T>(kg, desc.offset + sd->prim);
}
return make_zero<T>();
}

View File

@@ -374,7 +374,6 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg,
ray.self.prim = last_isect_prim;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
bool hit = scene_intersect(kg, &ray, visibility, &isect);
/* TODO: remove this and do it in the various intersection functions instead. */

View File

@@ -189,7 +189,6 @@ ccl_device bool shadow_linking_intersect(KernelGlobals kg, IntegratorState state
ray.self.object = INTEGRATOR_STATE(state, isect, object);
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
Intersection isect ccl_optional_struct_init;
if (!shadow_linking_pick_light_intersection(kg, state, &ray, &isect)) {

View File

@@ -34,7 +34,6 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim);
volume_ray.self.light_object = OBJECT_NONE;
volume_ray.self.light_prim = PRIM_NONE;
volume_ray.self.light = LAMP_NONE;
/* Store to avoid global fetches on every intersection step. */
const uint volume_stack_size = kernel_data.volume_stack_size;
@@ -98,7 +97,6 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
volume_ray.self.prim = PRIM_NONE;
volume_ray.self.light_object = OBJECT_NONE;
volume_ray.self.light_prim = PRIM_NONE;
volume_ray.self.light = LAMP_NONE;
int stack_index = 0;
int enclosed_index = 0;

View File

@@ -422,7 +422,6 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg,
Ray projection_ray;
projection_ray.self.light_object = OBJECT_NONE;
projection_ray.self.light_prim = PRIM_NONE;
projection_ray.self.light = LAMP_NONE;
projection_ray.dP = differential_make_compact(sd->dP);
projection_ray.dD = differential_zero_compact();
projection_ray.tmin = 0.0f;
@@ -866,7 +865,6 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
Ray probe_ray;
probe_ray.self.light_object = ls->object;
probe_ray.self.light_prim = ls->prim;
probe_ray.self.light = ls->lamp;
probe_ray.tmin = 0.0f;
probe_ray.dP = differential_make_compact(sd->dP);
probe_ray.dD = differential_zero_compact();
@@ -915,7 +913,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
wi_len,
sd->time,
false,
LAMP_NONE);
false);
/* Set bounce info in case a light path node is used in the refractive interface
* shader graph. */
@@ -968,7 +966,6 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
probe_ray.self.prim = sd->prim;
probe_ray.self.light_object = ls->object;
probe_ray.self.light_prim = ls->prim;
probe_ray.self.light = ls->lamp;
probe_ray.P = sd->P;
probe_ray.tmin = 0.0f;
if (ls->t == FLT_MAX) {
@@ -1100,7 +1097,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
/* Distant or environment light. */
bool light_fixed_direction = (ls->t == FLT_MAX);
if (ls->type == LIGHT_AREA) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim);
if (klight->area.tan_half_spread == 0.0f) {
/* Area light with zero spread also has fixed direction. */
light_fixed_direction = true;

View File

@@ -138,15 +138,18 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
}
#endif
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
#ifdef __LIGHT_LINKING__
if (!light_link_light_match(kg, light_link_receiver_forward(kg, state), lamp) &&
if (!light_link_light_match(kg, light_link_receiver_forward(kg, state), klight->object_id) &&
!(path_flag & PATH_RAY_CAMERA))
{
continue;
}
#endif
#ifdef __SHADOW_LINKING__
if (kernel_data_fetch(lights, lamp).shadow_set_membership != LIGHT_LINK_MASK_ALL) {
if (kernel_data_fetch(objects, klight->object_id).shadow_set_membership !=
LIGHT_LINK_MASK_ALL)
{
continue;
}
#endif
@@ -155,7 +158,6 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
if (INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_CULL_LIGHT_CONNECTION) {
/* This path should have been resolved with mnee, it will
* generate a firefly for small lights since it is improbable. */
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
if (klight->use_caustics) {
continue;
}

View File

@@ -67,16 +67,8 @@ ccl_device void shadow_linking_setup_ray_from_intersection(
ray->self.object = INTEGRATOR_STATE(state, shadow_link, last_isect_object);
ray->self.prim = INTEGRATOR_STATE(state, shadow_link, last_isect_prim);
if (isect->type == PRIMITIVE_LAMP) {
ray->self.light_object = OBJECT_NONE;
ray->self.light_prim = PRIM_NONE;
ray->self.light = isect->prim;
}
else {
ray->self.light_object = isect->object;
ray->self.light_prim = isect->prim;
ray->self.light = LAMP_NONE;
}
ray->self.light_object = isect->object;
ray->self.light_prim = isect->prim;
}
ccl_device bool shadow_linking_shade_light(KernelGlobals kg,

View File

@@ -83,7 +83,6 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg,
ray.self.prim = PRIM_NONE;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
/* Modify ray position and length to match current segment. */
ray.tmin = (hit == 0) ? ray.tmin : INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t);
ray.tmax = (hit < num_recorded_hits) ? INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) :

View File

@@ -23,6 +23,7 @@
#include "kernel/integrator/subsurface.h"
#include "kernel/integrator/volume_stack.h"
#include "kernel/types.h"
#include "util/math_intersect.h"
CCL_NAMESPACE_BEGIN
@@ -348,9 +349,9 @@ ccl_device
#ifdef __MNEE__
IF_KERNEL_FEATURE(MNEE)
{
if (ls.lamp != LAMP_NONE) {
if (ls.type != LIGHT_TRIANGLE) {
/* Is this a caustic light? */
const bool use_caustics = kernel_data_fetch(lights, ls.lamp).use_caustics;
const bool use_caustics = kernel_data_fetch(lights, ls.prim).use_caustics;
if (use_caustics) {
/* Are we on a caustic caster? */
if (is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_CASTER)) {
@@ -647,7 +648,6 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
ray.self.prim = (skip_self) ? sd->prim : PRIM_NONE;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();

View File

@@ -337,11 +337,11 @@ ccl_device_inline bool volume_equiangular_valid_ray_segment(KernelGlobals kg,
const ccl_private LightSample *ls)
{
if (ls->type == LIGHT_SPOT) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
return spot_light_valid_ray_segment(klight, ray_P, ray_D, t_range);
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim);
return spot_light_valid_ray_segment(kg, klight, ray_P, ray_D, t_range);
}
if (ls->type == LIGHT_AREA) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim);
return area_light_valid_ray_segment(&klight->area, ray_P - klight->co, ray_D, t_range);
}
if (ls->type == LIGHT_TRIANGLE) {

View File

@@ -63,7 +63,6 @@ KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, tmin, KERNEL_FEATURE_PATH_TRACING
KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, tmax, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, int, self_light, KERNEL_FEATURE_SHADOW_LINKING)
KERNEL_STRUCT_END(shadow_ray)
/*********************** Shadow Intersection result **************************/

View File

@@ -98,10 +98,6 @@ ccl_device_forceinline void integrator_state_read_shadow_ray(ConstIntegratorShad
ccl_device_forceinline void integrator_state_write_shadow_ray_self(
KernelGlobals kg, IntegratorShadowState state, const ccl_private Ray *ccl_restrict ray)
{
if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) {
INTEGRATOR_STATE_WRITE(state, shadow_ray, self_light) = ray->self.light;
}
/* Save memory by storing the light and object indices in the shadow_isect. */
/* TODO(sergey): This optimization does not work on GPU where multiple iterations of intersection
* is needed if there are more than 4 transparent intersections. The indices starts to conflict
@@ -115,10 +111,6 @@ ccl_device_forceinline void integrator_state_write_shadow_ray_self(
ccl_device_forceinline void integrator_state_read_shadow_ray_self(
KernelGlobals kg, ConstIntegratorShadowState state, ccl_private Ray *ccl_restrict ray)
{
if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) {
ray->self.light = INTEGRATOR_STATE(state, shadow_ray, self_light);
}
ray->self.object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, object);
ray->self.prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, prim);
ray->self.light_object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, object);

View File

@@ -111,7 +111,6 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
ray.self.prim = PRIM_NONE;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
/* Intersect with the same object. if multiple intersections are found it
* will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */

View File

@@ -199,7 +199,6 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
ray.self.prim = prim;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
/* Convert subsurface to volume coefficients.
* The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */

View File

@@ -439,7 +439,6 @@ ccl_device_inline bool volume_shader_eval_entry(KernelGlobals kg,
/* Setup shader-data from stack. It's mostly setup already in shader_setup_from_volume, this
* switching should be quick. */
sd->object = entry.object;
sd->lamp = LAMP_NONE;
sd->shader = entry.shader;
sd->flag &= ~SD_SHADER_FLAGS;

View File

@@ -22,9 +22,8 @@ struct LightSample {
float pdf_selection; /* pdf for selecting light */
float eval_fac; /* intensity multiplier */
int object; /* object id for triangle/curve lights */
int prim; /* primitive id for triangle/curve lights */
int prim; /* lamp id for lights, primitive id for triangle/curve lights */
int shader; /* shader id */
int lamp; /* lamp id */
int group; /* lightgroup */
LightType type; /* type of light */
int emitter_id; /* index in the emitter array */

View File

@@ -12,7 +12,8 @@
CCL_NAMESPACE_BEGIN
ccl_device_inline void distant_light_uv(const ccl_global KernelLight *klight,
ccl_device_inline void distant_light_uv(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float3 D,
ccl_private float *u,
ccl_private float *v)
@@ -24,7 +25,7 @@ ccl_device_inline void distant_light_uv(const ccl_global KernelLight *klight,
const float fac = klight->distant.half_inv_sin_half_angle / len(D - klight->co);
/* Get u axis and v axis. */
const Transform itfm = klight->itfm;
const Transform itfm = lamp_get_inverse_transform(kg, klight);
const float u_ = dot(D, make_float3(itfm.x)) * fac;
const float v_ = dot(D, make_float3(itfm.y)) * fac;
@@ -33,7 +34,8 @@ ccl_device_inline void distant_light_uv(const ccl_global KernelLight *klight,
*v = -u_ - v_;
}
ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight,
ccl_device_inline bool distant_light_sample(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float2 rand,
ccl_private LightSample *ls)
{
@@ -47,7 +49,7 @@ ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight
ls->eval_fac = klight->distant.eval_fac;
distant_light_uv(klight, ls->D, &ls->u, &ls->v);
distant_light_uv(kg, klight, ls->D, &ls->u, &ls->v);
return true;
}
@@ -114,19 +116,18 @@ ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
#ifndef __HIP__
ls->shader = klight->shader_id;
#endif
ls->object = PRIM_NONE;
ls->prim = PRIM_NONE;
ls->lamp = lamp;
ls->object = klight->object_id;
ls->prim = lamp;
ls->t = FLT_MAX;
ls->P = -ray_D;
ls->Ng = -ray_D;
ls->D = ray_D;
ls->group = lamp_lightgroup(kg, lamp);
ls->group = object_lightgroup(kg, ls->object);
ls->pdf = klight->distant.pdf;
ls->eval_fac = klight->distant.eval_fac;
distant_light_uv(klight, ray_D, &ls->u, &ls->v);
distant_light_uv(kg, klight, ray_D, &ls->u, &ls->v);
return true;
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include "kernel/geom/object.h"
#include "kernel/globals.h"
#include "kernel/integrator/state.h"
@@ -57,14 +58,14 @@ ccl_device_inline int light_link_receiver_forward(KernelGlobals kg, IntegratorSt
ccl_device_inline bool light_link_light_match(KernelGlobals kg,
const int object_receiver,
const int light_emitter)
const int object_emitter)
{
#ifdef __LIGHT_LINKING__
if (!(kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING)) {
return true;
}
const uint64_t set_membership = kernel_data_fetch(lights, light_emitter).light_set_membership;
const uint64_t set_membership = kernel_data_fetch(objects, object_emitter).light_set_membership;
const uint receiver_set = (object_receiver != OBJECT_NONE) ?
kernel_data_fetch(objects, object_receiver).receiver_light_set :
0;
@@ -122,12 +123,11 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
const LightType type = (LightType)klight->type;
ls->type = type;
ls->shader = klight->shader_id;
ls->object = PRIM_NONE;
ls->prim = PRIM_NONE;
ls->lamp = lamp;
ls->object = klight->object_id;
ls->prim = lamp;
ls->u = rand.x;
ls->v = rand.y;
ls->group = lamp_lightgroup(kg, lamp);
ls->group = object_lightgroup(kg, ls->object);
if (in_volume_segment && (type == LIGHT_DISTANT || type == LIGHT_BACKGROUND)) {
/* Distant lights in a volume get a dummy sample, position will not actually
@@ -143,7 +143,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
}
if (type == LIGHT_DISTANT) {
if (!distant_light_sample(klight, rand, ls)) {
if (!distant_light_sample(kg, klight, rand, ls)) {
return false;
}
}
@@ -158,12 +158,12 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
ls->eval_fac = 1.0f;
}
else if (type == LIGHT_SPOT) {
if (!spot_light_sample<in_volume_segment>(klight, rand, P, N, shader_flags, ls)) {
if (!spot_light_sample<in_volume_segment>(kg, klight, rand, P, N, shader_flags, ls)) {
return false;
}
}
else if (type == LIGHT_POINT) {
if (!point_light_sample(klight, rand, P, N, shader_flags, ls)) {
if (!point_light_sample(kg, klight, rand, P, N, shader_flags, ls)) {
return false;
}
}
@@ -196,14 +196,15 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
const float2 rand = make_float2(rand_light);
int prim;
MeshLight mesh_light;
int shader_flag;
int object_id;
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
const ccl_global KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
ls->emitter_id);
prim = kemitter->light.id;
mesh_light.shader_flag = kemitter->mesh_light.shader_flag;
mesh_light.object_id = ls->object;
shader_flag = kemitter->shader_flag;
object_id = (prim >= 0) ? ls->object : kemitter->object_id;
}
else
#endif
@@ -211,26 +212,25 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
const ccl_global KernelLightDistribution *kdistribution = &kernel_data_fetch(
light_distribution, ls->emitter_id);
prim = kdistribution->prim;
mesh_light = kdistribution->mesh_light;
object_id = kdistribution->object_id;
shader_flag = 0;
}
if (!light_link_object_match(kg, object_receiver, object_id)) {
return false;
}
if (prim >= 0) {
/* Mesh light. */
const int object = mesh_light.object_id;
if (!light_link_object_match(kg, object_receiver, object)) {
return false;
}
/* Exclude synthetic meshes from shadow catcher pass. */
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER))
!(kernel_data_fetch(object_flag, object_id) & SD_OBJECT_SHADOW_CATCHER))
{
return false;
}
const int shader_flag = mesh_light.shader_flag;
if (!triangle_light_sample<in_volume_segment>(kg, prim, object, rand, time, ls, P)) {
if (!triangle_light_sample<in_volume_segment>(kg, prim, object_id, rand, time, ls, P)) {
return false;
}
ls->shader |= shader_flag;
@@ -238,10 +238,6 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
else {
const int light = ~prim;
if (!light_link_light_match(kg, object_receiver, light)) {
return false;
}
if (UNLIKELY(light_select_reached_max_bounces(kg, light, bounce))) {
return false;
}
@@ -278,6 +274,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg,
for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
const int object = klight->object_id;
if (path_flag & PATH_RAY_CAMERA) {
if (klight->shader_id & SHADER_EXCLUDE_CAMERA) {
@@ -312,12 +309,12 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg,
if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) {
if (is_main_path) {
if (is_indirect_ray &&
kernel_data_fetch(lights, lamp).shadow_set_membership != LIGHT_LINK_MASK_ALL)
kernel_data_fetch(objects, object).shadow_set_membership != LIGHT_LINK_MASK_ALL)
{
continue;
}
}
else if (kernel_data_fetch(lights, lamp).shadow_set_membership == LIGHT_LINK_MASK_ALL) {
else if (kernel_data_fetch(objects, object).shadow_set_membership == LIGHT_LINK_MASK_ALL) {
continue;
}
}
@@ -325,7 +322,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg,
#ifdef __LIGHT_LINKING__
/* Light linking. */
if (!light_link_light_match(kg, receiver_forward, lamp) && !(path_flag & PATH_RAY_CAMERA)) {
if (!light_link_light_match(kg, receiver_forward, object) && !(path_flag & PATH_RAY_CAMERA)) {
continue;
}
#endif
@@ -363,7 +360,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg,
}
/* Avoid self-intersections. */
if (last_prim == lamp && last_object == OBJECT_NONE && last_type == PRIMITIVE_LAMP) {
if (last_prim == lamp && last_object == object && last_type == PRIMITIVE_LAMP) {
continue;
}
@@ -391,7 +388,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg,
isect->v = v;
isect->type = PRIMITIVE_LAMP;
isect->prim = lamp;
isect->object = OBJECT_NONE;
isect->object = object;
}
return num_hits;
@@ -465,26 +462,24 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
const uint32_t path_flag,
ccl_private LightSample *ccl_restrict ls)
{
const int lamp = isect->prim;
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, isect->prim);
const LightType type = (LightType)klight->type;
ls->type = type;
ls->shader = klight->shader_id;
ls->object = isect->object;
ls->prim = isect->prim;
ls->lamp = lamp;
ls->t = isect->t;
ls->P = ray_P + ray_D * ls->t;
ls->D = ray_D;
ls->group = lamp_lightgroup(kg, lamp);
ls->group = object_lightgroup(kg, ls->object);
if (type == LIGHT_SPOT) {
if (!spot_light_sample_from_intersection(klight, ray_P, ray_D, N, path_flag, ls)) {
if (!spot_light_sample_from_intersection(kg, klight, ray_P, ray_D, N, path_flag, ls)) {
return false;
}
}
else if (type == LIGHT_POINT) {
if (!point_light_sample_from_intersection(klight, ray_P, ray_D, N, path_flag, ls)) {
if (!point_light_sample_from_intersection(kg, klight, ray_P, ray_D, N, path_flag, ls)) {
return false;
}
}

View File

@@ -4,13 +4,16 @@
#pragma once
#include "kernel/globals.h"
#include "kernel/light/common.h"
#include "util/math_intersect.h"
CCL_NAMESPACE_BEGIN
ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
ccl_device_inline bool point_light_sample(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float2 rand,
const float3 P,
const float3 N,
@@ -72,7 +75,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
}
/* Texture coordinates. */
const Transform itfm = klight->itfm;
const Transform itfm = lamp_get_inverse_transform(kg, klight);
const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng));
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
ls->u = uv.y;
@@ -92,7 +95,8 @@ ccl_device_forceinline float sphere_light_pdf(
return has_transmission ? M_1_2PI_F * 0.5f : pdf_cos_hemisphere(N, D);
}
ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global KernelLight *klight,
ccl_device_forceinline void point_light_mnee_sample_update(KernelGlobals kg,
const ccl_global KernelLight *klight,
ccl_private LightSample *ls,
const float3 P,
const float3 N,
@@ -122,7 +126,7 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern
}
/* Texture coordinates. */
const Transform itfm = klight->itfm;
const Transform itfm = lamp_get_inverse_transform(kg, klight);
const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng));
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
ls->u = uv.y;
@@ -149,7 +153,8 @@ ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *kligh
ray->P, ray->D, ray->tmin, ray->tmax, klight->co, diskN, radius, &P, t);
}
ccl_device_inline bool point_light_sample_from_intersection(const ccl_global KernelLight *klight,
ccl_device_inline bool point_light_sample_from_intersection(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float3 ray_P,
const float3 ray_D,
const float3 N,
@@ -179,7 +184,7 @@ ccl_device_inline bool point_light_sample_from_intersection(const ccl_global Ker
}
/* Texture coordinates. */
const Transform itfm = klight->itfm;
const Transform itfm = lamp_get_inverse_transform(kg, klight);
const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng));
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
ls->u = uv.y;

View File

@@ -8,6 +8,7 @@
#include "kernel/light/distribution.h"
#include "kernel/light/light.h"
#include "kernel/types.h"
#ifdef __LIGHT_TREE__
# include "kernel/light/tree.h"
@@ -56,7 +57,7 @@ light_sample_shader_eval(KernelGlobals kg,
ls->t,
time,
false,
ls->lamp);
ls->type != LIGHT_TRIANGLE);
ls->Ng = emission_sd->Ng;
}
@@ -80,8 +81,8 @@ light_sample_shader_eval(KernelGlobals kg,
eval *= ls->eval_fac;
if (ls->lamp != LAMP_NONE) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
if (ls->type != LIGHT_TRIANGLE) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim);
eval *= rgb_to_spectrum(
make_float3(klight->strength[0], klight->strength[1], klight->strength[2]));
}
@@ -251,7 +252,6 @@ ccl_device_inline void shadow_ray_setup(const ccl_private ShaderData *ccl_restri
ray->self.prim = (skip_self) ? sd->prim : PRIM_NONE;
ray->self.light_object = ls->object;
ray->self.light_prim = ls->prim;
ray->self.light = ls->lamp;
}
/* Create shadow ray towards light sample. */
@@ -392,13 +392,13 @@ ccl_device_forceinline void light_sample_update(KernelGlobals kg,
const float3 N,
const uint32_t path_flag)
{
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim);
if (ls->type == LIGHT_POINT) {
point_light_mnee_sample_update(klight, ls, P, N, path_flag);
point_light_mnee_sample_update(kg, klight, ls, P, N, path_flag);
}
else if (ls->type == LIGHT_SPOT) {
spot_light_mnee_sample_update(klight, ls, P, N, path_flag);
spot_light_mnee_sample_update(kg, klight, ls, P, N, path_flag);
}
else if (ls->type == LIGHT_AREA) {
area_light_mnee_sample_update(klight, ls, P);
@@ -487,7 +487,7 @@ ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg,
dt,
path_flag,
0,
kernel_data_fetch(light_to_tree, ls->lamp),
kernel_data_fetch(light_to_tree, ls->prim),
light_link_receiver_forward(kg, state));
}
else

View File

@@ -13,9 +13,11 @@
CCL_NAMESPACE_BEGIN
/* Transform vector to spot light's local coordinate system. */
ccl_device float3 spot_light_to_local(const ccl_global KernelLight *klight, const float3 ray)
ccl_device float3 spot_light_to_local(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float3 ray)
{
const Transform itfm = klight->itfm;
const Transform itfm = lamp_get_inverse_transform(kg, klight);
float3 transformed_ray = safe_normalize(transform_direction(&itfm, ray));
transformed_ray.z = -transformed_ray.z;
@@ -42,7 +44,8 @@ ccl_device void spot_light_uv(const float3 ray,
}
template<bool in_volume_segment>
ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
ccl_device_inline bool spot_light_sample(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float2 rand,
const float3 P,
const float3 N,
@@ -98,7 +101,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
}
/* Attenuation. */
const float3 local_ray = spot_light_to_local(klight, -ls->D);
const float3 local_ray = spot_light_to_local(kg, klight, -ls->D);
if (d_sq > r_sq) {
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
}
@@ -134,7 +137,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
ls->Ng = -ls->D;
/* Attenuation. */
const float3 local_ray = spot_light_to_local(klight, -ls->D);
const float3 local_ray = spot_light_to_local(kg, klight, -ls->D);
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
@@ -167,7 +170,8 @@ ccl_device_forceinline float spot_light_pdf(const ccl_global KernelSpotLight *sp
return has_transmission ? M_1_2PI_F * 0.5f : pdf_cos_hemisphere(N, D);
}
ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global KernelLight *klight,
ccl_device_forceinline void spot_light_mnee_sample_update(KernelGlobals kg,
const ccl_global KernelLight *klight,
ccl_private LightSample *ls,
const float3 P,
const float3 N,
@@ -203,7 +207,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne
}
/* Attenuation. */
const float3 local_ray = spot_light_to_local(klight, -ls->D);
const float3 local_ray = spot_light_to_local(kg, klight, -ls->D);
if (use_attenuation) {
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
}
@@ -224,7 +228,8 @@ ccl_device_inline bool spot_light_intersect(const ccl_global KernelLight *klight
return point_light_intersect(klight, ray, t);
}
ccl_device_inline bool spot_light_sample_from_intersection(const ccl_global KernelLight *klight,
ccl_device_inline bool spot_light_sample_from_intersection(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float3 ray_P,
const float3 ray_D,
const float3 N,
@@ -254,7 +259,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(const ccl_global Kern
}
/* Attenuation. */
const float3 local_ray = spot_light_to_local(klight, -ray_D);
const float3 local_ray = spot_light_to_local(kg, klight, -ray_D);
if (!klight->spot.is_sphere || d_sq > r_sq) {
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
}
@@ -269,13 +274,14 @@ ccl_device_inline bool spot_light_sample_from_intersection(const ccl_global Kern
}
/* Find the ray segment lit by the spot light. */
ccl_device_inline bool spot_light_valid_ray_segment(const ccl_global KernelLight *klight,
ccl_device_inline bool spot_light_valid_ray_segment(KernelGlobals kg,
const ccl_global KernelLight *klight,
const float3 P,
const float3 D,
ccl_private Interval<float> *t_range)
{
/* Convert to local space of the spot light. */
const Transform itfm = klight->itfm;
const Transform itfm = lamp_get_inverse_transform(kg, klight);
float3 local_P = P + klight->spot.dir * klight->spot.ray_segment_dp;
local_P = transform_point(&itfm, local_P);
const float3 local_D = transform_direction(&itfm, D);

View File

@@ -73,12 +73,12 @@ ccl_device_inline bool is_light(const ccl_global KernelLightTreeEmitter *kemitte
ccl_device_inline bool is_mesh(const ccl_global KernelLightTreeEmitter *kemitter)
{
return !is_light(kemitter) && kemitter->mesh_light.object_id == OBJECT_NONE;
return !is_light(kemitter) && kemitter->object_id == OBJECT_NONE;
}
ccl_device_inline bool is_triangle(const ccl_global KernelLightTreeEmitter *kemitter)
{
return !is_light(kemitter) && kemitter->mesh_light.object_id != OBJECT_NONE;
return !is_light(kemitter) && kemitter->object_id != OBJECT_NONE;
}
ccl_device_inline bool is_leaf(const ccl_global KernelLightTreeNode *knode)
@@ -274,7 +274,7 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
}
else {
kernel_assert(is_triangle(kemitter));
const int object = kemitter->mesh_light.object_id;
const int object = kemitter->object_id;
float3 vertices[3];
triangle_vertices(kg, kemitter->triangle.id, vertices);
centroid = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;

View File

@@ -168,7 +168,6 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
ls->eval_fac = 1.0f;
ls->object = object;
ls->prim = prim;
ls->lamp = LAMP_NONE;
ls->shader |= SHADER_USE_MIS;
ls->type = LIGHT_TRIANGLE;
ls->group = object_lightgroup(kg, object);

View File

@@ -104,7 +104,7 @@ void osl_eval_nodes<SHADER_TYPE_SURFACE>(const ThreadKernelGlobalsCPU *kg,
OSL::ShadingContext *octx = kg->osl.context;
const int shader = sd->shader & SHADER_MASK;
if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) {
if (sd->object == OBJECT_NONE) {
/* background */
if (kg->osl.globals->background_state) {
ss->execute(*octx,

View File

@@ -75,7 +75,6 @@ ustring OSLRenderServices::u_geom_dupli_generated("geom:dupli_generated");
ustring OSLRenderServices::u_geom_dupli_uv("geom:dupli_uv");
ustring OSLRenderServices::u_material_index("material:index");
ustring OSLRenderServices::u_object_random("object:random");
ustring OSLRenderServices::u_light_random("light:random");
ustring OSLRenderServices::u_particle_index("particle:index");
ustring OSLRenderServices::u_particle_random("particle:random");
ustring OSLRenderServices::u_particle_age("particle:age");
@@ -177,12 +176,6 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
return true;
}
if (sd->type == PRIMITIVE_LAMP) {
const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false);
copy_matrix(result, tfm);
return true;
}
return false;
}
@@ -221,12 +214,6 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
return true;
}
if (sd->type == PRIMITIVE_LAMP) {
const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true);
copy_matrix(result, itfm);
return true;
}
return false;
}
@@ -317,12 +304,6 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
return true;
}
if (sd->type == PRIMITIVE_LAMP) {
const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false);
copy_matrix(result, tfm);
return true;
}
return false;
}
@@ -349,12 +330,6 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
return true;
}
if (sd->type == PRIMITIVE_LAMP) {
const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true);
copy_matrix(result, itfm);
return true;
}
return false;
}
@@ -754,10 +729,6 @@ bool OSLRenderServices::get_object_standard_attribute(
const float f = object_random_number(kg, sd->object);
return set_attribute(f, type, derivatives, val);
}
if (name == u_light_random) {
const float f = lamp_random_number(kg, sd->lamp);
return set_attribute(f, type, derivatives, val);
}
/* Particle Attributes */
if (name == u_particle_index) {
@@ -1571,7 +1542,6 @@ bool OSLRenderServices::trace(TraceOpt &options,
ray.self.prim = PRIM_NONE;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
if (options.mindist == 0.0f) {
/* avoid self-intersections */

View File

@@ -275,7 +275,6 @@ class OSLRenderServices : public OSL::RendererServices {
static ustring u_geom_dupli_uv;
static ustring u_material_index;
static ustring u_object_random;
static ustring u_light_random;
static ustring u_particle_index;
static ustring u_particle_random;
static ustring u_particle_age;

View File

@@ -80,8 +80,6 @@ ccl_device_constant DeviceString u_geom_dupli_uv = 1294253317490155849ull;
ccl_device_constant DeviceString u_material_index = 741770758159634623ull;
/* "object:random" */
ccl_device_constant DeviceString u_object_random = 15789063994977955884ull;
/* "light:random" */
ccl_device_constant DeviceString u_light_random = 1743557801140685447ull;
/* "particle:index" */
ccl_device_constant DeviceString u_particle_index = 9489711748229903784ull;
/* "particle:random" */
@@ -628,11 +626,6 @@ ccl_device_extern bool osl_get_matrix(ccl_private ShaderGlobals *sg,
copy_matrix(res, tfm);
return true;
}
else if (sd->type == PRIMITIVE_LAMP) {
const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false);
copy_matrix(res, tfm);
return true;
}
}
else if (from == DeviceStrings::u_ndc) {
copy_matrix(res, kernel_data.cam.ndctoworld);
@@ -672,11 +665,6 @@ ccl_device_extern bool osl_get_inverse_matrix(ccl_private ShaderGlobals *sg,
copy_matrix(res, itfm);
return true;
}
else if (sd->type == PRIMITIVE_LAMP) {
const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true);
copy_matrix(res, itfm);
return true;
}
}
else if (to == DeviceStrings::u_ndc) {
copy_matrix(res, kernel_data.cam.worldtondc);
@@ -1245,11 +1233,6 @@ ccl_device_inline bool get_object_standard_attribute(KernelGlobals kg,
return set_attribute(f, type, derivatives, val);
}
else if (name == DeviceStrings::u_light_random) {
const float f = lamp_random_number(kg, sd->lamp);
return set_attribute(f, type, derivatives, val);
}
/* Particle attributes */
else if (name == DeviceStrings::u_particle_index) {

View File

@@ -16,13 +16,5 @@ shader node_object_info(output point Location = point(0.0, 0.0, 0.0),
getattribute("object:alpha", Alpha);
getattribute("object:index", ObjectIndex);
getattribute("material:index", MaterialIndex);
float is_light;
getattribute("object:is_light", is_light);
if (is_light) {
getattribute("light:random", Random);
}
else {
getattribute("object:random", Random);
}
getattribute("object:random", Random);
}

View File

@@ -76,7 +76,6 @@ ccl_device float svm_ao(
ray.self.prim = sd->prim;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();

View File

@@ -201,7 +201,6 @@ ccl_device float3 svm_bevel(
ray.self.prim = PRIM_NONE;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.self.light = LAMP_NONE;
/* Intersect with the same object. if multiple intersections are found it
* will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */

View File

@@ -141,12 +141,7 @@ ccl_device_noinline void svm_node_object_info(KernelGlobals kg,
data = shader_pass_id(kg, sd);
break;
case NODE_INFO_OB_RANDOM: {
if (sd->lamp != LAMP_NONE) {
data = lamp_random_number(kg, sd->lamp);
}
else {
data = object_random_number(kg, sd->object);
}
data = object_random_number(kg, sd->object);
break;
}
default:

View File

@@ -32,7 +32,7 @@ ccl_device_noinline void svm_node_vector_transform(KernelGlobals kg,
const NodeVectorTransformConvertSpace to = (NodeVectorTransformConvertSpace)ito;
Transform tfm;
const bool is_object = (sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP);
const bool is_object = (sd->object != OBJECT_NONE);
const bool is_normal = (type == NODE_VECTOR_TRANSFORM_TYPE_NORMAL);
const bool is_direction = (type == NODE_VECTOR_TRANSFORM_TYPE_VECTOR);

View File

@@ -724,7 +724,6 @@ struct RaySelfPrimitives {
int object; /* Instance prim is a part of */
int light_prim; /* Light primitive */
int light_object; /* Light object */
int light; /* Light ID (the light the shadow ray is traced towards to) */
};
struct Ray {
@@ -1151,8 +1150,6 @@ struct ccl_align(16) ShaderData
float v;
/* object id if there is one, ~0 otherwise */
int object;
/* lamp id if there is one, ~0 otherwise */
int lamp;
/* motion blur sample time */
float time;
@@ -1606,32 +1603,24 @@ struct KernelLight {
int type;
packed_float3 co;
int shader_id;
int object_id;
float max_bounces;
float random;
float strength[3];
int use_caustics;
int lightgroup;
Transform tfm;
Transform itfm;
int pad;
union {
KernelSpotLight spot;
KernelAreaLight area;
KernelDistantLight distant;
};
uint64_t light_set_membership;
uint64_t shadow_set_membership;
};
static_assert_align(KernelLight, 16);
struct MeshLight {
int shader_flag;
int object_id;
};
struct KernelLightDistribution {
float totarea;
int prim;
MeshLight mesh_light;
int shader_flag;
int object_id;
};
static_assert_align(KernelLightDistribution, 16);
@@ -1718,7 +1707,9 @@ struct KernelLightTreeEmitter {
} mesh;
};
MeshLight mesh_light;
/* Object and shader. */
int object_id;
int shader_flag;
/* Bit trail from root node to leaf node containing emitter. */
int bit_trail;

View File

@@ -75,6 +75,7 @@ class Geometry : public Node {
HAIR,
VOLUME,
POINTCLOUD,
LIGHT,
};
Type geometry_type;
@@ -185,6 +186,11 @@ class Geometry : public Node {
return geometry_type == VOLUME;
}
bool is_light() const
{
return geometry_type == LIGHT;
}
/* Updates */
void tag_update(Scene *scene, bool rebuild);
};

View File

@@ -84,7 +84,7 @@ static void shade_background_pixels(Device *device,
NODE_DEFINE(Light)
{
NodeType *type = NodeType::add("light", create);
NodeType *type = NodeType::add("light", create, NodeType::NONE, Geometry::get_node_base_type());
static NodeEnum type_enum;
type_enum.insert("point", LIGHT_POINT);
@@ -112,8 +112,6 @@ NODE_DEFINE(Light)
SOCKET_FLOAT(spot_angle, "Spot Angle", M_PI_4_F);
SOCKET_FLOAT(spot_smooth, "Spot Smooth", 0.0f);
SOCKET_TRANSFORM(tfm, "Transform", transform_identity());
SOCKET_BOOLEAN(cast_shadow, "Cast Shadow", true);
SOCKET_BOOLEAN(use_mis, "Use Mis", false);
SOCKET_BOOLEAN(use_camera, "Use Camera", true);
@@ -124,24 +122,17 @@ NODE_DEFINE(Light)
SOCKET_BOOLEAN(use_caustics, "Shadow Caustics", false);
SOCKET_INT(max_bounces, "Max Bounces", 1024);
SOCKET_UINT(random_id, "Random ID", 0);
SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", true);
SOCKET_BOOLEAN(is_portal, "Is Portal", false);
SOCKET_BOOLEAN(is_enabled, "Is Enabled", true);
SOCKET_NODE(shader, "Shader", Shader::get_node_type());
SOCKET_STRING(lightgroup, "Light Group", ustring());
SOCKET_UINT64(light_set_membership, "Light Set Membership", LIGHT_LINK_MASK_ALL);
SOCKET_UINT64(shadow_set_membership, "Shadow Set Membership", LIGHT_LINK_MASK_ALL);
SOCKET_BOOLEAN(normalize, "Normalize", true);
return type;
}
Light::Light() : Node(get_node_type())
Light::Light() : Geometry(get_node_type(), Geometry::LIGHT)
{
dereference_all_used_nodes();
}
@@ -165,46 +156,33 @@ bool Light::has_contribution(Scene *scene)
return true;
}
const Shader *effective_shader = (shader) ? shader : scene->default_light;
const Shader *effective_shader = (get_shader()) ? get_shader() : scene->default_light;
return !is_zero(effective_shader->emission_estimate);
}
bool Light::has_light_linking() const
Shader *Light::get_shader() const
{
if (get_light_set_membership() != LIGHT_LINK_MASK_ALL) {
return true;
}
return false;
return (used_shaders.empty()) ? nullptr : static_cast<Shader *>(used_shaders[0]);
}
bool Light::has_shadow_linking() const
void Light::compute_bounds()
{
if (get_shadow_set_membership() != LIGHT_LINK_MASK_ALL) {
return true;
}
return false;
/* To be implemented when this becomes actual geometry. */
}
float3 Light::get_co() const
void Light::apply_transform(const Transform & /*tfm*/, const bool /*apply_to_motion*/)
{
return transform_get_column(&tfm, 3);
/* To be implemented when this becomes actual geometry. */
}
float3 Light::get_dir() const
void Light::get_uv_tiles(ustring /*map*/, unordered_set<int> & /*tiles*/)
{
return -transform_get_column(&tfm, 2);
/* To be implemented when this becomes actual geometry. */
}
float3 Light::get_axisu() const
PrimitiveType Light::primitive_type() const
{
return transform_get_column(&tfm, 0);
}
float3 Light::get_axisv() const
{
return transform_get_column(&tfm, 1);
return PRIMITIVE_LAMP;
}
/* Light Manager */
@@ -219,7 +197,12 @@ LightManager::LightManager()
bool LightManager::has_background_light(Scene *scene)
{
for (Light *light : scene->lights) {
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
Light *light = static_cast<Light *>(object->get_geometry());
if (light->light_type == LIGHT_BACKGROUND && light->is_enabled) {
return true;
}
@@ -233,18 +216,31 @@ void LightManager::test_enabled_lights(Scene *scene)
* needed for finer-tuning of settings (for example, check whether we've
* got portals or not).
*/
vector<Light *> background_lights;
size_t num_lights = 0;
bool has_portal = false;
bool has_background = false;
for (Light *light : scene->lights) {
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
Light *light = static_cast<Light *>(object->get_geometry());
light->is_enabled = light->has_contribution(scene);
has_portal |= light->is_portal;
has_background |= light->light_type == LIGHT_BACKGROUND;
if (light->light_type == LIGHT_BACKGROUND) {
background_lights.push_back(light);
}
num_lights++;
}
VLOG_INFO << "Total " << num_lights << " lights.";
bool background_enabled = false;
int background_resolution = 0;
if (has_background) {
if (!background_lights.empty()) {
/* Ignore background light if:
* - If unsupported on a device
* - If we don't need it (no HDRs etc.)
@@ -254,12 +250,10 @@ void LightManager::test_enabled_lights(Scene *scene)
if (disable_mis) {
VLOG_INFO << "Background MIS has been disabled.\n";
}
for (Light *light : scene->lights) {
if (light->light_type == LIGHT_BACKGROUND) {
light->is_enabled = !disable_mis;
background_enabled = !disable_mis;
background_resolution = light->map_resolution;
}
for (Light *light : background_lights) {
light->is_enabled = !disable_mis;
background_enabled = !disable_mis;
background_resolution = light->map_resolution;
}
}
@@ -335,7 +329,6 @@ void LightManager::device_update_distribution(Device * /*unused*/,
/* Triangles. */
size_t offset = 0;
int j = 0;
for (Object *object : scene->objects) {
if (progress.get_cancel()) {
@@ -343,14 +336,12 @@ void LightManager::device_update_distribution(Device * /*unused*/,
}
if (!object->usable_as_light()) {
j++;
continue;
}
/* Sum area. */
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
const bool transform_applied = mesh->transform_applied;
const Transform tfm = object->get_tfm();
const int object_id = j;
int shader_flag = 0;
if (!(object->get_visibility() & PATH_RAY_CAMERA)) {
@@ -382,8 +373,8 @@ void LightManager::device_update_distribution(Device * /*unused*/,
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
distribution[offset].totarea = totarea;
distribution[offset].prim = i + mesh->prim_offset;
distribution[offset].mesh_light.shader_flag = shader_flag;
distribution[offset].mesh_light.object_id = object_id;
distribution[offset].shader_flag = shader_flag;
distribution[offset].object_id = object->index;
offset++;
const Mesh::Triangle t = mesh->get_triangle(i);
@@ -403,8 +394,6 @@ void LightManager::device_update_distribution(Device * /*unused*/,
totarea += triangle_area(p1, p2, p3);
}
}
j++;
}
const float trianglearea = totarea;
@@ -414,15 +403,20 @@ void LightManager::device_update_distribution(Device * /*unused*/,
if (num_lights > 0) {
const float lightarea = (totarea > 0.0f) ? totarea / num_lights : 1.0f;
for (Light *light : scene->lights) {
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
Light *light = static_cast<Light *>(object->get_geometry());
if (!light->is_enabled) {
continue;
}
distribution[offset].totarea = totarea;
distribution[offset].prim = ~light_index;
distribution[offset].mesh_light.object_id = OBJECT_NONE;
distribution[offset].mesh_light.shader_flag = 0;
distribution[offset].object_id = object->index;
distribution[offset].shader_flag = 0;
totarea += lightarea;
light_index++;
@@ -433,8 +427,8 @@ void LightManager::device_update_distribution(Device * /*unused*/,
/* normalize cumulative distribution functions */
distribution[num_distribution].totarea = totarea;
distribution[num_distribution].prim = 0;
distribution[num_distribution].mesh_light.object_id = OBJECT_NONE;
distribution[num_distribution].mesh_light.shader_flag = 0;
distribution[num_distribution].object_id = OBJECT_NONE;
distribution[num_distribution].shader_flag = 0;
if (totarea > 0.0f) {
for (size_t i = 0; i < num_distribution; i++) {
@@ -566,8 +560,8 @@ static void light_tree_leaf_emitters_copy_and_flatten(LightTreeFlatten &flatten,
}
kemitter.triangle.id = emitter.prim_id + mesh->prim_offset;
kemitter.mesh_light.shader_flag = shader_flag;
kemitter.mesh_light.object_id = emitter.object_id;
kemitter.shader_flag = shader_flag;
kemitter.object_id = emitter.object_id;
kemitter.triangle.emission_sampling = shader->emission_sampling;
flatten.triangle_array[emitter.prim_id + flatten.object_lookup_offset[emitter.object_id]] =
emitter_index;
@@ -575,16 +569,16 @@ static void light_tree_leaf_emitters_copy_and_flatten(LightTreeFlatten &flatten,
else if (emitter.is_light()) {
/* Light object. */
kemitter.light.id = emitter.light_id;
kemitter.mesh_light.shader_flag = 0;
kemitter.mesh_light.object_id = OBJECT_NONE;
kemitter.shader_flag = 0;
kemitter.object_id = emitter.object_id;
flatten.light_array[~emitter.light_id] = emitter_index;
}
else {
/* Mesh instance. */
assert(emitter.is_mesh());
kemitter.mesh.object_id = emitter.object_id;
kemitter.mesh_light.shader_flag = 0;
kemitter.mesh_light.object_id = OBJECT_NONE;
kemitter.shader_flag = 0;
kemitter.object_id = OBJECT_NONE;
flatten.mesh_array[emitter.object_id] = emitter_index;
/* Create instance node. One instance node will be the same as the
@@ -595,7 +589,8 @@ static void light_tree_leaf_emitters_copy_and_flatten(LightTreeFlatten &flatten,
auto map_it = flatten.instances.find(reference_node);
if (map_it == flatten.instances.end()) {
if (instance_node != reference_node) {
/* Flatten the node with the subtree first so the subsequent instances know the index. */
/* Flatten the node with the subtree first so the subsequent instances know the index.
*/
std::swap(instance_node->type, reference_node->type);
std::swap(instance_node->variant_type, reference_node->variant_type);
}
@@ -727,8 +722,8 @@ static std::pair<int, LightTreeMeasure> light_tree_specialize_nodes_flatten(
assert(first_emitter != -1);
/* Preserve the type of the node, so that the kernel can do proper decision when sampling node
* with multiple distant lights in it. */
/* Preserve the type of the node, so that the kernel can do proper decision when sampling
* node with multiple distant lights in it. */
if (node->is_leaf()) {
new_node.make_leaf(first_emitter, num_emitters);
}
@@ -951,7 +946,12 @@ void LightManager::device_update_background(Device *device,
bool background_mis = false;
/* find background light */
for (Light *light : scene->lights) {
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
Light *light = static_cast<Light *>(object->get_geometry());
if (light->light_type == LIGHT_BACKGROUND && light->is_enabled) {
background_light = light;
background_mis |= light->use_mis;
@@ -1125,7 +1125,12 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
size_t num_distant_lights = 0;
bool use_light_mis = false;
for (Light *light : scene->lights) {
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
Light *light = static_cast<Light *>(object->get_geometry());
if (light->is_enabled) {
num_lights++;
@@ -1165,14 +1170,24 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
int light_index = 0;
int portal_index = num_lights;
for (Light *light : scene->lights) {
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
Light *light = static_cast<Light *>(object->get_geometry());
const float3 axisu = transform_get_column(&object->get_tfm(), 0);
const float3 axisv = transform_get_column(&object->get_tfm(), 1);
const float3 dir = -transform_get_column(&object->get_tfm(), 2);
const float3 co = transform_get_column(&object->get_tfm(), 3);
/* Consider moving portals update to their own function
* keeping this one more manageable. */
if (light->is_portal) {
assert(light->light_type == LIGHT_AREA);
const float3 extentu = light->get_axisu() * (light->sizeu * light->size);
const float3 extentv = light->get_axisv() * (light->sizev * light->size);
const float3 extentu = axisu * (light->sizeu * light->size);
const float3 extentv = axisv * (light->sizev * light->size);
float len_u;
float len_v;
@@ -1188,17 +1203,14 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
invarea = -invarea;
}
const float3 dir = safe_normalize(light->get_dir());
klights[portal_index].co = light->get_co();
klights[portal_index].co = co;
klights[portal_index].area.axis_u = axis_u;
klights[portal_index].area.len_u = len_u;
klights[portal_index].area.axis_v = axis_v;
klights[portal_index].area.len_v = len_v;
klights[portal_index].area.invarea = invarea;
klights[portal_index].area.dir = dir;
klights[portal_index].tfm = light->tfm;
klights[portal_index].itfm = transform_inverse(light->tfm);
klights[portal_index].area.dir = safe_normalize(dir);
klights[portal_index].object_id = object->index;
portal_index++;
continue;
@@ -1208,9 +1220,8 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
continue;
}
Shader *shader = (light->shader) ? light->shader : scene->default_light;
Shader *shader = (light->get_shader()) ? light->get_shader() : scene->default_light;
int shader_id = scene->shader_manager->get_shader_id(shader);
const float random = (float)light->random_id * (1.0f / (float)0xFFFFFFFF);
if (!light->cast_shadow) {
shader_id &= ~SHADER_CAST_SHADOW;
@@ -1255,7 +1266,7 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
shader_id |= SHADER_USE_MIS;
}
klights[light_index].co = light->get_co();
klights[light_index].co = co;
klights[light_index].spot.radius = radius;
klights[light_index].spot.eval_fac = eval_fac;
klights[light_index].spot.is_sphere = light->get_is_sphere() && radius != 0.0f;
@@ -1263,7 +1274,6 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
else if (light->light_type == LIGHT_DISTANT) {
shader_id &= ~SHADER_AREA_LIGHT;
const float3 dir = safe_normalize(light->get_dir());
const float angle = light->angle / 2.0f;
if (light->use_mis && angle > 0.0f) {
@@ -1273,7 +1283,7 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
const float one_minus_cosangle = 2.0f * sqr(sinf(0.5f * angle));
const float pdf = (angle > 0.0f) ? (M_1_2PI_F / one_minus_cosangle) : 1.0f;
klights[light_index].co = dir;
klights[light_index].co = safe_normalize(dir);
klights[light_index].distant.angle = angle;
klights[light_index].distant.one_minus_cosangle = one_minus_cosangle;
klights[light_index].distant.pdf = pdf;
@@ -1307,8 +1317,8 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
}
else if (light->light_type == LIGHT_AREA) {
const float light_size = light->size;
const float3 extentu = light->get_axisu() * (light->sizeu * light_size);
const float3 extentv = light->get_axisv() * (light->sizev * light_size);
const float3 extentu = axisu * (light->sizeu * light_size);
const float3 extentv = axisv * (light->sizev * light_size);
float len_u;
float len_v;
@@ -1336,19 +1346,17 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
3.0f / powf(half_spread, 3.0f)) :
FLT_MAX;
const float3 dir = safe_normalize(light->get_dir());
if (light->use_mis && area != 0.0f && light->spread > 0.0f) {
shader_id |= SHADER_USE_MIS;
}
klights[light_index].co = light->get_co();
klights[light_index].co = co;
klights[light_index].area.axis_u = axis_u;
klights[light_index].area.len_u = len_u;
klights[light_index].area.axis_v = axis_v;
klights[light_index].area.len_v = len_v;
klights[light_index].area.invarea = invarea;
klights[light_index].area.dir = dir;
klights[light_index].area.dir = safe_normalize(dir);
klights[light_index].area.tan_half_spread = tan_half_spread;
klights[light_index].area.normalize_spread = normalize_spread;
}
@@ -1357,12 +1365,12 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
const float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
const float tan_half_spot_angle = tanf(light->spot_angle * 0.5f);
const float len_w_sq = len_squared(light->get_dir());
const float len_u_sq = len_squared(light->get_axisu());
const float len_v_sq = len_squared(light->get_axisv());
const float len_w_sq = len_squared(dir);
const float len_u_sq = len_squared(axisu);
const float len_v_sq = len_squared(axisv);
const float tan_sq = sqr(tan_half_spot_angle);
klights[light_index].spot.dir = safe_normalize(light->get_dir());
klights[light_index].spot.dir = safe_normalize(dir);
klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle;
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tan_half_spot_angle;
klights[light_index].spot.spot_smooth = spot_smooth;
@@ -1375,31 +1383,11 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene)
}
klights[light_index].shader_id = shader_id;
klights[light_index].object_id = object->index;
klights[light_index].max_bounces = light->max_bounces;
klights[light_index].random = random;
klights[light_index].use_caustics = light->use_caustics;
klights[light_index].tfm = light->tfm;
klights[light_index].itfm = transform_inverse(light->tfm);
/* Light group. */
if (light->light_type == LIGHT_BACKGROUND) {
klights[light_index].lightgroup = dscene->data.background.lightgroup;
}
else {
auto it = scene->lightgroups.find(light->lightgroup);
if (it != scene->lightgroups.end()) {
klights[light_index].lightgroup = it->second;
}
else {
klights[light_index].lightgroup = LIGHTGROUP_NONE;
}
}
klights[light_index].light_set_membership = light->light_set_membership;
klights[light_index].shadow_set_membership = light->shadow_set_membership;
light_index++;
}
@@ -1423,8 +1411,6 @@ void LightManager::device_update(Device *device,
}
});
VLOG_INFO << "Total " << scene->lights.size() << " lights.";
/* Detect which lights are enabled, also determines if we need to update the background. */
test_enabled_lights(scene);

View File

@@ -8,9 +8,7 @@
#include "graph/node.h"
/* included as Light::set_shader defined through NODE_SOCKET_API does not select
* the right Node::set overload as it does not know that Shader is a Node */
#include "scene/shader.h"
#include "scene/geometry.h"
#include "util/ies.h"
#include "util/thread.h"
@@ -26,7 +24,7 @@ class Progress;
class Scene;
class Shader;
class Light : public Node {
class Light : public Geometry {
public:
NODE_DECLARE;
@@ -43,8 +41,6 @@ class Light : public Node {
NODE_SOCKET_API(bool, ellipse)
NODE_SOCKET_API(float, spread)
NODE_SOCKET_API(Transform, tfm)
NODE_SOCKET_API(int, map_resolution)
NODE_SOCKET_API(float, average_radiance)
@@ -66,13 +62,7 @@ class Light : public Node {
NODE_SOCKET_API(bool, is_portal)
NODE_SOCKET_API(bool, is_enabled)
NODE_SOCKET_API(Shader *, shader)
NODE_SOCKET_API(int, max_bounces)
NODE_SOCKET_API(uint, random_id)
NODE_SOCKET_API(ustring, lightgroup)
NODE_SOCKET_API(uint64_t, light_set_membership);
NODE_SOCKET_API(uint64_t, shadow_set_membership);
/* Normalize power by the surface area of the light. */
NODE_SOCKET_API(bool, normalize)
@@ -82,15 +72,14 @@ class Light : public Node {
/* Check whether the light has contribution the scene. */
bool has_contribution(Scene *scene);
/* Check whether this light participates in light or shadow linking. */
bool has_light_linking() const;
bool has_shadow_linking() const;
/* Shader */
Shader *get_shader() const;
/* Convenience access to transform. */
float3 get_co() const;
float3 get_dir() const;
float3 get_axisu() const;
float3 get_axisv() const;
/* Geometry */
void compute_bounds() override;
void apply_transform(const Transform &tfm, const bool apply_to_motion) override;
void get_uv_tiles(ustring map, unordered_set<int> &tiles) override;
PrimitiveType primitive_type() const override;
friend class LightManager;
friend class LightTree;

View File

@@ -88,9 +88,10 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
bool need_transformation)
: prim_id(prim_id), object_id(object_id)
{
Object *object = scene->objects[object_id];
if (is_triangle()) {
float3 vertices[3];
Object *object = scene->objects[object_id];
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
const Mesh::Triangle triangle = mesh->get_triangle(prim_id);
Shader *shader = static_cast<Shader *>(mesh->get_used_shaders()[mesh->get_shader()[prim_id]]);
@@ -148,13 +149,13 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
}
else {
assert(is_light());
Light *lamp = scene->lights[object_id];
Light *lamp = static_cast<Light *>(object->get_geometry());
const LightType type = lamp->get_light_type();
const float size = lamp->get_size();
float3 strength = lamp->get_strength();
centroid = lamp->get_co();
measure.bcone.axis = safe_normalize(lamp->get_dir());
centroid = transform_get_column(&object->get_tfm(), 3);
measure.bcone.axis = -safe_normalize(transform_get_column(&object->get_tfm(), 2));
if (type == LIGHT_AREA) {
measure.bcone.theta_o = 0;
@@ -163,8 +164,10 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
/* For an area light, sizeu and sizev determine the 2 dimensions of the area light,
* while axisu and axisv determine the orientation of the 2 dimensions.
* We want to add all 4 corners to our bounding box. */
const float3 half_extentu = 0.5f * lamp->get_sizeu() * lamp->get_axisu() * size;
const float3 half_extentv = 0.5f * lamp->get_sizev() * lamp->get_axisv() * size;
const float3 axisu = transform_get_column(&object->get_tfm(), 0);
const float3 axisv = transform_get_column(&object->get_tfm(), 1);
const float3 half_extentu = 0.5f * lamp->get_sizeu() * axisu * size;
const float3 half_extentv = 0.5f * lamp->get_sizev() * axisv * size;
measure.bbox.grow(centroid + half_extentu + half_extentv);
measure.bbox.grow(centroid + half_extentu - half_extentv);
measure.bbox.grow(centroid - half_extentu + half_extentv);
@@ -188,9 +191,9 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
measure.bcone.theta_o = 0;
float theta_e = min(lamp->get_spot_angle() * 0.5f, M_PI_2_F);
const float len_u = len(lamp->get_axisu());
const float len_v = len(lamp->get_axisv());
const float len_w = len(lamp->get_dir());
const float len_u = len(transform_get_column(&object->get_tfm(), 0));
const float len_v = len(transform_get_column(&object->get_tfm(), 1));
const float len_w = len(transform_get_column(&object->get_tfm(), 2));
/* As `theta_e` approaches `pi/2`, the behavior of `atan(tan(theta_e))` can become quite
* unpredictable as `tan(x)` has an asymptote at `x = pi/2`. To avoid this, we skip the back
@@ -236,7 +239,7 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
* light tree. */
measure.energy = average(fabs(strength));
light_set_membership = lamp->get_light_set_membership();
light_set_membership = object->get_light_set_membership();
}
}
@@ -289,45 +292,42 @@ LightTree::LightTree(Scene *scene,
* Therefore, we want to keep track of the light's index on the device.
* However, we also need the light's index in the scene when we're constructing the tree. */
int device_light_index = 0;
int scene_light_index = 0;
for (Light *light : scene->lights) {
if (light->is_enabled) {
if (light->light_type == LIGHT_BACKGROUND || light->light_type == LIGHT_DISTANT) {
distant_lights_.emplace_back(scene, ~device_light_index, scene_light_index);
}
else {
local_lights_.emplace_back(scene, ~device_light_index, scene_light_index);
}
device_light_index++;
}
scene_light_index++;
}
/* Similarly, we also want to keep track of the index of triangles of emissive objects. */
int object_id = 0;
for (Object *object : scene->objects) {
if (progress_.get_cancel()) {
return;
}
light_link_receiver_used |= (uint64_t(1) << object->get_receiver_light_set());
if (object->get_geometry()->is_light()) {
/* Regular lights. */
Light *light = static_cast<Light *>(object->get_geometry());
if (light->is_enabled) {
if (light->light_type == LIGHT_BACKGROUND || light->light_type == LIGHT_DISTANT) {
distant_lights_.emplace_back(scene, ~device_light_index, object->index);
}
else {
local_lights_.emplace_back(scene, ~device_light_index, object->index);
}
if (!object->usable_as_light()) {
object_id++;
continue;
device_light_index++;
}
}
else {
/* Emissive triangles. */
light_link_receiver_used |= (uint64_t(1) << object->get_receiver_light_set());
mesh_lights_.emplace_back(object, object_id);
object_id++;
if (!object->usable_as_light()) {
continue;
}
/* Only count unique meshes. */
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
auto map_it = offset_map_.find(mesh);
if (map_it == offset_map_.end()) {
offset_map_[mesh] = num_triangles;
num_triangles += mesh->num_triangles();
mesh_lights_.emplace_back(object, object->index);
/* Only count unique meshes. */
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
auto map_it = offset_map_.find(mesh);
if (map_it == offset_map_.end()) {
offset_map_[mesh] = num_triangles;
num_triangles += mesh->num_triangles();
}
}
}
}

View File

@@ -75,8 +75,8 @@ static void recursive_print_node(FILE *file, const LightTreeNode &node)
fprintf(file, "];\n");
if (node.is_inner()) {
const LightTreeNode &left_node = *node.get_inner().children[LightTree::left].get();
const LightTreeNode &right_node = *node.get_inner().children[LightTree::right].get();
const LightTreeNode &left_node = *node.get_inner().children[LightTree::left];
const LightTreeNode &right_node = *node.get_inner().children[LightTree::right];
recursive_print_node(file, left_node);
recursive_print_node(file, right_node);
@@ -95,21 +95,17 @@ static void print_emitters(FILE *file, const Scene &scene, const LightTree &tree
string label = string_printf("<f%d> emitter %s", field++, std::to_string(i).c_str());
/* Emitter details (type, object or light name). */
const Object &object = *scene.objects[emitter.object_id];
if (emitter.is_light()) {
const Light &lamp = *scene.lights[emitter.object_id];
label += string_printf("|<f%d> light", field++);
label += string_printf("|<f%d> %s", field++, lamp.name.c_str());
}
else if (emitter.is_triangle()) {
const Object &object = *scene.objects[emitter.object_id];
label += string_printf("|<f%d> triangle", field++);
label += string_printf("|<f%d> %s", field++, object.name.c_str());
}
else if (emitter.is_mesh()) {
const Object &object = *scene.objects[emitter.object_id];
label += string_printf("|<f%d> mesh", field++);
label += string_printf("|<f%d> %s", field++, object.name.c_str());
}
label += string_printf("|<f%d> %s", field++, object.name.c_str());
/* Light linking. */
if (emitter.light_set_membership == ~uint64_t(0)) {
@@ -176,8 +172,8 @@ static void recursive_print_node_relations(FILE *file,
return;
}
const LightTreeNode &left_node = *node.get_inner().children[LightTree::left].get();
const LightTreeNode &right_node = *node.get_inner().children[LightTree::right].get();
const LightTreeNode &left_node = *node.get_inner().children[LightTree::left];
const LightTreeNode &right_node = *node.get_inner().children[LightTree::right];
const string left_node_id = get_node_id(left_node);
const string right_node_id = get_node_id(right_node);

View File

@@ -272,6 +272,10 @@ int Object::motion_step(const float time) const
bool Object::is_traceable() const
{
/* Not supported for lights yet. */
if (geometry->is_light()) {
return false;
}
/* Mesh itself can be empty,can skip all such objects. */
if (!bounds.valid() || bounds.size() == zero_float3()) {
return false;

View File

@@ -77,6 +77,10 @@ class Object : public Node {
/* Set during device update. */
bool intersects_volume;
/* Specifies the position of the object in scene->objects and
* in the device vectors. Gets set in device_update. */
int index;
Object();
~Object() override;
@@ -120,10 +124,6 @@ class Object : public Node {
bool has_shadow_linking() const;
protected:
/* Specifies the position of the object in scene->objects and
* in the device vectors. Gets set in device_update. */
int index;
/* Reference to the attribute map with object attributes,
* or 0 if none. Set in update_svm_attributes. */
size_t attr_map_offset;

View File

@@ -99,7 +99,6 @@ void Scene::free_memory(bool final)
procedurals.clear();
objects.clear();
geometry.clear();
lights.clear();
particle_systems.clear();
passes.clear();
@@ -511,6 +510,12 @@ void Scene::update_kernel_features()
else if (geom->is_pointcloud()) {
kernel_features |= KERNEL_FEATURE_POINTCLOUD;
}
else if (geom->is_light()) {
const Light *light = static_cast<const Light *>(object->get_geometry());
if (light->get_use_caustics()) {
has_caustics_light = true;
}
}
if (object->has_light_linking()) {
kernel_features |= KERNEL_FEATURE_LIGHT_LINKING;
}
@@ -519,19 +524,6 @@ void Scene::update_kernel_features()
}
}
for (Light *light : lights) {
if (light->get_use_caustics()) {
has_caustics_light = true;
}
if (light->has_light_linking()) {
kernel_features |= KERNEL_FEATURE_LIGHT_LINKING;
}
if (light->has_shadow_linking()) {
kernel_features |= KERNEL_FEATURE_SHADOW_LINKING;
}
}
dscene.data.integrator.use_caustics = false;
if (device->info.has_mnee && has_caustics_caster && has_caustics_receiver && has_caustics_light)
{
@@ -741,7 +733,7 @@ template<> Light *Scene::create_node<Light>()
unique_ptr<Light> node = make_unique<Light>();
Light *node_ptr = node.get();
node->set_owner(this);
lights.push_back(std::move(node));
geometry.push_back(std::move(node));
light_manager->tag_update(this, LightManager::LIGHT_ADDED);
return node_ptr;
}
@@ -879,7 +871,7 @@ template<> Film *Scene::create_node<Film>()
template<> void Scene::delete_node(Light *node)
{
assert(node->get_owner() == this);
lights.erase_by_swap(node);
geometry.erase_by_swap(node);
light_manager->tag_update(this, LightManager::LIGHT_REMOVED);
}
@@ -983,18 +975,12 @@ template<typename T> static void assert_same_owner(const set<T *> &nodes, const
#endif
}
template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner)
{
assert_same_owner(nodes, owner);
lights.erase_in_set(nodes);
light_manager->tag_update(this, LightManager::LIGHT_REMOVED);
}
template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner)
{
assert_same_owner(nodes, owner);
geometry.erase_in_set(nodes);
geometry_manager->tag_update(this, GeometryManager::GEOMETRY_REMOVED);
light_manager->tag_update(this, LightManager::LIGHT_REMOVED);
}
template<> void Scene::delete_nodes(const set<Object *> &nodes, const NodeOwner *owner)

View File

@@ -136,7 +136,6 @@ class Scene : public NodeOwner {
unique_ptr_vector<Shader> shaders;
unique_ptr_vector<Pass> passes;
unique_ptr_vector<ParticleSystem> particle_systems;
unique_ptr_vector<Light> lights;
unique_ptr_vector<Geometry> geometry;
unique_ptr_vector<Object> objects;
unique_ptr_vector<Procedural> procedurals;
@@ -281,7 +280,6 @@ template<> void Scene::delete_node(Procedural *node);
template<> void Scene::delete_node(AlembicProcedural *node);
template<> void Scene::delete_node(Pass *node);
template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<Object *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const NodeOwner *owner);