realize-depth #5

Merged
Arye Ramaty merged 43 commits from David-Haver/blender:realize-depth into WIP-realize-depth 2024-03-31 17:22:49 +02:00
11 changed files with 127 additions and 131 deletions
Showing only changes of commit fdc2962beb - Show all commits

View File

@ -324,13 +324,6 @@ ccl_device_inline bool volume_equiangular_valid_ray_segment(KernelGlobals kg,
ccl_private float2 *t_range,
const ccl_private LightSample *ls)
{
# ifdef __LIGHT_TREE__
/* Do not compute ray segment until #119389 is landed. */
if (kernel_data.integrator.use_light_tree) {
return true;
}
# endif
if (ls->type == LIGHT_SPOT) {
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
return spot_light_valid_ray_segment(klight, ray_P, ray_D, t_range);
@ -708,7 +701,8 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
ccl_private const Ray *ccl_restrict ray,
ccl_private const ShaderData *ccl_restrict sd,
ccl_private const RNGState *ccl_restrict rng_state,
ccl_private EquiangularCoefficients *ccl_restrict equiangular_coeffs)
ccl_private EquiangularCoefficients *ccl_restrict equiangular_coeffs,
ccl_private LightSample &ccl_restrict ls)
{
/* Test if there is a light or BSDF that needs direct light. */
if (!kernel_data.integrator.use_direct_light) {
@ -720,7 +714,6 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
LightSample ls ccl_optional_struct_init;
if (!light_sample_from_volume_segment(kg,
rand_light,
sd->time,
@ -761,41 +754,26 @@ ccl_device_forceinline void integrate_volume_direct_light(
# ifdef __PATH_GUIDING__
ccl_private const Spectrum unlit_throughput,
# endif
ccl_private const Spectrum throughput)
ccl_private const Spectrum throughput,
ccl_private LightSample &ccl_restrict ls)
{
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT);
if (!kernel_data.integrator.use_direct_light) {
if (!kernel_data.integrator.use_direct_light || ls.emitter_id == EMITTER_NONE) {
return;
}
/* Sample position on the same light again, now from the shading point where we scattered.
*
* Note that this means we sample the light tree twice when equiangular sampling is used.
* We could consider sampling the light tree just once and use the same light position again.
*
* This would make the PDFs for MIS weights more complicated due to having to account for
* both distance/equiangular and direct/indirect light sampling, but could be more accurate.
* Additionally we could end up behind the light or outside a spot light cone, which might
* waste a sample. Though on the other hand it would be possible to prevent that with
* equiangular sampling restricted to a smaller sub-segment where the light has influence. */
LightSample ls ccl_optional_struct_init;
/* Sample position on the same light again, now from the shading point where we scattered. */
{
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
const float3 N = zero_float3();
const int object_receiver = light_link_receiver_nee(kg, sd);
const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
if (!light_sample_from_position(kg,
rng_state,
rand_light,
sd->time,
P,
zero_float3(),
light_link_receiver_nee(kg, sd),
SD_BSDF_HAS_TRANSMISSION,
bounce,
path_flag,
&ls))
if (!light_sample<false>(
kg, rand_light, sd->time, P, N, object_receiver, shader_flags, bounce, path_flag, &ls))
{
return;
}
@ -913,6 +891,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const Ray *ray,
ccl_private const RNGState *rng_state,
ccl_private const ShaderVolumePhases *phases)
{
@ -965,6 +944,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_wo);
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
INTEGRATOR_STATE_WRITE(state, ray, previous_dt) = ray->tmax - ray->tmin;
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
# ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
@ -993,7 +973,8 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
/* Update path state */
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf;
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = zero_float3();
const float3 previous_P = ray->P + ray->D * ray->tmin;
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = sd->P - previous_P;
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
@ -1025,13 +1006,15 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
/* Sample light ahead of volume stepping, for equiangular sampling. */
/* TODO: distant lights are ignored now, but could instead use even distribution. */
LightSample ls ccl_optional_struct_init;
ls.emitter_id = EMITTER_NONE;
const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE);
EquiangularCoefficients equiangular_coeffs = {zero_float3(), make_float2(ray->tmin, ray->tmax)};
const bool have_equiangular_sample = need_light_sample &&
integrate_volume_equiangular_sample_light(
kg, state, ray, &sd, &rng_state, &equiangular_coeffs);
const bool have_equiangular_sample =
need_light_sample && integrate_volume_equiangular_sample_light(
kg, state, ray, &sd, &rng_state, &equiangular_coeffs, ls);
VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ?
volume_stack_sample_method(kg, state) :
@ -1129,7 +1112,8 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
# ifdef __PATH_GUIDING__
unlit_throughput,
# endif
result.direct_throughput);
result.direct_throughput,
ls);
}
/* Indirect light.
@ -1168,7 +1152,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
# endif
# endif
if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) {
if (integrate_volume_phase_scatter(kg, state, &sd, ray, &rng_state, &result.indirect_phases)) {
return VOLUME_PATH_SCATTERED;
}
else {

View File

@ -75,6 +75,9 @@ KERNEL_STRUCT_MEMBER(ray, float, tmax, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING)
#ifdef __LIGHT_TREE__
KERNEL_STRUCT_MEMBER(ray, float, previous_dt, KERNEL_FEATURE_PATH_TRACING)
#endif
KERNEL_STRUCT_END(ray)
/*************************** Intersection result ******************************/

