Cycles: GPU code generation optimizations for direct lighting
Use a single loop to iterate over all lights, reducing divergence and amount of code to generate. Moving ray intersection calls out of conditionals will also help the Optix compiler. Ref D5363
This commit is contained in:

committed by
Brecht Van Lommel

parent
f491c23f1e
commit
fd52dc58dd
@@ -1076,7 +1076,7 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu)
|
|||||||
int len = kernel_data.integrator.num_distribution + 1;
|
int len = kernel_data.integrator.num_distribution + 1;
|
||||||
float r = *randu;
|
float r = *randu;
|
||||||
|
|
||||||
while (len > 0) {
|
do {
|
||||||
int half_len = len >> 1;
|
int half_len = len >> 1;
|
||||||
int middle = first + half_len;
|
int middle = first + half_len;
|
||||||
|
|
||||||
@@ -1087,7 +1087,7 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu)
|
|||||||
first = middle + 1;
|
first = middle + 1;
|
||||||
len = len - half_len - 1;
|
len = len - half_len - 1;
|
||||||
}
|
}
|
||||||
}
|
} while (len > 0);
|
||||||
|
|
||||||
/* Clamping should not be needed but float rounding errors seem to
|
/* Clamping should not be needed but float rounding errors seem to
|
||||||
* make this fail on rare occasions. */
|
* make this fail on rare occasions. */
|
||||||
@@ -1104,42 +1104,49 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu)
|
|||||||
|
|
||||||
/* Generic Light */
|
/* Generic Light */
|
||||||
|
|
||||||
ccl_device bool light_select_reached_max_bounces(KernelGlobals *kg, int index, int bounce)
|
ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals *kg, int index, int bounce)
|
||||||
{
|
{
|
||||||
return (bounce > kernel_tex_fetch(__lights, index).max_bounces);
|
return (bounce > kernel_tex_fetch(__lights, index).max_bounces);
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device_noinline bool light_sample(
|
ccl_device_noinline bool light_sample(KernelGlobals *kg,
|
||||||
KernelGlobals *kg, float randu, float randv, float time, float3 P, int bounce, LightSample *ls)
|
int lamp,
|
||||||
|
float randu,
|
||||||
|
float randv,
|
||||||
|
float time,
|
||||||
|
float3 P,
|
||||||
|
int bounce,
|
||||||
|
LightSample *ls)
|
||||||
{
|
{
|
||||||
/* sample index */
|
if (lamp < 0) {
|
||||||
int index = light_distribution_sample(kg, &randu);
|
/* sample index */
|
||||||
|
int index = light_distribution_sample(kg, &randu);
|
||||||
|
|
||||||
/* fetch light data */
|
/* fetch light data */
|
||||||
const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(__light_distribution,
|
const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(
|
||||||
index);
|
__light_distribution, index);
|
||||||
int prim = kdistribution->prim;
|
int prim = kdistribution->prim;
|
||||||
|
|
||||||
if (prim >= 0) {
|
if (prim >= 0) {
|
||||||
int object = kdistribution->mesh_light.object_id;
|
int object = kdistribution->mesh_light.object_id;
|
||||||
int shader_flag = kdistribution->mesh_light.shader_flag;
|
int shader_flag = kdistribution->mesh_light.shader_flag;
|
||||||
|
|
||||||
triangle_light_sample(kg, prim, object, randu, randv, time, ls, P);
|
triangle_light_sample(kg, prim, object, randu, randv, time, ls, P);
|
||||||
ls->shader |= shader_flag;
|
ls->shader |= shader_flag;
|
||||||
return (ls->pdf > 0.0f);
|
return (ls->pdf > 0.0f);
|
||||||
}
|
|
||||||
else {
|
|
||||||
int lamp = -prim - 1;
|
|
||||||
|
|
||||||
if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lamp_light_sample(kg, lamp, randu, randv, P, ls);
|
lamp = -prim - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lamp_light_sample(kg, lamp, randu, randv, P, ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device int light_select_num_samples(KernelGlobals *kg, int index)
|
ccl_device_inline int light_select_num_samples(KernelGlobals *kg, int index)
|
||||||
{
|
{
|
||||||
return kernel_tex_fetch(__lights, index).samples;
|
return kernel_tex_fetch(__lights, index).samples;
|
||||||
}
|
}
|
||||||
|
@@ -474,12 +474,10 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
|
|||||||
# endif /* __SUBSURFACE__ */
|
# endif /* __SUBSURFACE__ */
|
||||||
|
|
||||||
# if defined(__EMISSION__)
|
# if defined(__EMISSION__)
|
||||||
if (kernel_data.integrator.use_direct_light) {
|
int all = (kernel_data.integrator.sample_all_lights_indirect) ||
|
||||||
int all = (kernel_data.integrator.sample_all_lights_indirect) ||
|
(state->flag & PATH_RAY_SHADOW_CATCHER);
|
||||||
(state->flag & PATH_RAY_SHADOW_CATCHER);
|
kernel_branched_path_surface_connect_light(
|
||||||
kernel_branched_path_surface_connect_light(
|
kg, sd, emission_sd, state, throughput, 1.0f, L, all);
|
||||||
kg, sd, emission_sd, state, throughput, 1.0f, L, all);
|
|
||||||
}
|
|
||||||
# endif /* defined(__EMISSION__) */
|
# endif /* defined(__EMISSION__) */
|
||||||
|
|
||||||
# ifdef __VOLUME__
|
# ifdef __VOLUME__
|
||||||
|
@@ -32,140 +32,100 @@ ccl_device_noinline_cpu void kernel_branched_path_surface_connect_light(
|
|||||||
{
|
{
|
||||||
# ifdef __EMISSION__
|
# ifdef __EMISSION__
|
||||||
/* sample illumination from lights to find path contribution */
|
/* sample illumination from lights to find path contribution */
|
||||||
if (!(sd->flag & SD_BSDF_HAS_EVAL))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Ray light_ray;
|
|
||||||
BsdfEval L_light;
|
BsdfEval L_light;
|
||||||
bool is_lamp;
|
|
||||||
|
|
||||||
|
int num_lights = 0;
|
||||||
|
if (kernel_data.integrator.use_direct_light) {
|
||||||
|
if (sample_all_lights) {
|
||||||
|
num_lights = kernel_data.integrator.num_all_lights;
|
||||||
|
if (kernel_data.integrator.pdf_triangles != 0.0f) {
|
||||||
|
num_lights += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
num_lights = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_lights; i++) {
|
||||||
|
/* sample one light at random */
|
||||||
|
int num_samples = 1;
|
||||||
|
int num_all_lights = 1;
|
||||||
|
uint lamp_rng_hash = state->rng_hash;
|
||||||
|
bool double_pdf = false;
|
||||||
|
bool is_mesh_light = false;
|
||||||
|
bool is_lamp = false;
|
||||||
|
|
||||||
|
if (sample_all_lights) {
|
||||||
|
/* lamp sampling */
|
||||||
|
is_lamp = i < kernel_data.integrator.num_all_lights;
|
||||||
|
if (is_lamp) {
|
||||||
|
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
num_samples = ceil_to_int(num_samples_adjust * light_select_num_samples(kg, i));
|
||||||
|
num_all_lights = kernel_data.integrator.num_all_lights;
|
||||||
|
lamp_rng_hash = cmj_hash(state->rng_hash, i);
|
||||||
|
double_pdf = kernel_data.integrator.pdf_triangles != 0.0f;
|
||||||
|
}
|
||||||
|
/* mesh light sampling */
|
||||||
|
else {
|
||||||
|
num_samples = ceil_to_int(num_samples_adjust * kernel_data.integrator.mesh_light_samples);
|
||||||
|
double_pdf = kernel_data.integrator.num_all_lights != 0;
|
||||||
|
is_mesh_light = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float num_samples_inv = num_samples_adjust / (num_samples * num_all_lights);
|
||||||
|
|
||||||
|
for (int j = 0; j < num_samples; j++) {
|
||||||
|
Ray light_ray;
|
||||||
|
light_ray.t = 0.0f; /* reset ray */
|
||||||
# ifdef __OBJECT_MOTION__
|
# ifdef __OBJECT_MOTION__
|
||||||
light_ray.time = sd->time;
|
light_ray.time = sd->time;
|
||||||
# endif
|
# endif
|
||||||
|
bool has_emission = false;
|
||||||
|
|
||||||
if (sample_all_lights) {
|
if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) {
|
||||||
/* lamp sampling */
|
|
||||||
for (int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
|
|
||||||
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int num_samples = ceil_to_int(num_samples_adjust * light_select_num_samples(kg, i));
|
|
||||||
float num_samples_inv = num_samples_adjust /
|
|
||||||
(num_samples * kernel_data.integrator.num_all_lights);
|
|
||||||
uint lamp_rng_hash = cmj_hash(state->rng_hash, i);
|
|
||||||
|
|
||||||
for (int j = 0; j < num_samples; j++) {
|
|
||||||
float light_u, light_v;
|
float light_u, light_v;
|
||||||
path_branched_rng_2D(
|
path_branched_rng_2D(
|
||||||
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
float terminate = path_branched_rng_light_termination(
|
float terminate = path_branched_rng_light_termination(
|
||||||
kg, lamp_rng_hash, state, j, num_samples);
|
kg, lamp_rng_hash, state, j, num_samples);
|
||||||
|
|
||||||
LightSample ls;
|
|
||||||
if (lamp_light_sample(kg, i, light_u, light_v, sd->P, &ls)) {
|
|
||||||
/* The sampling probability returned by lamp_light_sample assumes that all lights were
|
|
||||||
* sampled.
|
|
||||||
* However, this code only samples lamps, so if the scene also had mesh lights, the real
|
|
||||||
* probability is twice as high. */
|
|
||||||
if (kernel_data.integrator.pdf_triangles != 0.0f)
|
|
||||||
ls.pdf *= 2.0f;
|
|
||||||
|
|
||||||
if (direct_emission(
|
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L,
|
|
||||||
state,
|
|
||||||
throughput * num_samples_inv,
|
|
||||||
&L_light,
|
|
||||||
shadow,
|
|
||||||
num_samples_inv,
|
|
||||||
is_lamp);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
path_radiance_accum_total_light(L, state, throughput * num_samples_inv, &L_light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* mesh light sampling */
|
|
||||||
if (kernel_data.integrator.pdf_triangles != 0.0f) {
|
|
||||||
int num_samples = ceil_to_int(num_samples_adjust *
|
|
||||||
kernel_data.integrator.mesh_light_samples);
|
|
||||||
float num_samples_inv = num_samples_adjust / num_samples;
|
|
||||||
|
|
||||||
for (int j = 0; j < num_samples; j++) {
|
|
||||||
float light_u, light_v;
|
|
||||||
path_branched_rng_2D(
|
|
||||||
kg, state->rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
|
||||||
float terminate = path_branched_rng_light_termination(
|
|
||||||
kg, state->rng_hash, state, j, num_samples);
|
|
||||||
|
|
||||||
/* only sample triangle lights */
|
/* only sample triangle lights */
|
||||||
if (kernel_data.integrator.num_all_lights)
|
if (is_mesh_light && double_pdf) {
|
||||||
light_u = 0.5f * light_u;
|
light_u = 0.5f * light_u;
|
||||||
|
}
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
if (light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
const int lamp = is_lamp ? i : -1;
|
||||||
/* Same as above, probability needs to be corrected since the sampling was forced to
|
if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
||||||
* select a mesh light. */
|
/* The sampling probability returned by lamp_light_sample assumes that all lights were
|
||||||
if (kernel_data.integrator.num_all_lights)
|
* sampled. However, this code only samples lamps, so if the scene also had mesh lights,
|
||||||
|
* the real probability is twice as high. */
|
||||||
|
if (double_pdf) {
|
||||||
ls.pdf *= 2.0f;
|
ls.pdf *= 2.0f;
|
||||||
|
|
||||||
if (direct_emission(
|
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L,
|
|
||||||
state,
|
|
||||||
throughput * num_samples_inv,
|
|
||||||
&L_light,
|
|
||||||
shadow,
|
|
||||||
num_samples_inv,
|
|
||||||
is_lamp);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
path_radiance_accum_total_light(L, state, throughput * num_samples_inv, &L_light);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
has_emission = direct_emission(
|
||||||
|
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* sample one light at random */
|
|
||||||
float light_u, light_v;
|
|
||||||
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
|
|
||||||
float terminate = path_state_rng_light_termination(kg, state);
|
|
||||||
|
|
||||||
LightSample ls;
|
/* trace shadow ray */
|
||||||
if (light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
float3 shadow;
|
||||||
/* sample random light */
|
|
||||||
if (direct_emission(
|
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
|
||||||
|
|
||||||
|
if (has_emission) {
|
||||||
|
if (!blocked) {
|
||||||
/* accumulate */
|
/* accumulate */
|
||||||
path_radiance_accum_light(L,
|
path_radiance_accum_light(
|
||||||
state,
|
L, state, throughput * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp);
|
||||||
throughput * num_samples_adjust,
|
|
||||||
&L_light,
|
|
||||||
shadow,
|
|
||||||
num_samples_adjust,
|
|
||||||
is_lamp);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
path_radiance_accum_total_light(L, state, throughput * num_samples_adjust, &L_light);
|
path_radiance_accum_total_light(L, state, throughput * num_samples_inv, &L_light);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,45 +215,48 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg,
|
|||||||
PROFILING_INIT(kg, PROFILING_CONNECT_LIGHT);
|
PROFILING_INIT(kg, PROFILING_CONNECT_LIGHT);
|
||||||
|
|
||||||
#ifdef __EMISSION__
|
#ifdef __EMISSION__
|
||||||
if (!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
# ifdef __SHADOW_TRICKS__
|
# ifdef __SHADOW_TRICKS__
|
||||||
if (state->flag & PATH_RAY_SHADOW_CATCHER) {
|
int all = (state->flag & PATH_RAY_SHADOW_CATCHER);
|
||||||
kernel_branched_path_surface_connect_light(kg, sd, emission_sd, state, throughput, 1.0f, L, 1);
|
kernel_branched_path_surface_connect_light(kg, sd, emission_sd, state, throughput, 1.0f, L, all);
|
||||||
return;
|
# else
|
||||||
}
|
|
||||||
# endif
|
|
||||||
|
|
||||||
/* sample illumination from lights to find path contribution */
|
/* sample illumination from lights to find path contribution */
|
||||||
float light_u, light_v;
|
|
||||||
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
|
|
||||||
|
|
||||||
Ray light_ray;
|
Ray light_ray;
|
||||||
BsdfEval L_light;
|
BsdfEval L_light;
|
||||||
bool is_lamp;
|
bool is_lamp = false;
|
||||||
|
bool has_emission = false;
|
||||||
|
|
||||||
# ifdef __OBJECT_MOTION__
|
light_ray.t = 0.0f;
|
||||||
|
# ifdef __OBJECT_MOTION__
|
||||||
light_ray.time = sd->time;
|
light_ray.time = sd->time;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
LightSample ls;
|
if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) {
|
||||||
if (light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
float light_u, light_v;
|
||||||
float terminate = path_state_rng_light_termination(kg, state);
|
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
if (direct_emission(
|
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
LightSample ls;
|
||||||
/* accumulate */
|
if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
||||||
path_radiance_accum_light(L, state, throughput, &L_light, shadow, 1.0f, is_lamp);
|
float terminate = path_state_rng_light_termination(kg, state);
|
||||||
}
|
has_emission = direct_emission(
|
||||||
else {
|
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
|
||||||
path_radiance_accum_total_light(L, state, throughput, &L_light);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
|
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
|
||||||
|
|
||||||
|
if (has_emission) {
|
||||||
|
if (!blocked) {
|
||||||
|
/* accumulate */
|
||||||
|
path_radiance_accum_light(L, state, throughput, &L_light, shadow, 1.0f, is_lamp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
path_radiance_accum_total_light(L, state, throughput, &L_light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,34 +26,39 @@ ccl_device_inline void kernel_path_volume_connect_light(KernelGlobals *kg,
|
|||||||
PathRadiance *L)
|
PathRadiance *L)
|
||||||
{
|
{
|
||||||
# ifdef __EMISSION__
|
# ifdef __EMISSION__
|
||||||
if (!kernel_data.integrator.use_direct_light)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* sample illumination from lights to find path contribution */
|
/* sample illumination from lights to find path contribution */
|
||||||
float light_u, light_v;
|
|
||||||
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
|
|
||||||
|
|
||||||
Ray light_ray;
|
Ray light_ray;
|
||||||
BsdfEval L_light;
|
BsdfEval L_light;
|
||||||
LightSample ls;
|
bool is_lamp = false;
|
||||||
bool is_lamp;
|
bool has_emission = false;
|
||||||
|
|
||||||
|
light_ray.t = 0.0f;
|
||||||
|
# ifdef __OBJECT_MOTION__
|
||||||
/* connect to light from given point where shader has been evaluated */
|
/* connect to light from given point where shader has been evaluated */
|
||||||
light_ray.time = sd->time;
|
light_ray.time = sd->time;
|
||||||
|
# endif
|
||||||
|
|
||||||
if (light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
if (kernel_data.integrator.use_direct_light) {
|
||||||
float terminate = path_state_rng_light_termination(kg, state);
|
float light_u, light_v;
|
||||||
if (direct_emission(
|
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
LightSample ls;
|
||||||
/* accumulate */
|
if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
||||||
path_radiance_accum_light(L, state, throughput, &L_light, shadow, 1.0f, is_lamp);
|
float terminate = path_state_rng_light_termination(kg, state);
|
||||||
}
|
has_emission = direct_emission(
|
||||||
|
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
|
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
|
||||||
|
|
||||||
|
if (has_emission && !blocked) {
|
||||||
|
/* accumulate */
|
||||||
|
path_radiance_accum_light(L, state, throughput, &L_light, shadow, 1.0f, is_lamp);
|
||||||
|
}
|
||||||
# endif /* __EMISSION__ */
|
# endif /* __EMISSION__ */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +127,7 @@ ccl_device_noinline_cpu bool kernel_path_volume_bounce(KernelGlobals *kg,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifndef __SPLIT_KERNEL__
|
# if !defined(__SPLIT_KERNEL__) && (defined(__BRANCHED_PATH__) || defined(__VOLUME_DECOUPLED__))
|
||||||
ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg,
|
ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg,
|
||||||
ShaderData *sd,
|
ShaderData *sd,
|
||||||
ShaderData *emission_sd,
|
ShaderData *emission_sd,
|
||||||
@@ -134,96 +139,71 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg,
|
|||||||
const VolumeSegment *segment)
|
const VolumeSegment *segment)
|
||||||
{
|
{
|
||||||
# ifdef __EMISSION__
|
# ifdef __EMISSION__
|
||||||
if (!kernel_data.integrator.use_direct_light)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Ray light_ray;
|
|
||||||
BsdfEval L_light;
|
BsdfEval L_light;
|
||||||
bool is_lamp;
|
|
||||||
|
|
||||||
light_ray.time = sd->time;
|
|
||||||
|
|
||||||
|
int num_lights = 1;
|
||||||
if (sample_all_lights) {
|
if (sample_all_lights) {
|
||||||
/* lamp sampling */
|
num_lights = kernel_data.integrator.num_all_lights;
|
||||||
for (int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
|
if (kernel_data.integrator.pdf_triangles != 0.0f) {
|
||||||
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce)))
|
num_lights += 1;
|
||||||
continue;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int num_samples = light_select_num_samples(kg, i);
|
for (int i = 0; i < num_lights; ++i) {
|
||||||
float num_samples_inv = 1.0f / (num_samples * kernel_data.integrator.num_all_lights);
|
/* sample one light at random */
|
||||||
uint lamp_rng_hash = cmj_hash(state->rng_hash, i);
|
int num_samples = 1;
|
||||||
|
int num_all_lights = 1;
|
||||||
|
uint lamp_rng_hash = state->rng_hash;
|
||||||
|
bool double_pdf = false;
|
||||||
|
bool is_mesh_light = false;
|
||||||
|
bool is_lamp = false;
|
||||||
|
|
||||||
for (int j = 0; j < num_samples; j++) {
|
if (sample_all_lights) {
|
||||||
/* sample random position on given light */
|
/* lamp sampling */
|
||||||
|
is_lamp = i < kernel_data.integrator.num_all_lights;
|
||||||
|
if (is_lamp) {
|
||||||
|
if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
num_samples = light_select_num_samples(kg, i);
|
||||||
|
num_all_lights = kernel_data.integrator.num_all_lights;
|
||||||
|
lamp_rng_hash = cmj_hash(state->rng_hash, i);
|
||||||
|
double_pdf = kernel_data.integrator.pdf_triangles != 0.0f;
|
||||||
|
}
|
||||||
|
/* mesh light sampling */
|
||||||
|
else {
|
||||||
|
num_samples = kernel_data.integrator.mesh_light_samples;
|
||||||
|
double_pdf = kernel_data.integrator.num_all_lights != 0;
|
||||||
|
is_mesh_light = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float num_samples_inv = 1.0f / (num_samples * num_all_lights);
|
||||||
|
|
||||||
|
for (int j = 0; j < num_samples; j++) {
|
||||||
|
Ray light_ray;
|
||||||
|
light_ray.t = 0.0f; /* reset ray */
|
||||||
|
# ifdef __OBJECT_MOTION__
|
||||||
|
light_ray.time = sd->time;
|
||||||
|
# endif
|
||||||
|
bool has_emission = false;
|
||||||
|
|
||||||
|
float3 tp = throughput;
|
||||||
|
|
||||||
|
if (kernel_data.integrator.use_direct_light) {
|
||||||
|
/* sample random position on random light/triangle */
|
||||||
float light_u, light_v;
|
float light_u, light_v;
|
||||||
path_branched_rng_2D(
|
path_branched_rng_2D(
|
||||||
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
|
|
||||||
LightSample ls;
|
|
||||||
lamp_light_sample(kg, i, light_u, light_v, ray->P, &ls);
|
|
||||||
|
|
||||||
float3 tp = throughput;
|
|
||||||
|
|
||||||
/* sample position on volume segment */
|
|
||||||
float rphase = path_branched_rng_1D(
|
|
||||||
kg, state->rng_hash, state, j, num_samples, PRNG_PHASE_CHANNEL);
|
|
||||||
float rscatter = path_branched_rng_1D(
|
|
||||||
kg, state->rng_hash, state, j, num_samples, PRNG_SCATTER_DISTANCE);
|
|
||||||
|
|
||||||
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
|
|
||||||
state,
|
|
||||||
ray,
|
|
||||||
sd,
|
|
||||||
&tp,
|
|
||||||
rphase,
|
|
||||||
rscatter,
|
|
||||||
segment,
|
|
||||||
(ls.t != FLT_MAX) ? &ls.P :
|
|
||||||
NULL,
|
|
||||||
false);
|
|
||||||
|
|
||||||
/* todo: split up light_sample so we don't have to call it again with new position */
|
|
||||||
if (result == VOLUME_PATH_SCATTERED &&
|
|
||||||
lamp_light_sample(kg, i, light_u, light_v, sd->P, &ls)) {
|
|
||||||
if (kernel_data.integrator.pdf_triangles != 0.0f)
|
|
||||||
ls.pdf *= 2.0f;
|
|
||||||
|
|
||||||
float terminate = path_branched_rng_light_termination(
|
|
||||||
kg, state->rng_hash, state, j, num_samples);
|
|
||||||
if (direct_emission(
|
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(
|
|
||||||
L, state, tp * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* mesh light sampling */
|
|
||||||
if (kernel_data.integrator.pdf_triangles != 0.0f) {
|
|
||||||
int num_samples = kernel_data.integrator.mesh_light_samples;
|
|
||||||
float num_samples_inv = 1.0f / num_samples;
|
|
||||||
|
|
||||||
for (int j = 0; j < num_samples; j++) {
|
|
||||||
/* sample random position on random triangle */
|
|
||||||
float light_u, light_v;
|
|
||||||
path_branched_rng_2D(
|
|
||||||
kg, state->rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
|
||||||
|
|
||||||
/* only sample triangle lights */
|
/* only sample triangle lights */
|
||||||
if (kernel_data.integrator.num_all_lights)
|
if (is_mesh_light && double_pdf) {
|
||||||
light_u = 0.5f * light_u;
|
light_u = 0.5f * light_u;
|
||||||
|
}
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
light_sample(kg, light_u, light_v, sd->time, ray->P, state->bounce, &ls);
|
const int lamp = is_lamp ? i : -1;
|
||||||
|
light_sample(kg, lamp, light_u, light_v, sd->time, ray->P, state->bounce, &ls);
|
||||||
float3 tp = throughput;
|
|
||||||
|
|
||||||
/* sample position on volume segment */
|
/* sample position on volume segment */
|
||||||
float rphase = path_branched_rng_1D(
|
float rphase = path_branched_rng_1D(
|
||||||
@@ -243,69 +223,31 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg,
|
|||||||
NULL,
|
NULL,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
/* todo: split up light_sample so we don't have to call it again with new position */
|
if (result == VOLUME_PATH_SCATTERED) {
|
||||||
if (result == VOLUME_PATH_SCATTERED &&
|
/* todo: split up light_sample so we don't have to call it again with new position */
|
||||||
light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
||||||
if (kernel_data.integrator.num_all_lights)
|
if (double_pdf) {
|
||||||
ls.pdf *= 2.0f;
|
ls.pdf *= 2.0f;
|
||||||
|
|
||||||
float terminate = path_branched_rng_light_termination(
|
|
||||||
kg, state->rng_hash, state, j, num_samples);
|
|
||||||
if (direct_emission(
|
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(
|
|
||||||
L, state, tp * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* sample random light */
|
||||||
|
float terminate = path_branched_rng_light_termination(
|
||||||
|
kg, state->rng_hash, state, j, num_samples);
|
||||||
|
has_emission = direct_emission(
|
||||||
|
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* sample random position on random light */
|
|
||||||
float light_u, light_v;
|
|
||||||
path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v);
|
|
||||||
|
|
||||||
LightSample ls;
|
/* trace shadow ray */
|
||||||
light_sample(kg, light_u, light_v, sd->time, ray->P, state->bounce, &ls);
|
float3 shadow;
|
||||||
|
|
||||||
float3 tp = throughput;
|
const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow);
|
||||||
|
|
||||||
/* sample position on volume segment */
|
if (has_emission && !blocked) {
|
||||||
float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL);
|
/* accumulate */
|
||||||
float rscatter = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE);
|
path_radiance_accum_light(
|
||||||
|
L, state, tp * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp);
|
||||||
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
|
|
||||||
state,
|
|
||||||
ray,
|
|
||||||
sd,
|
|
||||||
&tp,
|
|
||||||
rphase,
|
|
||||||
rscatter,
|
|
||||||
segment,
|
|
||||||
(ls.t != FLT_MAX) ? &ls.P :
|
|
||||||
NULL,
|
|
||||||
false);
|
|
||||||
|
|
||||||
/* todo: split up light_sample so we don't have to call it again with new position */
|
|
||||||
if (result == VOLUME_PATH_SCATTERED &&
|
|
||||||
light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
|
||||||
/* sample random light */
|
|
||||||
float terminate = path_state_rng_light_termination(kg, state);
|
|
||||||
if (direct_emission(
|
|
||||||
kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, state, tp, &L_light, shadow, 1.0f, is_lamp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -86,8 +86,7 @@ ccl_device void kernel_direct_lighting(KernelGlobals *kg,
|
|||||||
float terminate = path_state_rng_light_termination(kg, state);
|
float terminate = path_state_rng_light_termination(kg, state);
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
if (light_sample(kg, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
||||||
|
|
||||||
Ray light_ray;
|
Ray light_ray;
|
||||||
light_ray.time = sd->time;
|
light_ray.time = sd->time;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user