forked from bartvdbraak/blender
Cycles: Added support for light portals
This patch adds support for light portals: objects that help sampling the environment light, therefore improving convergence. Using them tor other lights in a unidirectional pathtracer is virtually useless. The sampling is done with the area-preserving code already used for area lamps. MIS is used both for combination of different portals and for combining portal- and envmap-sampling. The direction of portals is considered, they aren't used if the sampling point is behind them. Reviewers: sergey, dingto, #cycles Reviewed By: dingto, #cycles Subscribers: Lapineige, nutel, jtheninja, dsisco11, januz, vitorbalbio, candreacchio, TARDISMaker, lichtwerk, ace_dragon, marcog, mib2berlin, Tunge, lopataasdf, lordodin, sergey, dingto Differential Revision: https://developer.blender.org/D1133
This commit is contained in:
parent
84836e8952
commit
f478c2cfbd
@ -694,6 +694,12 @@ class CyclesLampSettings(bpy.types.PropertyGroup):
|
|||||||
"reduces noise for area lamps and sharp glossy materials",
|
"reduces noise for area lamps and sharp glossy materials",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
cls.is_portal = BoolProperty(
|
||||||
|
name="Is Portal",
|
||||||
|
description="Use this area lamp to guide sampling of the background, "
|
||||||
|
"note that this will make the lamp invisible",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unregister(cls):
|
def unregister(cls):
|
||||||
|
@ -743,7 +743,10 @@ class CyclesLamp_PT_preview(CyclesButtonsPanel, Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.lamp and CyclesButtonsPanel.poll(context)
|
return context.lamp and \
|
||||||
|
not (context.lamp.type == 'AREA' and
|
||||||
|
context.lamp.cycles.is_portal) \
|
||||||
|
and CyclesButtonsPanel.poll(context)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
self.layout.template_preview(context.lamp)
|
self.layout.template_preview(context.lamp)
|
||||||
@ -781,13 +784,21 @@ class CyclesLamp_PT_lamp(CyclesButtonsPanel, Panel):
|
|||||||
sub.prop(lamp, "size", text="Size X")
|
sub.prop(lamp, "size", text="Size X")
|
||||||
sub.prop(lamp, "size_y", text="Size Y")
|
sub.prop(lamp, "size_y", text="Size Y")
|
||||||
|
|
||||||
if cscene.progressive == 'BRANCHED_PATH':
|
if not (lamp.type == 'AREA' and clamp.is_portal):
|
||||||
col.prop(clamp, "samples")
|
sub = col.column(align=True)
|
||||||
col.prop(clamp, "max_bounces")
|
if cscene.progressive == 'BRANCHED_PATH':
|
||||||
|
sub.prop(clamp, "samples")
|
||||||
|
sub.prop(clamp, "max_bounces")
|
||||||
|
|
||||||
col = split.column()
|
col = split.column()
|
||||||
col.prop(clamp, "cast_shadow")
|
|
||||||
col.prop(clamp, "use_multiple_importance_sampling", text="Multiple Importance")
|
sub = col.column(align=True)
|
||||||
|
sub.active = not (lamp.type == 'AREA' and clamp.is_portal)
|
||||||
|
sub.prop(clamp, "cast_shadow")
|
||||||
|
sub.prop(clamp, "use_multiple_importance_sampling", text="Multiple Importance")
|
||||||
|
|
||||||
|
if lamp.type == 'AREA':
|
||||||
|
col.prop(clamp, "is_portal", text="Portal")
|
||||||
|
|
||||||
if lamp.type == 'HEMI':
|
if lamp.type == 'HEMI':
|
||||||
layout.label(text="Not supported, interpreted as sun lamp")
|
layout.label(text="Not supported, interpreted as sun lamp")
|
||||||
@ -799,7 +810,9 @@ class CyclesLamp_PT_nodes(CyclesButtonsPanel, Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.lamp and CyclesButtonsPanel.poll(context)
|
return context.lamp and not (context.lamp.type == 'AREA' and
|
||||||
|
context.lamp.cycles.is_portal) and \
|
||||||
|
CyclesButtonsPanel.poll(context)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
@ -90,14 +90,17 @@ static uint object_ray_visibility(BL::Object b_ob)
|
|||||||
|
|
||||||
/* Light */
|
/* Light */
|
||||||
|
|
||||||
void BlenderSync::sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::Object b_ob, Transform& tfm)
|
void BlenderSync::sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::Object b_ob, Transform& tfm, bool *use_portal)
|
||||||
{
|
{
|
||||||
/* test if we need to sync */
|
/* test if we need to sync */
|
||||||
Light *light;
|
Light *light;
|
||||||
ObjectKey key(b_parent, persistent_id, b_ob);
|
ObjectKey key(b_parent, persistent_id, b_ob);
|
||||||
|
|
||||||
if(!light_map.sync(&light, b_ob, b_parent, key))
|
if(!light_map.sync(&light, b_ob, b_parent, key)) {
|
||||||
|
if(light->is_portal)
|
||||||
|
*use_portal = true;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BL::Lamp b_lamp(b_ob.data());
|
BL::Lamp b_lamp(b_ob.data());
|
||||||
|
|
||||||
@ -171,6 +174,14 @@ void BlenderSync::sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSI
|
|||||||
|
|
||||||
light->max_bounces = get_int(clamp, "max_bounces");
|
light->max_bounces = get_int(clamp, "max_bounces");
|
||||||
|
|
||||||
|
if(light->type == LIGHT_AREA)
|
||||||
|
light->is_portal = get_boolean(clamp, "is_portal");
|
||||||
|
else
|
||||||
|
light->is_portal = false;
|
||||||
|
|
||||||
|
if(light->is_portal)
|
||||||
|
*use_portal = true;
|
||||||
|
|
||||||
/* visibility */
|
/* visibility */
|
||||||
uint visibility = object_ray_visibility(b_ob);
|
uint visibility = object_ray_visibility(b_ob);
|
||||||
light->use_diffuse = (visibility & PATH_RAY_DIFFUSE) != 0;
|
light->use_diffuse = (visibility & PATH_RAY_DIFFUSE) != 0;
|
||||||
@ -182,7 +193,7 @@ void BlenderSync::sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSI
|
|||||||
light->tag_update(scene);
|
light->tag_update(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlenderSync::sync_background_light()
|
void BlenderSync::sync_background_light(bool use_portal)
|
||||||
{
|
{
|
||||||
BL::World b_world = b_scene.world();
|
BL::World b_world = b_scene.world();
|
||||||
|
|
||||||
@ -191,19 +202,20 @@ void BlenderSync::sync_background_light()
|
|||||||
PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
|
PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
|
||||||
bool sample_as_light = get_boolean(cworld, "sample_as_light");
|
bool sample_as_light = get_boolean(cworld, "sample_as_light");
|
||||||
|
|
||||||
if(sample_as_light) {
|
if(sample_as_light || use_portal) {
|
||||||
/* test if we need to sync */
|
/* test if we need to sync */
|
||||||
Light *light;
|
Light *light;
|
||||||
ObjectKey key(b_world, 0, b_world);
|
ObjectKey key(b_world, 0, b_world);
|
||||||
|
|
||||||
if(light_map.sync(&light, b_world, b_world, key) ||
|
if(light_map.sync(&light, b_world, b_world, key) ||
|
||||||
world_recalc ||
|
world_recalc ||
|
||||||
b_world.ptr.data != world_map)
|
b_world.ptr.data != world_map)
|
||||||
{
|
{
|
||||||
light->type = LIGHT_BACKGROUND;
|
light->type = LIGHT_BACKGROUND;
|
||||||
light->map_resolution = get_int(cworld, "sample_map_resolution");
|
light->map_resolution = get_int(cworld, "sample_map_resolution");
|
||||||
light->shader = scene->default_background;
|
light->shader = scene->default_background;
|
||||||
|
light->use_mis = sample_as_light;
|
||||||
|
|
||||||
int samples = get_int(cworld, "samples");
|
int samples = get_int(cworld, "samples");
|
||||||
if(get_boolean(cscene, "use_square_samples"))
|
if(get_boolean(cscene, "use_square_samples"))
|
||||||
light->samples = samples * samples;
|
light->samples = samples * samples;
|
||||||
@ -223,7 +235,7 @@ void BlenderSync::sync_background_light()
|
|||||||
/* Object */
|
/* Object */
|
||||||
|
|
||||||
Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob,
|
Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob,
|
||||||
Transform& tfm, uint layer_flag, float motion_time, bool hide_tris)
|
Transform& tfm, uint layer_flag, float motion_time, bool hide_tris, bool *use_portal)
|
||||||
{
|
{
|
||||||
BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
|
BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
|
||||||
bool motion = motion_time != 0.0f;
|
bool motion = motion_time != 0.0f;
|
||||||
@ -232,7 +244,7 @@ Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_P
|
|||||||
if(object_is_light(b_ob)) {
|
if(object_is_light(b_ob)) {
|
||||||
/* don't use lamps for excluded layers used as mask layer */
|
/* don't use lamps for excluded layers used as mask layer */
|
||||||
if(!motion && !((layer_flag & render_layer.holdout_layer) && (layer_flag & render_layer.exclude_layer)))
|
if(!motion && !((layer_flag & render_layer.holdout_layer) && (layer_flag & render_layer.exclude_layer)))
|
||||||
sync_light(b_parent, persistent_id, b_ob, tfm);
|
sync_light(b_parent, persistent_id, b_ob, tfm, use_portal);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -476,6 +488,7 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
|
|||||||
int dupli_settings = preview ? 1 : 2;
|
int dupli_settings = preview ? 1 : 2;
|
||||||
|
|
||||||
bool cancel = false;
|
bool cancel = false;
|
||||||
|
bool use_portal = false;
|
||||||
|
|
||||||
for(; b_sce && !cancel; b_sce = b_sce.background_set()) {
|
for(; b_sce && !cancel; b_sce = b_sce.background_set()) {
|
||||||
for(b_sce.object_bases.begin(b_base); b_base != b_sce.object_bases.end() && !cancel; ++b_base) {
|
for(b_sce.object_bases.begin(b_base); b_base != b_sce.object_bases.end() && !cancel; ++b_base) {
|
||||||
@ -506,7 +519,7 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
|
|||||||
BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup->persistent_id();
|
BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup->persistent_id();
|
||||||
|
|
||||||
/* sync object and mesh or light data */
|
/* sync object and mesh or light data */
|
||||||
Object *object = sync_object(b_ob, persistent_id.data, *b_dup, tfm, ob_layer, motion_time, hide_tris);
|
Object *object = sync_object(b_ob, persistent_id.data, *b_dup, tfm, ob_layer, motion_time, hide_tris, &use_portal);
|
||||||
|
|
||||||
/* sync possible particle data, note particle_id
|
/* sync possible particle data, note particle_id
|
||||||
* starts counting at 1, first is dummy particle */
|
* starts counting at 1, first is dummy particle */
|
||||||
@ -526,7 +539,7 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
|
|||||||
if(!object_render_hide(b_ob, true, true, hide_tris)) {
|
if(!object_render_hide(b_ob, true, true, hide_tris)) {
|
||||||
/* object itself */
|
/* object itself */
|
||||||
Transform tfm = get_transform(b_ob.matrix_world());
|
Transform tfm = get_transform(b_ob.matrix_world());
|
||||||
sync_object(b_ob, NULL, PointerRNA_NULL, tfm, ob_layer, motion_time, hide_tris);
|
sync_object(b_ob, NULL, PointerRNA_NULL, tfm, ob_layer, motion_time, hide_tris, &use_portal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,7 +550,7 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
|
|||||||
progress.set_sync_status("");
|
progress.set_sync_status("");
|
||||||
|
|
||||||
if(!cancel && !motion) {
|
if(!cancel && !motion) {
|
||||||
sync_background_light();
|
sync_background_light(use_portal);
|
||||||
|
|
||||||
/* handle removed data and modified pointers */
|
/* handle removed data and modified pointers */
|
||||||
if(light_map.post_sync())
|
if(light_map.post_sync())
|
||||||
|
@ -86,9 +86,9 @@ private:
|
|||||||
Mesh *sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tris);
|
Mesh *sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tris);
|
||||||
void sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool motion, int time_index = 0);
|
void sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool motion, int time_index = 0);
|
||||||
Object *sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob,
|
Object *sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob,
|
||||||
Transform& tfm, uint layer_flag, float motion_time, bool hide_tris);
|
Transform& tfm, uint layer_flag, float motion_time, bool hide_tris, bool *use_portal);
|
||||||
void sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::Object b_ob, Transform& tfm);
|
void sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::Object b_ob, Transform& tfm, bool *use_portal);
|
||||||
void sync_background_light();
|
void sync_background_light(bool use_portal);
|
||||||
void sync_mesh_motion(BL::Object b_ob, Object *object, float motion_time);
|
void sync_mesh_motion(BL::Object b_ob, Object *object, float motion_time);
|
||||||
void sync_camera_motion(BL::Object b_ob, float motion_time);
|
void sync_camera_motion(BL::Object b_ob, float motion_time);
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ ccl_device_noinline float3 indirect_background(KernelGlobals *kg, PathState *sta
|
|||||||
if(!(state->flag & PATH_RAY_MIS_SKIP) && res) {
|
if(!(state->flag & PATH_RAY_MIS_SKIP) && res) {
|
||||||
/* multiple importance sampling, get background light pdf for ray
|
/* multiple importance sampling, get background light pdf for ray
|
||||||
* direction, and compute weight with respect to BSDF pdf */
|
* direction, and compute weight with respect to BSDF pdf */
|
||||||
float pdf = background_light_pdf(kg, ray->D);
|
float pdf = background_light_pdf(kg, ray->P, ray->D);
|
||||||
float mis_weight = power_heuristic(state->ray_pdf, pdf);
|
float mis_weight = power_heuristic(state->ray_pdf, pdf);
|
||||||
|
|
||||||
return L*mis_weight;
|
return L*mis_weight;
|
||||||
|
@ -33,156 +33,7 @@ typedef struct LightSample {
|
|||||||
LightType type; /* type of light */
|
LightType type; /* type of light */
|
||||||
} LightSample;
|
} LightSample;
|
||||||
|
|
||||||
/* Background Light */
|
/* Area light sampling */
|
||||||
|
|
||||||
#ifdef __BACKGROUND_MIS__
|
|
||||||
|
|
||||||
/* TODO(sergey): In theory it should be all fine to use noinline for all
|
|
||||||
* devices, but we're so close to the release so better not screw things
|
|
||||||
* up for CPU at least.
|
|
||||||
*/
|
|
||||||
#ifdef __KERNEL_GPU__
|
|
||||||
ccl_device_noinline
|
|
||||||
#else
|
|
||||||
ccl_device
|
|
||||||
#endif
|
|
||||||
float3 background_light_sample(KernelGlobals *kg, float randu, float randv, float *pdf)
|
|
||||||
{
|
|
||||||
/* for the following, the CDF values are actually a pair of floats, with the
|
|
||||||
* function value as X and the actual CDF as Y. The last entry's function
|
|
||||||
* value is the CDF total. */
|
|
||||||
int res = kernel_data.integrator.pdf_background_res;
|
|
||||||
int cdf_count = res + 1;
|
|
||||||
|
|
||||||
/* this is basically std::lower_bound as used by pbrt */
|
|
||||||
int first = 0;
|
|
||||||
int count = res;
|
|
||||||
|
|
||||||
while(count > 0) {
|
|
||||||
int step = count >> 1;
|
|
||||||
int middle = first + step;
|
|
||||||
|
|
||||||
if(kernel_tex_fetch(__light_background_marginal_cdf, middle).y < randv) {
|
|
||||||
first = middle + 1;
|
|
||||||
count -= step + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
count = step;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index_v = max(0, first - 1);
|
|
||||||
kernel_assert(index_v >= 0 && index_v < res);
|
|
||||||
|
|
||||||
float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
|
|
||||||
float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1);
|
|
||||||
float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res);
|
|
||||||
|
|
||||||
/* importance-sampled V direction */
|
|
||||||
float dv = (randv - cdf_v.y) / (cdf_next_v.y - cdf_v.y);
|
|
||||||
float v = (index_v + dv) / res;
|
|
||||||
|
|
||||||
/* this is basically std::lower_bound as used by pbrt */
|
|
||||||
first = 0;
|
|
||||||
count = res;
|
|
||||||
while(count > 0) {
|
|
||||||
int step = count >> 1;
|
|
||||||
int middle = first + step;
|
|
||||||
|
|
||||||
if(kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + middle).y < randu) {
|
|
||||||
first = middle + 1;
|
|
||||||
count -= step + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
count = step;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index_u = max(0, first - 1);
|
|
||||||
kernel_assert(index_u >= 0 && index_u < res);
|
|
||||||
|
|
||||||
float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u);
|
|
||||||
float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u + 1);
|
|
||||||
float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + res);
|
|
||||||
|
|
||||||
/* importance-sampled U direction */
|
|
||||||
float du = (randu - cdf_u.y) / (cdf_next_u.y - cdf_u.y);
|
|
||||||
float u = (index_u + du) / res;
|
|
||||||
|
|
||||||
/* compute pdf */
|
|
||||||
float denom = cdf_last_u.x * cdf_last_v.x;
|
|
||||||
float sin_theta = sinf(M_PI_F * v);
|
|
||||||
|
|
||||||
if(sin_theta == 0.0f || denom == 0.0f)
|
|
||||||
*pdf = 0.0f;
|
|
||||||
else
|
|
||||||
*pdf = (cdf_u.x * cdf_v.x)/(M_2PI_F * M_PI_F * sin_theta * denom);
|
|
||||||
|
|
||||||
*pdf *= kernel_data.integrator.pdf_lights;
|
|
||||||
|
|
||||||
/* compute direction */
|
|
||||||
return -equirectangular_to_direction(u, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO(sergey): Same as above, after the release we should consider using
|
|
||||||
* 'noinline' for all devices.
|
|
||||||
*/
|
|
||||||
#ifdef __KERNEL_GPU__
|
|
||||||
ccl_device_noinline
|
|
||||||
#else
|
|
||||||
ccl_device
|
|
||||||
#endif
|
|
||||||
float background_light_pdf(KernelGlobals *kg, float3 direction)
|
|
||||||
{
|
|
||||||
float2 uv = direction_to_equirectangular(direction);
|
|
||||||
int res = kernel_data.integrator.pdf_background_res;
|
|
||||||
|
|
||||||
float sin_theta = sinf(uv.y * M_PI_F);
|
|
||||||
|
|
||||||
if(sin_theta == 0.0f)
|
|
||||||
return 0.0f;
|
|
||||||
|
|
||||||
int index_u = clamp(float_to_int(uv.x * res), 0, res - 1);
|
|
||||||
int index_v = clamp(float_to_int(uv.y * res), 0, res - 1);
|
|
||||||
|
|
||||||
/* pdfs in V direction */
|
|
||||||
float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + res);
|
|
||||||
float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res);
|
|
||||||
|
|
||||||
float denom = cdf_last_u.x * cdf_last_v.x;
|
|
||||||
|
|
||||||
if(denom == 0.0f)
|
|
||||||
return 0.0f;
|
|
||||||
|
|
||||||
/* pdfs in U direction */
|
|
||||||
float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + index_u);
|
|
||||||
float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
|
|
||||||
|
|
||||||
float pdf = (cdf_u.x * cdf_v.x)/(M_2PI_F * M_PI_F * sin_theta * denom);
|
|
||||||
|
|
||||||
return pdf * kernel_data.integrator.pdf_lights;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Regular Light */
|
|
||||||
|
|
||||||
ccl_device float3 disk_light_sample(float3 v, float randu, float randv)
|
|
||||||
{
|
|
||||||
float3 ru, rv;
|
|
||||||
|
|
||||||
make_orthonormals(v, &ru, &rv);
|
|
||||||
to_unit_disk(&randu, &randv);
|
|
||||||
|
|
||||||
return ru*randu + rv*randv;
|
|
||||||
}
|
|
||||||
|
|
||||||
ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv)
|
|
||||||
{
|
|
||||||
return normalize(D + disk_light_sample(D, randu, randv)*radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
ccl_device float3 sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv)
|
|
||||||
{
|
|
||||||
return disk_light_sample(normalize(P - center), randu, randv)*radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Uses the following paper:
|
/* Uses the following paper:
|
||||||
*
|
*
|
||||||
@ -200,8 +51,8 @@ ccl_device float area_light_sample(float3 P,
|
|||||||
bool sample_coord)
|
bool sample_coord)
|
||||||
{
|
{
|
||||||
/* In our name system we're using P for the center,
|
/* In our name system we're using P for the center,
|
||||||
* which is o in the paper.
|
* which is o in the paper.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f;
|
float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f;
|
||||||
float axisu_len, axisv_len;
|
float axisu_len, axisv_len;
|
||||||
@ -274,6 +125,374 @@ ccl_device float area_light_sample(float3 P,
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Background Light */
|
||||||
|
|
||||||
|
#ifdef __BACKGROUND_MIS__
|
||||||
|
|
||||||
|
/* TODO(sergey): In theory it should be all fine to use noinline for all
|
||||||
|
* devices, but we're so close to the release so better not screw things
|
||||||
|
* up for CPU at least.
|
||||||
|
*/
|
||||||
|
#ifdef __KERNEL_GPU__
|
||||||
|
ccl_device_noinline
|
||||||
|
#else
|
||||||
|
ccl_device
|
||||||
|
#endif
|
||||||
|
float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float *pdf)
|
||||||
|
{
|
||||||
|
/* for the following, the CDF values are actually a pair of floats, with the
|
||||||
|
* function value as X and the actual CDF as Y. The last entry's function
|
||||||
|
* value is the CDF total. */
|
||||||
|
int res = kernel_data.integrator.pdf_background_res;
|
||||||
|
int cdf_count = res + 1;
|
||||||
|
|
||||||
|
/* this is basically std::lower_bound as used by pbrt */
|
||||||
|
int first = 0;
|
||||||
|
int count = res;
|
||||||
|
|
||||||
|
while(count > 0) {
|
||||||
|
int step = count >> 1;
|
||||||
|
int middle = first + step;
|
||||||
|
|
||||||
|
if(kernel_tex_fetch(__light_background_marginal_cdf, middle).y < randv) {
|
||||||
|
first = middle + 1;
|
||||||
|
count -= step + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
count = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index_v = max(0, first - 1);
|
||||||
|
kernel_assert(index_v >= 0 && index_v < res);
|
||||||
|
|
||||||
|
float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
|
||||||
|
float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1);
|
||||||
|
float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res);
|
||||||
|
|
||||||
|
/* importance-sampled V direction */
|
||||||
|
float dv = (randv - cdf_v.y) / (cdf_next_v.y - cdf_v.y);
|
||||||
|
float v = (index_v + dv) / res;
|
||||||
|
|
||||||
|
/* this is basically std::lower_bound as used by pbrt */
|
||||||
|
first = 0;
|
||||||
|
count = res;
|
||||||
|
while(count > 0) {
|
||||||
|
int step = count >> 1;
|
||||||
|
int middle = first + step;
|
||||||
|
|
||||||
|
if(kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + middle).y < randu) {
|
||||||
|
first = middle + 1;
|
||||||
|
count -= step + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
count = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index_u = max(0, first - 1);
|
||||||
|
kernel_assert(index_u >= 0 && index_u < res);
|
||||||
|
|
||||||
|
float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u);
|
||||||
|
float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u + 1);
|
||||||
|
float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + res);
|
||||||
|
|
||||||
|
/* importance-sampled U direction */
|
||||||
|
float du = (randu - cdf_u.y) / (cdf_next_u.y - cdf_u.y);
|
||||||
|
float u = (index_u + du) / res;
|
||||||
|
|
||||||
|
/* compute pdf */
|
||||||
|
float denom = cdf_last_u.x * cdf_last_v.x;
|
||||||
|
float sin_theta = sinf(M_PI_F * v);
|
||||||
|
|
||||||
|
if(sin_theta == 0.0f || denom == 0.0f)
|
||||||
|
*pdf = 0.0f;
|
||||||
|
else
|
||||||
|
*pdf = (cdf_u.x * cdf_v.x)/(M_2PI_F * M_PI_F * sin_theta * denom);
|
||||||
|
|
||||||
|
/* compute direction */
|
||||||
|
return equirectangular_to_direction(u, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO(sergey): Same as above, after the release we should consider using
|
||||||
|
* 'noinline' for all devices.
|
||||||
|
*/
|
||||||
|
#ifdef __KERNEL_GPU__
|
||||||
|
ccl_device_noinline
|
||||||
|
#else
|
||||||
|
ccl_device
|
||||||
|
#endif
|
||||||
|
float background_map_pdf(KernelGlobals *kg, float3 direction)
|
||||||
|
{
|
||||||
|
float2 uv = direction_to_equirectangular(direction);
|
||||||
|
int res = kernel_data.integrator.pdf_background_res;
|
||||||
|
|
||||||
|
float sin_theta = sinf(uv.y * M_PI_F);
|
||||||
|
|
||||||
|
if(sin_theta == 0.0f)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
int index_u = clamp(float_to_int(uv.x * res), 0, res - 1);
|
||||||
|
int index_v = clamp(float_to_int(uv.y * res), 0, res - 1);
|
||||||
|
|
||||||
|
/* pdfs in V direction */
|
||||||
|
float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + res);
|
||||||
|
float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res);
|
||||||
|
|
||||||
|
float denom = cdf_last_u.x * cdf_last_v.x;
|
||||||
|
|
||||||
|
if(denom == 0.0f)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
/* pdfs in U direction */
|
||||||
|
float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + index_u);
|
||||||
|
float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
|
||||||
|
|
||||||
|
return (cdf_u.x * cdf_v.x)/(M_2PI_F * M_PI_F * sin_theta * denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
ccl_device float background_portal_pdf(KernelGlobals *kg,
|
||||||
|
float3 P,
|
||||||
|
float3 direction,
|
||||||
|
int ignore_portal,
|
||||||
|
bool *is_possible)
|
||||||
|
{
|
||||||
|
float portal_pdf = 0.0f;
|
||||||
|
if(is_possible)
|
||||||
|
*is_possible = false;
|
||||||
|
|
||||||
|
for(int p = 0; p < kernel_data.integrator.num_portals; p++) {
|
||||||
|
if(p == ignore_portal)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* TODO(sergey): Consider moving portal data fetch to a
|
||||||
|
* dedicated function.
|
||||||
|
*/
|
||||||
|
float4 data0 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 0);
|
||||||
|
float4 data3 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 3);
|
||||||
|
|
||||||
|
float3 lightpos = make_float3(data0.y, data0.z, data0.w);
|
||||||
|
float3 dir = make_float3(data3.y, data3.z, data3.w);
|
||||||
|
|
||||||
|
if(dot(dir, P - lightpos) <= 1e-5f) {
|
||||||
|
/* P is on the wrong side or too close. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_possible) {
|
||||||
|
/* There's a portal that could be sampled from this position. */
|
||||||
|
*is_possible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float t = -(dot(P, dir) - dot(lightpos, dir)) / dot(direction, dir);
|
||||||
|
if(t <= 1e-5f) {
|
||||||
|
/* Either behind the portal or too close. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 data1 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 1);
|
||||||
|
float4 data2 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 2);
|
||||||
|
|
||||||
|
float3 axisu = make_float3(data1.y, data1.z, data1.w);
|
||||||
|
float3 axisv = make_float3(data2.y, data2.z, data2.w);
|
||||||
|
|
||||||
|
float3 hit = P + t*direction;
|
||||||
|
float3 inplane = hit - lightpos;
|
||||||
|
/* Skip if the the ray doesn't pass through portal. */
|
||||||
|
if(fabsf(dot(inplane, axisu) / dot(axisu, axisu)) > 0.5f)
|
||||||
|
continue;
|
||||||
|
if(fabsf(dot(inplane, axisv) / dot(axisv, axisv)) > 0.5f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
portal_pdf += area_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return kernel_data.integrator.num_portals? portal_pdf / kernel_data.integrator.num_portals: 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P)
|
||||||
|
{
|
||||||
|
int num_possible_portals = 0;
|
||||||
|
for(int p = 0; p < kernel_data.integrator.num_portals; p++) {
|
||||||
|
float4 data0 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 0);
|
||||||
|
float4 data3 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 3);
|
||||||
|
float3 lightpos = make_float3(data0.y, data0.z, data0.w);
|
||||||
|
float3 dir = make_float3(data3.y, data3.z, data3.w);
|
||||||
|
|
||||||
|
/* Check whether portal is on the right side. */
|
||||||
|
if(dot(dir, P - lightpos) > 1e-5f)
|
||||||
|
num_possible_portals++;
|
||||||
|
}
|
||||||
|
return num_possible_portals;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccl_device float3 background_portal_sample(KernelGlobals *kg,
|
||||||
|
float3 P,
|
||||||
|
float randu,
|
||||||
|
float randv,
|
||||||
|
int num_possible,
|
||||||
|
int *sampled_portal,
|
||||||
|
float *pdf)
|
||||||
|
{
|
||||||
|
/* Pick a portal, then re-normalize randv. */
|
||||||
|
randv *= num_possible;
|
||||||
|
int portal = (int)randv;
|
||||||
|
randv -= portal;
|
||||||
|
|
||||||
|
/* TODO(sergey): Some smarter way of finding portal to sample
|
||||||
|
* is welcome.
|
||||||
|
*/
|
||||||
|
for(int p = 0; p < kernel_data.integrator.num_portals; p++) {
|
||||||
|
/* Search for the sampled portal. */
|
||||||
|
float4 data0 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 0);
|
||||||
|
float4 data3 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 3);
|
||||||
|
float3 lightpos = make_float3(data0.y, data0.z, data0.w);
|
||||||
|
float3 dir = make_float3(data3.y, data3.z, data3.w);
|
||||||
|
|
||||||
|
/* Check whether portal is on the right side. */
|
||||||
|
if(dot(dir, P - lightpos) <= 1e-5f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(portal == 0) {
|
||||||
|
/* p is the portal to be sampled. */
|
||||||
|
float4 data1 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 1);
|
||||||
|
float4 data2 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 2);
|
||||||
|
float3 axisu = make_float3(data1.y, data1.z, data1.w);
|
||||||
|
float3 axisv = make_float3(data2.y, data2.z, data2.w);
|
||||||
|
|
||||||
|
float3 lightPoint = lightpos;
|
||||||
|
|
||||||
|
*pdf = area_light_sample(P, &lightPoint,
|
||||||
|
axisu, axisv,
|
||||||
|
randu, randv,
|
||||||
|
true);
|
||||||
|
|
||||||
|
*pdf /= num_possible;
|
||||||
|
*sampled_portal = p;
|
||||||
|
return normalize(lightPoint - P);
|
||||||
|
}
|
||||||
|
|
||||||
|
portal--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_float3(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
ccl_device float3 background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf)
|
||||||
|
{
|
||||||
|
/* Probability of sampling portals instead of the map. */
|
||||||
|
float portal_sampling_pdf = kernel_data.integrator.portal_pdf;
|
||||||
|
|
||||||
|
/* Check if there are portals in the scene which we can sample. */
|
||||||
|
if(portal_sampling_pdf > 0.0f) {
|
||||||
|
int num_portals = background_num_possible_portals(kg, P);
|
||||||
|
if(num_portals > 0) {
|
||||||
|
if(portal_sampling_pdf == 1.0f || randu < portal_sampling_pdf) {
|
||||||
|
if(portal_sampling_pdf < 1.0f) {
|
||||||
|
randu /= portal_sampling_pdf;
|
||||||
|
}
|
||||||
|
int portal;
|
||||||
|
float3 D = background_portal_sample(kg, P, randu, randv, num_portals, &portal, pdf);
|
||||||
|
if(num_portals > 1) {
|
||||||
|
/* Ignore the chosen portal, its pdf is already included. */
|
||||||
|
*pdf += background_portal_pdf(kg, P, D, portal, NULL);
|
||||||
|
}
|
||||||
|
/* We could also have sampled the map, so combine with MIS. */
|
||||||
|
if(portal_sampling_pdf < 1.0f) {
|
||||||
|
float cdf_pdf = background_map_pdf(kg, D);
|
||||||
|
*pdf = (portal_sampling_pdf * (*pdf)
|
||||||
|
+ (1.0f - portal_sampling_pdf) * cdf_pdf);
|
||||||
|
}
|
||||||
|
return D;
|
||||||
|
} else {
|
||||||
|
/* Sample map, but with nonzero portal_sampling_pdf for MIS. */
|
||||||
|
randu = (randu - portal_sampling_pdf) / (1.0f - portal_sampling_pdf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* We can't sample a portal.
|
||||||
|
* Check if we can sample the map instead.
|
||||||
|
*/
|
||||||
|
if(portal_sampling_pdf == 1.0f) {
|
||||||
|
/* Use uniform as a fallback if we can't sample the map. */
|
||||||
|
*pdf = 1.0f / M_4PI_F;
|
||||||
|
return sample_uniform_sphere(randu, randv);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
portal_sampling_pdf = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 D = background_map_sample(kg, randu, randv, pdf);
|
||||||
|
/* Use MIS if portals could be sampled as well. */
|
||||||
|
if(portal_sampling_pdf > 0.0f) {
|
||||||
|
float portal_pdf = background_portal_pdf(kg, P, D, -1, NULL);
|
||||||
|
*pdf = (portal_sampling_pdf * portal_pdf
|
||||||
|
+ (1.0f - portal_sampling_pdf) * (*pdf));
|
||||||
|
}
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direction)
|
||||||
|
{
|
||||||
|
/* Probability of sampling portals instead of the map. */
|
||||||
|
float portal_sampling_pdf = kernel_data.integrator.portal_pdf;
|
||||||
|
|
||||||
|
if(portal_sampling_pdf > 0.0f) {
|
||||||
|
bool is_possible;
|
||||||
|
float portal_pdf = background_portal_pdf(kg, P, direction, -1, &is_possible);
|
||||||
|
if(portal_pdf == 0.0f) {
|
||||||
|
if(portal_sampling_pdf == 1.0f) {
|
||||||
|
/* If there are no possible portals at this point,
|
||||||
|
* the fallback sampling would have been used.
|
||||||
|
* Otherwise, the direction would not be sampled at all => pdf = 0
|
||||||
|
*/
|
||||||
|
return is_possible? 0.0f: kernel_data.integrator.pdf_lights / M_4PI_F;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* We can only sample the map. */
|
||||||
|
return background_map_pdf(kg, direction) * kernel_data.integrator.pdf_lights;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(portal_sampling_pdf == 1.0f) {
|
||||||
|
/* We can only sample portals. */
|
||||||
|
return portal_pdf * kernel_data.integrator.pdf_lights;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* We can sample both, so combine with MIS. */
|
||||||
|
return (background_map_pdf(kg, direction) * (1.0f - portal_sampling_pdf)
|
||||||
|
+ portal_pdf * portal_sampling_pdf) * kernel_data.integrator.pdf_lights;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No portals in the scene, so must sample the map.
|
||||||
|
* At least one of them is always possible if we have a LIGHT_BACKGROUND.
|
||||||
|
*/
|
||||||
|
return background_map_pdf(kg, direction) * kernel_data.integrator.pdf_lights;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Regular Light */
|
||||||
|
|
||||||
|
ccl_device float3 disk_light_sample(float3 v, float randu, float randv)
|
||||||
|
{
|
||||||
|
float3 ru, rv;
|
||||||
|
|
||||||
|
make_orthonormals(v, &ru, &rv);
|
||||||
|
to_unit_disk(&randu, &randv);
|
||||||
|
|
||||||
|
return ru*randu + rv*randv;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv)
|
||||||
|
{
|
||||||
|
return normalize(D + disk_light_sample(D, randu, randv)*radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
ccl_device float3 sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv)
|
||||||
|
{
|
||||||
|
return disk_light_sample(normalize(P - center), randu, randv)*radius;
|
||||||
|
}
|
||||||
|
|
||||||
ccl_device float spot_light_attenuation(float4 data1, float4 data2, LightSample *ls)
|
ccl_device float spot_light_attenuation(float4 data1, float4 data2, LightSample *ls)
|
||||||
{
|
{
|
||||||
float3 dir = make_float3(data2.y, data2.z, data2.w);
|
float3 dir = make_float3(data2.y, data2.z, data2.w);
|
||||||
@ -344,13 +563,14 @@ ccl_device void lamp_light_sample(KernelGlobals *kg, int lamp,
|
|||||||
#ifdef __BACKGROUND_MIS__
|
#ifdef __BACKGROUND_MIS__
|
||||||
else if(type == LIGHT_BACKGROUND) {
|
else if(type == LIGHT_BACKGROUND) {
|
||||||
/* infinite area light (e.g. light dome or env light) */
|
/* infinite area light (e.g. light dome or env light) */
|
||||||
float3 D = background_light_sample(kg, randu, randv, &ls->pdf);
|
float3 D = -background_light_sample(kg, P, randu, randv, &ls->pdf);
|
||||||
|
|
||||||
ls->P = D;
|
ls->P = D;
|
||||||
ls->Ng = D;
|
ls->Ng = D;
|
||||||
ls->D = -D;
|
ls->D = -D;
|
||||||
ls->t = FLT_MAX;
|
ls->t = FLT_MAX;
|
||||||
ls->eval_fac = 1.0f;
|
ls->eval_fac = 1.0f;
|
||||||
|
ls->pdf *= kernel_data.integrator.pdf_lights;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else {
|
else {
|
||||||
|
@ -896,6 +896,11 @@ typedef struct KernelIntegrator {
|
|||||||
float inv_pdf_lights;
|
float inv_pdf_lights;
|
||||||
int pdf_background_res;
|
int pdf_background_res;
|
||||||
|
|
||||||
|
/* light portals */
|
||||||
|
float portal_pdf;
|
||||||
|
int num_portals;
|
||||||
|
int portal_offset;
|
||||||
|
|
||||||
/* bounces */
|
/* bounces */
|
||||||
int min_bounce;
|
int min_bounce;
|
||||||
int max_bounce;
|
int max_bounce;
|
||||||
@ -948,6 +953,8 @@ typedef struct KernelIntegrator {
|
|||||||
int volume_max_steps;
|
int volume_max_steps;
|
||||||
float volume_step_size;
|
float volume_step_size;
|
||||||
int volume_samples;
|
int volume_samples;
|
||||||
|
|
||||||
|
int pad;
|
||||||
} KernelIntegrator;
|
} KernelIntegrator;
|
||||||
|
|
||||||
typedef struct KernelBVH {
|
typedef struct KernelBVH {
|
||||||
|
@ -129,6 +129,8 @@ Light::Light()
|
|||||||
shader = 0;
|
shader = 0;
|
||||||
samples = 1;
|
samples = 1;
|
||||||
max_bounces = 1024;
|
max_bounces = 1024;
|
||||||
|
|
||||||
|
is_portal = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Light::tag_update(Scene *scene)
|
void Light::tag_update(Scene *scene)
|
||||||
@ -153,10 +155,17 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
|
|||||||
progress.set_status("Updating Lights", "Computing distribution");
|
progress.set_status("Updating Lights", "Computing distribution");
|
||||||
|
|
||||||
/* count */
|
/* count */
|
||||||
size_t num_lights = scene->lights.size();
|
size_t num_lights = 0;
|
||||||
size_t num_background_lights = 0;
|
size_t num_background_lights = 0;
|
||||||
size_t num_triangles = 0;
|
size_t num_triangles = 0;
|
||||||
|
|
||||||
|
bool background_mis = false;
|
||||||
|
|
||||||
|
foreach(Light *light, scene->lights) {
|
||||||
|
if(!light->is_portal)
|
||||||
|
num_lights++;
|
||||||
|
}
|
||||||
|
|
||||||
foreach(Object *object, scene->objects) {
|
foreach(Object *object, scene->objects) {
|
||||||
Mesh *mesh = object->mesh;
|
Mesh *mesh = object->mesh;
|
||||||
bool have_emission = false;
|
bool have_emission = false;
|
||||||
@ -287,22 +296,29 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
|
|||||||
float trianglearea = totarea;
|
float trianglearea = totarea;
|
||||||
|
|
||||||
/* point lights */
|
/* point lights */
|
||||||
float lightarea = (totarea > 0.0f)? totarea/scene->lights.size(): 1.0f;
|
float lightarea = (totarea > 0.0f) ? totarea / num_lights : 1.0f;
|
||||||
bool use_lamp_mis = false;
|
bool use_lamp_mis = false;
|
||||||
|
|
||||||
for(int i = 0; i < scene->lights.size(); i++, offset++) {
|
int light_index = 0;
|
||||||
Light *light = scene->lights[i];
|
foreach(Light *light, scene->lights) {
|
||||||
|
if(light->is_portal)
|
||||||
|
continue;
|
||||||
|
|
||||||
distribution[offset].x = totarea;
|
distribution[offset].x = totarea;
|
||||||
distribution[offset].y = __int_as_float(~(int)i);
|
distribution[offset].y = __int_as_float(~light_index);
|
||||||
distribution[offset].z = 1.0f;
|
distribution[offset].z = 1.0f;
|
||||||
distribution[offset].w = light->size;
|
distribution[offset].w = light->size;
|
||||||
totarea += lightarea;
|
totarea += lightarea;
|
||||||
|
|
||||||
if(light->size > 0.0f && light->use_mis)
|
if(light->size > 0.0f && light->use_mis)
|
||||||
use_lamp_mis = true;
|
use_lamp_mis = true;
|
||||||
if(light->type == LIGHT_BACKGROUND)
|
if(light->type == LIGHT_BACKGROUND) {
|
||||||
num_background_lights++;
|
num_background_lights++;
|
||||||
|
background_mis = light->use_mis;
|
||||||
|
}
|
||||||
|
|
||||||
|
light_index++;
|
||||||
|
offset++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* normalize cumulative distribution functions */
|
/* normalize cumulative distribution functions */
|
||||||
@ -364,6 +380,18 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
|
|||||||
|
|
||||||
/* CDF */
|
/* CDF */
|
||||||
device->tex_alloc("__light_distribution", dscene->light_distribution);
|
device->tex_alloc("__light_distribution", dscene->light_distribution);
|
||||||
|
|
||||||
|
/* Portals */
|
||||||
|
if(num_background_lights > 0 && light_index != scene->lights.size()) {
|
||||||
|
kintegrator->portal_offset = light_index;
|
||||||
|
kintegrator->num_portals = scene->lights.size() - light_index;
|
||||||
|
kintegrator->portal_pdf = background_mis? 0.5f: 1.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kintegrator->num_portals = 0;
|
||||||
|
kintegrator->portal_offset = 0;
|
||||||
|
kintegrator->portal_pdf = 0.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dscene->light_distribution.clear();
|
dscene->light_distribution.clear();
|
||||||
@ -374,6 +402,10 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
|
|||||||
kintegrator->pdf_lights = 0.0f;
|
kintegrator->pdf_lights = 0.0f;
|
||||||
kintegrator->inv_pdf_lights = 0.0f;
|
kintegrator->inv_pdf_lights = 0.0f;
|
||||||
kintegrator->use_lamp_mis = false;
|
kintegrator->use_lamp_mis = false;
|
||||||
|
kintegrator->num_portals = 0;
|
||||||
|
kintegrator->portal_offset = 0;
|
||||||
|
kintegrator->portal_pdf = 0.0f;
|
||||||
|
|
||||||
kfilm->pass_shadow_scale = 1.0f;
|
kfilm->pass_shadow_scale = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,8 +547,8 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
|
|||||||
|
|
||||||
float4 *light_data = dscene->light_data.resize(scene->lights.size()*LIGHT_SIZE);
|
float4 *light_data = dscene->light_data.resize(scene->lights.size()*LIGHT_SIZE);
|
||||||
|
|
||||||
if(!device->info.advanced_shading) {
|
/* remove background light? */
|
||||||
/* remove unsupported light */
|
if(!(device->info.advanced_shading)) {
|
||||||
foreach(Light *light, scene->lights) {
|
foreach(Light *light, scene->lights) {
|
||||||
if(light->type == LIGHT_BACKGROUND) {
|
if(light->type == LIGHT_BACKGROUND) {
|
||||||
scene->lights.erase(std::remove(scene->lights.begin(), scene->lights.end(), light), scene->lights.end());
|
scene->lights.erase(std::remove(scene->lights.begin(), scene->lights.end(), light), scene->lights.end());
|
||||||
@ -525,10 +557,14 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < scene->lights.size(); i++) {
|
int light_index = 0;
|
||||||
Light *light = scene->lights[i];
|
|
||||||
|
foreach(Light *light, scene->lights) {
|
||||||
|
if(light->is_portal)
|
||||||
|
continue;
|
||||||
|
|
||||||
float3 co = light->co;
|
float3 co = light->co;
|
||||||
int shader_id = scene->shader_manager->get_shader_id(scene->lights[i]->shader);
|
int shader_id = scene->shader_manager->get_shader_id(light->shader);
|
||||||
float samples = __int_as_float(light->samples);
|
float samples = __int_as_float(light->samples);
|
||||||
float max_bounces = __int_as_float(light->max_bounces);
|
float max_bounces = __int_as_float(light->max_bounces);
|
||||||
|
|
||||||
@ -561,11 +597,11 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
|
|||||||
if(light->use_mis && radius > 0.0f)
|
if(light->use_mis && radius > 0.0f)
|
||||||
shader_id |= SHADER_USE_MIS;
|
shader_id |= SHADER_USE_MIS;
|
||||||
|
|
||||||
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
|
light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
|
||||||
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, invarea, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, invarea, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
else if(light->type == LIGHT_DISTANT) {
|
else if(light->type == LIGHT_DISTANT) {
|
||||||
shader_id &= ~SHADER_AREA_LIGHT;
|
shader_id &= ~SHADER_AREA_LIGHT;
|
||||||
@ -582,11 +618,11 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
|
|||||||
if(light->use_mis && area > 0.0f)
|
if(light->use_mis && area > 0.0f)
|
||||||
shader_id |= SHADER_USE_MIS;
|
shader_id |= SHADER_USE_MIS;
|
||||||
|
|
||||||
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), dir.x, dir.y, dir.z);
|
light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), dir.x, dir.y, dir.z);
|
||||||
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, cosangle, invarea);
|
light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, cosangle, invarea);
|
||||||
light_data[i*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
else if(light->type == LIGHT_BACKGROUND) {
|
else if(light->type == LIGHT_BACKGROUND) {
|
||||||
uint visibility = scene->background->visibility;
|
uint visibility = scene->background->visibility;
|
||||||
@ -611,11 +647,11 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
|
|||||||
use_light_visibility = true;
|
use_light_visibility = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
else if(light->type == LIGHT_AREA) {
|
else if(light->type == LIGHT_AREA) {
|
||||||
float3 axisu = light->axisu*(light->sizeu*light->size);
|
float3 axisu = light->axisu*(light->sizeu*light->size);
|
||||||
@ -629,11 +665,11 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
|
|||||||
if(light->use_mis && area > 0.0f)
|
if(light->use_mis && area > 0.0f)
|
||||||
shader_id |= SHADER_USE_MIS;
|
shader_id |= SHADER_USE_MIS;
|
||||||
|
|
||||||
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
|
light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
|
||||||
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), axisu.x, axisu.y, axisu.z);
|
light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), axisu.x, axisu.y, axisu.z);
|
||||||
light_data[i*LIGHT_SIZE + 2] = make_float4(invarea, axisv.x, axisv.y, axisv.z);
|
light_data[light_index*LIGHT_SIZE + 2] = make_float4(invarea, axisv.x, axisv.y, axisv.z);
|
||||||
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, dir.x, dir.y, dir.z);
|
light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, dir.x, dir.y, dir.z);
|
||||||
light_data[i*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
else if(light->type == LIGHT_SPOT) {
|
else if(light->type == LIGHT_SPOT) {
|
||||||
shader_id &= ~SHADER_AREA_LIGHT;
|
shader_id &= ~SHADER_AREA_LIGHT;
|
||||||
@ -649,14 +685,44 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
|
|||||||
if(light->use_mis && radius > 0.0f)
|
if(light->use_mis && radius > 0.0f)
|
||||||
shader_id |= SHADER_USE_MIS;
|
shader_id |= SHADER_USE_MIS;
|
||||||
|
|
||||||
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
|
light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
|
||||||
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, invarea, spot_angle);
|
light_data[light_index*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), radius, invarea, spot_angle);
|
||||||
light_data[i*LIGHT_SIZE + 2] = make_float4(spot_smooth, dir.x, dir.y, dir.z);
|
light_data[light_index*LIGHT_SIZE + 2] = make_float4(spot_smooth, dir.x, dir.y, dir.z);
|
||||||
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
|
||||||
light_data[i*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
light_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO(sergey): Consider moving portals update to their own function
|
||||||
|
* keeping this one more manageable.
|
||||||
|
*/
|
||||||
|
foreach(Light *light, scene->lights) {
|
||||||
|
if(!light->is_portal)
|
||||||
|
continue;
|
||||||
|
assert(light->type == LIGHT_AREA);
|
||||||
|
|
||||||
|
float3 co = light->co;
|
||||||
|
float3 axisu = light->axisu*(light->sizeu*light->size);
|
||||||
|
float3 axisv = light->axisv*(light->sizev*light->size);
|
||||||
|
float area = len(axisu)*len(axisv);
|
||||||
|
float invarea = (area > 0.0f) ? 1.0f / area : 1.0f;
|
||||||
|
float3 dir = light->dir;
|
||||||
|
|
||||||
|
dir = safe_normalize(dir);
|
||||||
|
|
||||||
|
light_data[light_index*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
|
||||||
|
light_data[light_index*LIGHT_SIZE + 1] = make_float4(area, axisu.x, axisu.y, axisu.z);
|
||||||
|
light_data[light_index*LIGHT_SIZE + 2] = make_float4(invarea, axisv.x, axisv.y, axisv.z);
|
||||||
|
light_data[light_index*LIGHT_SIZE + 3] = make_float4(-1, dir.x, dir.y, dir.z);
|
||||||
|
light_data[light_index*LIGHT_SIZE + 4] = make_float4(-1, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
light_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(light_index == scene->lights.size());
|
||||||
|
|
||||||
device->tex_alloc("__light_data", dscene->light_data);
|
device->tex_alloc("__light_data", dscene->light_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,8 @@ public:
|
|||||||
bool use_transmission;
|
bool use_transmission;
|
||||||
bool use_scatter;
|
bool use_scatter;
|
||||||
|
|
||||||
|
bool is_portal;
|
||||||
|
|
||||||
int shader;
|
int shader;
|
||||||
int samples;
|
int samples;
|
||||||
int max_bounces;
|
int max_bounces;
|
||||||
|
Loading…
Reference in New Issue
Block a user