View File

@ -518,9 +518,8 @@ ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLi
const bool shape_above_surface = dot(N, centroid - P) + fabsf(dot(N, extentu)) +
fabsf(dot(N, extentv)) >
0;
const bool in_volume = is_zero(N);
return (front_facing && shape_above_surface) || in_volume;
return front_facing && shape_above_surface;
}
CCL_NAMESPACE_END

View File

@ -12,9 +12,9 @@ CCL_NAMESPACE_BEGIN
typedef struct LightSample {
float3 P; /* position on light, or direction for distant light */
float3 Ng; /* normal on light */
float3 D; /* direction from shading point to light */
packed_float3 Ng; /* normal on light */
float t; /* distance to light (FLT_MAX for distant light) */
float3 D; /* direction from shading point to light */
float u, v; /* parametric coordinate on primitive */
float pdf; /* pdf for selecting light and point on light */
float pdf_selection; /* pdf for selecting light */
@ -25,6 +25,7 @@ typedef struct LightSample {
int lamp; /* lamp id */
int group; /* lightgroup */
LightType type; /* type of light */
int emitter_id; /* index in the emitter array */
} LightSample;
/* Utilities */

View File

@ -41,36 +41,14 @@ ccl_device int light_distribution_sample(KernelGlobals kg, const float rand)
return index;
}
template<bool in_volume_segment>
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
const float3 rand,
const float time,
const float3 P,
const float3 N,
const int object_receiver,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
const float rand,
ccl_private LightSample *ls)
{
/* Sample light index from distribution. */
/* The first two dimensions of the Sobol sequence have better stratification. */
const int index = light_distribution_sample(kg, rand.z);
const float pdf_selection = kernel_data.integrator.distribution_pdf_lights;
const float2 rand_uv = float3_to_float2(rand);
return light_sample<in_volume_segment>(kg,
rand_uv,
time,
P,
N,
object_receiver,
shader_flags,
bounce,
path_flag,
index,
0,
pdf_selection,
ls);
ls->emitter_id = light_distribution_sample(kg, rand);
ls->pdf_selection = kernel_data.integrator.distribution_pdf_lights;
return true;
}
ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg)

View File

