diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index c9b4dc25cf2..4e8527387c0 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1409,15 +1409,15 @@ class CYCLES_LIGHT_PT_nodes(CyclesButtonsPanel, Panel): panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface') -class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel): - bl_label = "Spot Shape" +class CYCLES_LIGHT_PT_beam_shape(CyclesButtonsPanel, Panel): + bl_label = "Beam Shape" bl_parent_id = "CYCLES_LIGHT_PT_light" bl_context = "data" @classmethod def poll(cls, context): - light = context.light - return (light and light.type == 'SPOT') and CyclesButtonsPanel.poll(context) + if context.light.type in {'SPOT', 'AREA'}: + return context.light and CyclesButtonsPanel.poll(context) def draw(self, context): layout = self.layout @@ -1425,9 +1425,12 @@ class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel): layout.use_property_split = True col = layout.column() - col.prop(light, "spot_size", text="Size") - col.prop(light, "spot_blend", text="Blend", slider=True) - col.prop(light, "show_cone") + if light.type == 'SPOT': + col.prop(light, "spot_size", text="Spot Size") + col.prop(light, "spot_blend", text="Blend", slider=True) + col.prop(light, "show_cone") + elif light.type == 'AREA': + col.prop(light, "spread", text="Spread") class CYCLES_WORLD_PT_preview(CyclesButtonsPanel, Panel): @@ -2284,7 +2287,7 @@ classes = ( CYCLES_LIGHT_PT_preview, CYCLES_LIGHT_PT_light, CYCLES_LIGHT_PT_nodes, - CYCLES_LIGHT_PT_spot, + CYCLES_LIGHT_PT_beam_shape, CYCLES_WORLD_PT_preview, CYCLES_WORLD_PT_surface, CYCLES_WORLD_PT_volume, @@ -2314,7 +2317,7 @@ classes = ( node_panel(CYCLES_WORLD_PT_settings_surface), node_panel(CYCLES_WORLD_PT_settings_volume), node_panel(CYCLES_LIGHT_PT_light), - node_panel(CYCLES_LIGHT_PT_spot), + node_panel(CYCLES_LIGHT_PT_beam_shape) ) diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp index ff4ecc5a3f9..283ed7d0adc 100644 --- a/intern/cycles/blender/blender_light.cpp +++ b/intern/cycles/blender/blender_light.cpp @@ -82,6 +82,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_axisu(transform_get_column(&tfm, 0)); light->set_axisv(transform_get_column(&tfm, 1)); light->set_sizeu(b_area_light.size()); + light->set_spread(b_area_light.spread()); switch (b_area_light.shape()) { case BL::AreaLight::shape_SQUARE: light->set_sizev(light->get_sizeu()); diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index 9650b85a5c2..93b05f0ffce 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -147,6 +147,13 @@ ccl_device_inline bool lamp_light_sample( ls->D = normalize_len(ls->P - P, &ls->t); ls->eval_fac = 0.25f * invarea; + + if (klight->area.tan_spread > 0.0f) { + /* Area Light spread angle attenuation */ + ls->eval_fac *= light_spread_attenuation( + ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); + } + if (is_round) { ls->pdf *= lamp_light_pdf(kg, D, -ls->D, ls->t); } @@ -286,6 +293,15 @@ ccl_device bool lamp_light_eval( ls->pdf = rect_light_sample(P, &light_P, axisu, axisv, 0, 0, false); } ls->eval_fac = 0.25f * invarea; + + if (klight->area.tan_spread > 0.0f) { + /* Area Light spread angle attenuation */ + ls->eval_fac *= light_spread_attenuation( + ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); + if (ls->eval_fac == 0.0f) { + return false; + } + } } else { return false; diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/kernel_light_common.h index 39503a4b479..7efd1e74202 100644 --- a/intern/cycles/kernel/kernel_light_common.h +++ b/intern/cycles/kernel/kernel_light_common.h @@ -146,6 +146,17 @@ ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot return attenuation; } +ccl_device float light_spread_attenuation(const float3 D, + const float3 Ng, + const float tan_spread, + const float normalize_spread) +{ + const float cos_a = -dot(D, Ng); + const float sin_a = safe_sqrtf(1.0f - sqr(cos_a)); + const float tan_a = sin_a / cos_a; + return max((1.0f - (tan_spread * tan_a)) * normalize_spread, 0.0f); +} + ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t) { float cos_pi = dot(Ng, I); diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index df56360b1df..ab54fda14af 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1501,9 +1501,9 @@ typedef struct KernelAreaLight { float axisu[3]; float invarea; float axisv[3]; - float pad1; + float tan_spread; float dir[3]; - float pad2; + float normalize_spread; } KernelAreaLight; typedef struct KernelDistantLight { diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 72450e2c546..858b177b69c 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -129,6 +129,7 @@ NODE_DEFINE(Light) SOCKET_VECTOR(axisv, "Axis V", zero_float3()); SOCKET_FLOAT(sizev, "Size V", 1.0f); SOCKET_BOOLEAN(round, "Round", false); + SOCKET_FLOAT(spread, "Spread", M_PI_F); SOCKET_INT(map_resolution, "Map Resolution", 0); @@ -858,6 +859,15 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc float invarea = (area != 0.0f) ? 1.0f / area : 1.0f; float3 dir = light->dir; + /* Convert from spread angle 0..180 to 90..0, clamping to a minimum + * angle to avoid excessive noise. */ + const float min_spread_angle = 1.0f * M_PI_F / 180.0f; + const float spread_angle = 0.5f * (M_PI_F - max(light->spread, min_spread_angle)); + /* Normalization computed using: + * integrate cos(x) (1 - tan(x) * tan(a)) * sin(x) from x = a to pi/2. */ + const float tan_spread = tanf(spread_angle); + const float normalize_spread = 2.0f / (2.0f + (2.0f * spread_angle - M_PI_F) * tan_spread); + dir = safe_normalize(dir); if (light->use_mis && area != 0.0f) @@ -877,6 +887,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc klights[light_index].area.dir[0] = dir.x; klights[light_index].area.dir[1] = dir.y; klights[light_index].area.dir[2] = dir.z; + klights[light_index].area.tan_spread = tan_spread; + klights[light_index].area.normalize_spread = normalize_spread; } else if (light->light_type == LIGHT_SPOT) { shader_id &= ~SHADER_AREA_LIGHT; diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index 39014b5d667..fbd709125ff 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -58,6 +58,7 @@ class Light : public Node { NODE_SOCKET_API(float3, axisv) NODE_SOCKET_API(float, sizev) NODE_SOCKET_API(bool, round) + NODE_SOCKET_API(float, spread) NODE_SOCKET_API(Transform, tfm) diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index d8f798a11cd..0c6817542d1 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1963,5 +1963,12 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) arm->axes_position = 1.0; } } + + /* Initialize the spread parameter for area lights*/ + if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "area_spread")) { + LISTBASE_FOREACH (Light *, la, &bmain->lights) { + la->area_spread = DEG2RADF(180.0f); + } + } } } diff --git a/source/blender/makesdna/DNA_light_defaults.h b/source/blender/makesdna/DNA_light_defaults.h index f3ccf14ac5b..5e5ce4bf540 100644 --- a/source/blender/makesdna/DNA_light_defaults.h +++ b/source/blender/makesdna/DNA_light_defaults.h @@ -68,6 +68,7 @@ .volume_fac = 1.0f, \ .att_dist = 40.0f, \ .sun_angle = DEG2RADF(0.526f), \ + .area_spread = DEG2RADF(180.0f), \ } /** \} */ diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index 3b7440aedd2..82ff3c95834 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -70,9 +70,9 @@ typedef struct Light { short area_shape; float area_size, area_sizey, area_sizez; + float area_spread; float sun_angle; - char _pad3[4]; /* texact is for buttons */ short texact, shadhalostep; diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c index bb99e5c6c1d..0593db0dd56 100644 --- a/source/blender/makesrna/intern/rna_light.c +++ b/source/blender/makesrna/intern/rna_light.c @@ -480,6 +480,15 @@ static void rna_def_area_light(BlenderRNA *brna) "Size Y", "Size of the area of the area light in the Y direction for rectangle shapes"); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); + + prop = RNA_def_property(srna, "spread", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "area_spread"); + RNA_def_property_range(prop, DEG2RADF(1.0f), DEG2RADF(180.0f)); + RNA_def_property_ui_text( + prop, + "Spread", + "How widely the emitted light fans out, as in the case of a gridded softbox"); + RNA_def_property_update(prop, 0, "rna_Light_draw_update"); } static void rna_def_spot_light(BlenderRNA *brna)