@ -177,7 +177,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
template<bool in_volume_segment>
ccl_device_noinline bool light_sample(KernelGlobals kg,
const float2 rand,
const float3 rand_light,
const float time,
const float3 P,
const float3 N,
@ -185,33 +185,31 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
const int emitter_index,
const int object_id,
const float pdf_selection,
ccl_private LightSample *ls)
{
/* The first two dimensions of the Sobol sequence have better stratification, use them to sample
* position on the light. */
const float2 rand = float3_to_float2(rand_light);
int prim;
MeshLight mesh_light;
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
emitter_index);
ls->emitter_id);
prim = kemitter->light.id;
mesh_light.shader_flag = kemitter->mesh_light.shader_flag;
mesh_light.object_id = object_id;
mesh_light.object_id = ls->object;
}
else
#endif
{
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(
light_distribution, emitter_index);
light_distribution, ls->emitter_id);
prim = kdistribution->prim;
mesh_light = kdistribution->mesh_light;
}
/* A different value would be assigned in `triangle_light_sample()` if `!use_light_tree`. */
ls->pdf_selection = pdf_selection;
if (prim >= 0) {
/* Mesh light. */
const int object = mesh_light.object_id;

View File

@ -329,17 +329,25 @@ ccl_device_inline bool light_sample_from_volume_segment(KernelGlobals kg,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
return light_tree_sample<true>(
kg, rand, time, P, D, t, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
if (!light_tree_sample<true>(kg, rand.z, P, D, t, object_receiver, shader_flags, ls)) {
return false;
}
}
else
#endif
{
return light_distribution_sample<true>(
kg, rand, time, P, D, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
if (!light_distribution_sample(kg, rand.z, ls)) {
return false;
}
}
/* Sample position on the selected light. */
return light_sample<true>(
kg, rand, time, P, D, object_receiver, shader_flags, bounce, path_flag, ls);
}
ccl_device bool light_sample_from_position(KernelGlobals kg,
@ -354,17 +362,24 @@ ccl_device bool light_sample_from_position(KernelGlobals kg,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
/* Randomly select a light. */
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
return light_tree_sample<false>(
kg, rand, time, P, N, 0.0f, object_receiver, shader_flags, bounce, path_flag, ls);
if (!light_tree_sample<false>(kg, rand.z, P, N, 0.0f, object_receiver, shader_flags, ls)) {
return false;
}
}
else
#endif
{
return light_distribution_sample<false>(
kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls);
if (!light_distribution_sample(kg, rand.z, ls)) {
return false;
}
}
/* Sample position on the selected light. */
return light_sample<false>(
kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls);
}
/* Update light sample with new shading point position for MNEE. The position on the light is fixed
@ -415,13 +430,15 @@ ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
uint lookup_offset = kernel_data_fetch(object_lookup_offset, sd->object);
uint prim_offset = kernel_data_fetch(object_prim_offset, sd->object);
uint triangle = kernel_data_fetch(triangle_to_tree, sd->prim - prim_offset + lookup_offset);
pdf *= light_tree_pdf(
kg, ray_P, N, path_flag, sd->object, triangle, light_link_receiver_forward(kg, state));
kg, ray_P, N, dt, path_flag, sd->object, triangle, light_link_receiver_forward(kg, state));
}
else
#endif
@ -445,9 +462,11 @@ ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg,
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
pdf *= light_tree_pdf(kg,
P,
N,
dt,
path_flag,
0,
kernel_data_fetch(light_to_tree, ls->lamp),
@ -485,9 +504,10 @@ ccl_device_inline float light_sample_mis_weight_forward_background(KernelGlobals
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
uint light = kernel_data_fetch(light_to_tree, kernel_data.background.light_index);
pdf *= light_tree_pdf(
kg, ray_P, N, path_flag, 0, light, light_link_receiver_forward(kg, state));
kg, ray_P, N, dt, path_flag, 0, light, light_link_receiver_forward(kg, state));
}
else
#endif

View File

@ -148,10 +148,7 @@ ccl_device void light_tree_importance(const float3 N_or_D,
float cos_min_incidence_angle = 1.0f;
float cos_max_incidence_angle = 1.0f;
/* When sampling the light tree for the second time in `shade_volume.h` and when query the pdf in
* `sample.h`. */
const bool in_volume = is_zero(N_or_D);
if (!in_volume_segment && !in_volume) {
if (!in_volume_segment) {
const float3 N = N_or_D;
const float cos_theta_i = has_transmission ? fabsf(dot(point_to_centroid, N)) :
dot(point_to_centroid, N);
@ -221,9 +218,9 @@ ccl_device void light_tree_importance(const float3 N_or_D,
max_importance = fabsf(f_a * cos_min_incidence_angle * energy * cos_min_outgoing_angle /
(in_volume_segment ? min_distance : sqr(min_distance)));
/* TODO: also min importance for volume? */
/* TODO: compute proper min importance for volume. */
if (in_volume_segment) {
min_importance = max_importance;
min_importance = 0.0f;
return;
}
@ -270,10 +267,10 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
/* Arbitrary centroid and direction. */
centroid = make_float3(0.0f, 0.0f, 1.0f);
dir = make_float3(0.0f, 0.0f, -1.0f);
return !in_volume_segment;
break;
case LIGHT_DISTANT:
dir = centroid;
return !in_volume_segment;
break;
default:
return false;
}
@ -323,12 +320,13 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
float cos_theta_u;
float distance;
if (knode->type == LIGHT_TREE_DISTANT) {
if (in_volume_segment) {
return;
}
point_to_centroid = -bcone.axis;
cos_theta_u = fast_cosf(bcone.theta_o + bcone.theta_e);
distance = 1.0f;
if (t == FLT_MAX) {
/* In world volume, distant light has no contribution. */
return;
}
}
else {
const float3 centroid = 0.5f * (bbox.min + bbox.max);
@ -339,6 +337,9 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
/* Minimal distance of the ray to the cluster. */
distance = len(centroid - closest_point);
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
/* FIXME(weizhen): it is not clear from which point the `cos_theta_u` should be computed in
* volume segment. We could use `closest_point` as a conservative measure, but then
* `point_to_centroid` should also use `closest_point`. */
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, closest_point, point_to_centroid);
}
else {
@ -697,17 +698,16 @@ ccl_device int light_tree_root_node_index(KernelGlobals kg, const int object_rec
return 0;
}
/* Pick a random light from the light tree from a given shading point P, write to the picked light
* index and the probability of picking the light. */
template<bool in_volume_segment>
ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
const float3 rand,
const float time,
const float rand,
const float3 P,
float3 N_or_D,
float t,
const int object_receiver,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
if (!kernel_data.integrator.use_direct_light) {
@ -718,10 +718,8 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
float pdf_leaf = 1.0f;
float pdf_selection = 1.0f;
int selected_emitter = -1;
int object_emitter = 0;
int node_index = light_tree_root_node_index(kg, object_receiver);
/* The first two dimensions of the Sobol sequence have better stratification. */
float rand_selection = rand.z;
float rand_selection = rand;
float3 local_P = P;
@ -743,7 +741,7 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
}
/* Continue with the picked mesh light. */
object_emitter = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
ls->object = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
continue;
}
@ -766,27 +764,18 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
pdf_leaf *= (node_index == left_index) ? left_prob : (1.0f - left_prob);
}
pdf_selection *= pdf_leaf;
ls->emitter_id = selected_emitter;
ls->pdf_selection = pdf_selection * pdf_leaf;
return light_sample<in_volume_segment>(kg,
float3_to_float2(rand),
time,
P,
N_or_D,
object_receiver,
shader_flags,
bounce,
path_flag,
selected_emitter,
object_emitter,
pdf_selection,
ls);
return true;
}
/* We need to be able to find the probability of selecting a given light for MIS. */
template<bool in_volume_segment>
ccl_device float light_tree_pdf(KernelGlobals kg,
float3 P,
float3 N,
const float dt,
const int path_flag,
const int object_emitter,
const uint index_emitter,
@ -837,8 +826,8 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
for (int i = 0; i < knode->num_emitters; i++) {
const int emitter = knode->leaf.first_emitter + i;
float max_importance, min_importance;
light_tree_emitter_importance<false>(
kg, P, N, 0, has_transmission, emitter, max_importance, min_importance);
light_tree_emitter_importance<in_volume_segment>(
kg, P, N, dt, has_transmission, emitter, max_importance, min_importance);
num_has_importance += (max_importance > 0);
if (emitter == target_emitter) {
target_max_importance = max_importance;
@ -860,7 +849,7 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
if (subtree_root_index != -1) {
/* Arrived at the mesh light. Continue with the subtree. */
float unused;
light_tree_to_local_space<false>(kg, object_emitter, P, N, unused);
light_tree_to_local_space<in_volume_segment>(kg, object_emitter, P, N, unused);
node_index = subtree_root_index;
subtree_root_index = -1;
@ -878,8 +867,8 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
const int right_index = knode->inner.right_child;
float left_prob;
if (!get_left_probability<false>(
kg, P, N, 0, has_transmission, left_index, right_index, left_prob))
if (!get_left_probability<in_volume_segment>(
kg, P, N, dt, has_transmission, left_index, right_index, left_prob))
{
return 0.0f;
}
@ -897,4 +886,27 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
}
}
/* If the function is called in volume, retrieve the previous point in volume segment, and compute
* pdf from there. Otherwise compute from the current shading point. */
ccl_device_inline float light_tree_pdf(KernelGlobals kg,
float3 P,
float3 N,
const float dt,
const int path_flag,
const int emitter_object,
const uint emitter_id,
const int object_receiver)
{
if (path_flag & PATH_RAY_VOLUME_SCATTER) {
const float3 D_times_t = N;
const float3 D = normalize(D_times_t);
P = P - D_times_t;
return light_tree_pdf<true>(
kg, P, D, dt, path_flag, emitter_object, emitter_id, object_receiver);
}
return light_tree_pdf<false>(
kg, P, N, 0.0f, path_flag, emitter_object, emitter_id, object_receiver);
}
CCL_NAMESPACE_END

View File

@ -285,7 +285,8 @@ ccl_device_inline bool triangle_light_valid_ray_segment(KernelGlobals kg,
/* Only one side is sampled, intersect the ray and the triangle light plane to find the visible
* ray segment. Flip normal if Emission Sampling is set to back. */
return ray_plane_intersect((shader_flag & SD_MIS_BACK) ? -ls->Ng : ls->Ng, P, D, t_range);
const float3 N = ls->Ng;
return ray_plane_intersect((shader_flag & SD_MIS_BACK) ? -N : N, P, D, t_range);
}
template<bool in_volume_segment>
@ -326,9 +327,8 @@ ccl_device_forceinline bool triangle_light_tree_parameters(
}
const bool front_facing = bcone.theta_o != 0.0f || dot(bcone.axis, point_to_centroid) < 0;
const bool in_volume = is_zero(N);
return (front_facing && shape_above_surface) || in_volume;
return front_facing && shape_above_surface;
}
CCL_NAMESPACE_END

View File

@ -45,6 +45,7 @@ CCL_NAMESPACE_BEGIN
#define OBJECT_NONE (~0)
#define PRIM_NONE (~0)
#define LAMP_NONE (~0)
#define EMITTER_NONE (~0)
#define ID_NONE (0.0f)
#define PASS_UNUSED (~0)
#define LIGHTGROUP_NONE (~0)

@ -1 +1 @@
Subproject commit 00af9c65712b6aa78ce6eb0c62c5aafb7a867f18
Subproject commit 9ce3adf54dae89a9daaa4dcd04cc3a566aed3aaf