Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Stockner <lukas.stockner@freenet.de>2018-05-24 04:50:16 +0300
committerLukas Stockner <lukas.stockner@freenet.de>2018-05-24 17:43:47 +0300
commit5505ba8d4753ee6b3c0101ba3e4abf337546a1b0 (patch)
tree1c0fdbed36127f670e8aa0d49a53f72988cc34c5
parente8c8ff4f86c276e4dca884ff1a411fcbbf003fed (diff)
Cycles/Eevee: Implement disk and ellipse shapes for area lamps
The implementation is pretty straightforward. In Cycles, sampling the shapes is currently done w.r.t. area instead of solid angle. There is a paper on solid angle sampling for disks [1], but the described algorithm is based on simply sampling the enclosing square and rejecting samples outside of the disk, which is not exactly great for Cycles' RNG (we'd need to setup a LCG for the repeated sampling) and for GPU divergence. Even worse, the algorithm is only defined for disks. For ellipses, the basic idea still works, but a way to analytically calculate the solid angle is required. This is technically possible [2], but the calculation is extremely complex and still requires a lookup table for the Heuman Lambda function. Therefore, I've decided to not implement that for now, we could still look into it later on. In Eevee, the code uses the existing ltc_evaluate_disk to implement the lighting calculations. [1]: "Solid Angle Sampling of Disk and Cylinder Lights" [2]: "Analytical solution for the solid angle subtended at any point by an ellipse via a point source radiation vector potential" Reviewers: sergey, brecht, fclem Differential Revision: https://developer.blender.org/D3171
-rw-r--r--intern/cycles/blender/addon/ui.py4
-rw-r--r--intern/cycles/blender/blender_object.cpp22
-rw-r--r--intern/cycles/kernel/kernel_light.h180
-rw-r--r--intern/cycles/render/light.cpp13
-rw-r--r--intern/cycles/render/light.h1
-rw-r--r--intern/cycles/util/util_math_intersect.h22
-rw-r--r--release/scripts/startup/bl_ui/properties_data_lamp.py8
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c13
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h3
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl30
-rw-r--r--source/blender/draw/engines/eevee/shaders/lamps_lib.glsl31
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl5
-rw-r--r--source/blender/draw/intern/draw_cache.c42
-rw-r--r--source/blender/draw/intern/draw_cache.h3
-rw-r--r--source/blender/draw/modes/object_mode.c19
-rw-r--r--source/blender/editors/object/object_transform.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_manipulator_lamp.c12
-rw-r--r--source/blender/makesdna/DNA_lamp_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_lamp.c2
20 files changed, 298 insertions, 120 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index eeb059f79f1..ff8ca0adc18 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -909,9 +909,9 @@ class CYCLES_LAMP_PT_lamp(CyclesButtonsPanel, Panel):
col.prop(lamp, "shape", text="")
sub = col.column(align=True)
- if lamp.shape == 'SQUARE':
+ if lamp.shape in {'SQUARE', 'DISK'}:
sub.prop(lamp, "size")
- elif lamp.shape == 'RECTANGLE':
+ elif lamp.shape in {'RECTANGLE', 'ELLIPSE'}:
sub.prop(lamp, "size", text="Size X")
sub.prop(lamp, "size_y", text="Size Y")
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index d0df8e1800f..090682c8e14 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -162,10 +162,24 @@ void BlenderSync::sync_light(BL::Object& b_parent,
light->axisu = transform_get_column(&tfm, 0);
light->axisv = transform_get_column(&tfm, 1);
light->sizeu = b_area_lamp.size();
- if(b_area_lamp.shape() == BL::AreaLamp::shape_RECTANGLE)
- light->sizev = b_area_lamp.size_y();
- else
- light->sizev = light->sizeu;
+ switch(b_area_lamp.shape()) {
+ case BL::AreaLamp::shape_SQUARE:
+ light->sizev = light->sizeu;
+ light->round = false;
+ break;
+ case BL::AreaLamp::shape_RECTANGLE:
+ light->sizev = b_area_lamp.size_y();
+ light->round = false;
+ break;
+ case BL::AreaLamp::shape_DISK:
+ light->sizev = light->sizeu;
+ light->round = true;
+ break;
+ case BL::AreaLamp::shape_ELLIPSE:
+ light->sizev = b_area_lamp.size_y();
+ light->round = true;
+ break;
+ }
light->type = LIGHT_AREA;
break;
}
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index efab69ee37d..cd879e27e72 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -44,7 +44,7 @@ typedef struct LightSample {
*
* Note: light_p is modified when sample_coord is true.
*/
-ccl_device_inline float area_light_sample(float3 P,
+ccl_device_inline float rect_light_sample(float3 P,
float3 *light_p,
float3 axisu, float3 axisv,
float randu, float randv,
@@ -125,6 +125,60 @@ ccl_device_inline float area_light_sample(float3 P,
return 0.0f;
}
+ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv)
+{
+ to_unit_disk(&randu, &randv);
+ return ru*randu + rv*randv;
+}
+
+ccl_device float3 disk_light_sample(float3 v, float randu, float randv)
+{
+ float3 ru, rv;
+
+ make_orthonormals(v, &ru, &rv);
+
+ return ellipse_sample(ru, rv, randu, 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(float3 dir, float spot_angle, float spot_smooth, LightSample *ls)
+{
+ float3 I = ls->Ng;
+
+ float attenuation = dot(dir, I);
+
+ if(attenuation <= spot_angle) {
+ attenuation = 0.0f;
+ }
+ else {
+ float t = attenuation - spot_angle;
+
+ if(t < spot_smooth && spot_smooth != 0.0f)
+ attenuation *= smoothstepf(t/spot_smooth);
+ }
+
+ return attenuation;
+}
+
+ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t)
+{
+ float cos_pi = dot(Ng, I);
+
+ if(cos_pi <= 0.0f)
+ return 0.0f;
+
+ return t*t/cos_pi;
+}
+
/* Background Light */
#ifdef __BACKGROUND_MIS__
@@ -295,11 +349,19 @@ ccl_device_inline float background_portal_pdf(KernelGlobals *kg,
const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
float3 axisu = make_float3(klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
float3 axisv = make_float3(klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
+ bool is_round = (klight->area.invarea < 0.0f);
- if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL, NULL, NULL))
+ if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL, NULL, NULL, is_round))
continue;
- portal_pdf += area_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false);
+ if(is_round) {
+ float t;
+ float3 D = normalize_len(lightpos - P, &t);
+ portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
+ }
+ else {
+ portal_pdf += rect_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false);
+ }
}
if(ignore_portal >= 0) {
@@ -349,15 +411,26 @@ ccl_device float3 background_portal_sample(KernelGlobals *kg,
const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal);
float3 axisu = make_float3(klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
float3 axisv = make_float3(klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
-
- *pdf = area_light_sample(P, &lightpos,
- axisu, axisv,
- randu, randv,
- true);
+ bool is_round = (klight->area.invarea < 0.0f);
+
+ float3 D;
+ if(is_round) {
+ lightpos += ellipse_sample(axisu*0.5f, axisv*0.5f, randu, randv);
+ float t;
+ D = normalize_len(lightpos - P, &t);
+ *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
+ }
+ else {
+ *pdf = rect_light_sample(P, &lightpos,
+ axisu, axisv,
+ randu, randv,
+ true);
+ D = normalize(lightpos - P);
+ }
*pdf /= num_possible;
*sampled_portal = p;
- return normalize(lightpos - P);
+ return D;
}
portal--;
@@ -458,55 +531,6 @@ ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direct
/* 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(float3 dir, float spot_angle, float spot_smooth, LightSample *ls)
-{
- float3 I = ls->Ng;
-
- float attenuation = dot(dir, I);
-
- if(attenuation <= spot_angle) {
- attenuation = 0.0f;
- }
- else {
- float t = attenuation - spot_angle;
-
- if(t < spot_smooth && spot_smooth != 0.0f)
- attenuation *= smoothstepf(t/spot_smooth);
- }
-
- return attenuation;
-}
-
-ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t)
-{
- float cos_pi = dot(Ng, I);
-
- if(cos_pi <= 0.0f)
- return 0.0f;
-
- return t*t/cos_pi;
-}
-
ccl_device_inline bool lamp_light_sample(KernelGlobals *kg,
int lamp,
float randu, float randv,
@@ -601,26 +625,39 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg,
float3 D = make_float3(klight->area.dir[0],
klight->area.dir[1],
klight->area.dir[2]);
+ float invarea = fabsf(klight->area.invarea);
+ bool is_round = (klight->area.invarea < 0.0f);
if(dot(ls->P - P, D) > 0.0f) {
return false;
}
- float3 inplane = ls->P;
- ls->pdf = area_light_sample(P, &ls->P,
- axisu, axisv,
- randu, randv,
- true);
+ float3 inplane;
+
+ if(is_round) {
+ inplane = ellipse_sample(axisu*0.5f, axisv*0.5f, randu, randv);
+ ls->P += inplane;
+ ls->pdf = invarea;
+ }
+ else {
+ inplane = ls->P;
+ ls->pdf = rect_light_sample(P, &ls->P,
+ axisu, axisv,
+ randu, randv,
+ true);
+ inplane = ls->P - inplane;
+ }
- inplane = ls->P - inplane;
ls->u = dot(inplane, axisu) * (1.0f / dot(axisu, axisu)) + 0.5f;
ls->v = dot(inplane, axisv) * (1.0f / dot(axisv, axisv)) + 0.5f;
ls->Ng = D;
ls->D = normalize_len(ls->P - P, &ls->t);
- float invarea = klight->area.invarea;
ls->eval_fac = 0.25f*invarea;
+ if(is_round) {
+ ls->pdf *= lamp_light_pdf(kg, D, -ls->D, ls->t);
+ }
}
}
@@ -731,7 +768,8 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D,
}
else if(type == LIGHT_AREA) {
/* area light */
- float invarea = klight->area.invarea;
+ float invarea = fabsf(klight->area.invarea);
+ bool is_round = (klight->area.invarea < 0.0f);
if(invarea == 0.0f)
return false;
@@ -754,14 +792,20 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D,
if(!ray_quad_intersect(P, D, 0.0f, t, light_P,
axisu, axisv, Ng,
&ls->P, &ls->t,
- &ls->u, &ls->v))
+ &ls->u, &ls->v,
+ is_round))
{
return false;
}
ls->D = D;
ls->Ng = Ng;
- ls->pdf = area_light_sample(P, &light_P, axisu, axisv, 0, 0, false);
+ if(is_round) {
+ ls->pdf = invarea * lamp_light_pdf(kg, Ng, -D, ls->t);
+ }
+ else {
+ ls->pdf = rect_light_sample(P, &light_P, axisu, axisv, 0, 0, false);
+ }
ls->eval_fac = 0.25f*invarea;
}
else {
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 8dec7e4ea64..01a8b50cb21 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -117,6 +117,7 @@ NODE_DEFINE(Light)
SOCKET_FLOAT(sizeu, "Size U", 1.0f);
SOCKET_VECTOR(axisv, "Axis V", make_float3(0.0f, 0.0f, 0.0f));
SOCKET_FLOAT(sizev, "Size V", 1.0f);
+ SOCKET_BOOLEAN(round, "Round", false);
SOCKET_INT(map_resolution, "Map Resolution", 512);
@@ -730,12 +731,15 @@ void LightManager::device_update_points(Device *,
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;
+ if(light->round) {
+ area *= -M_PI_4_F;
+ }
+ float invarea = (area != 0.0f)? 1.0f/area: 1.0f;
float3 dir = light->dir;
dir = safe_normalize(dir);
- if(light->use_mis && area > 0.0f)
+ if(light->use_mis && area != 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co[0] = co.x;
@@ -803,7 +807,10 @@ void LightManager::device_update_points(Device *,
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;
+ if(light->round) {
+ area *= -M_PI_4_F;
+ }
+ float invarea = (area != 0.0f)? 1.0f/area: 1.0f;
float3 dir = light->dir;
dir = safe_normalize(dir);
diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h
index 97b7b971c73..539a33f4e41 100644
--- a/intern/cycles/render/light.h
+++ b/intern/cycles/render/light.h
@@ -49,6 +49,7 @@ public:
float sizeu;
float3 axisv;
float sizev;
+ bool round;
Transform tfm;
diff --git a/intern/cycles/util/util_math_intersect.h b/intern/cycles/util/util_math_intersect.h
index 61ddcc38f50..5f15487df57 100644
--- a/intern/cycles/util/util_math_intersect.h
+++ b/intern/cycles/util/util_math_intersect.h
@@ -186,12 +186,17 @@ ccl_device_forceinline bool ray_triangle_intersect(
#undef dot3
}
+/* Tests for an intersection between a ray and a quad defined by
+ * its midpoint, normal and sides.
+ * If ellipse is true, hits outside the ellipse that's enclosed by the
+ * quad are rejected.
+ */
ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D,
float ray_mint, float ray_maxt,
float3 quad_P,
float3 quad_u, float3 quad_v, float3 quad_n,
float3 *isect_P, float *isect_t,
- float *isect_u, float *isect_v)
+ float *isect_u, float *isect_v, bool ellipse)
{
/* Perform intersection test. */
float t = -(dot(ray_P, quad_n) - dot(quad_P, quad_n)) / dot(ray_D, quad_n);
@@ -200,20 +205,23 @@ ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D,
}
const float3 hit = ray_P + t*ray_D;
const float3 inplane = hit - quad_P;
- const float u = dot(inplane, quad_u) / dot(quad_u, quad_u) + 0.5f;
- if(u < 0.0f || u > 1.0f) {
+ const float u = dot(inplane, quad_u) / dot(quad_u, quad_u);
+ if(u < -0.5f || u > 0.5f) {
+ return false;
+ }
+ const float v = dot(inplane, quad_v) / dot(quad_v, quad_v);
+ if(v < -0.5f || v > 0.5f) {
return false;
}
- const float v = dot(inplane, quad_v) / dot(quad_v, quad_v) + 0.5f;
- if(v < 0.0f || v > 1.0f) {
+ if(ellipse && (u*u + v*v > 0.25f)) {
return false;
}
/* Store the result. */
/* TODO(sergey): Check whether we can avoid some checks here. */
if(isect_P != NULL) *isect_P = hit;
if(isect_t != NULL) *isect_t = t;
- if(isect_u != NULL) *isect_u = u;
- if(isect_v != NULL) *isect_v = v;
+ if(isect_u != NULL) *isect_u = u + 0.5f;
+ if(isect_v != NULL) *isect_v = v + 0.5f;
return true;
}
diff --git a/release/scripts/startup/bl_ui/properties_data_lamp.py b/release/scripts/startup/bl_ui/properties_data_lamp.py
index 1d7e5807b34..2e3dae25197 100644
--- a/release/scripts/startup/bl_ui/properties_data_lamp.py
+++ b/release/scripts/startup/bl_ui/properties_data_lamp.py
@@ -136,9 +136,9 @@ class DATA_PT_EEVEE_lamp(DataButtonsPanel, Panel):
elif lamp.type == 'AREA':
sub = sub.column(align=True)
sub.prop(lamp, "shape", text="")
- if lamp.shape == 'SQUARE':
+ if lamp.shape in {'SQUARE', 'DISK'}:
sub.prop(lamp, "size")
- elif lamp.shape == 'RECTANGLE':
+ elif lamp.shape in {'RECTANGLE', 'ELLIPSE'}:
sub.prop(lamp, "size", text="Size X")
sub.prop(lamp, "size_y", text="Size Y")
@@ -228,9 +228,9 @@ class DATA_PT_area(DataButtonsPanel, Panel):
col.row().prop(lamp, "shape", expand=True)
sub = col.row(align=True)
- if lamp.shape == 'SQUARE':
+ if lamp.shape in {'SQUARE', 'DISK'}:
sub.prop(lamp, "size")
- elif lamp.shape == 'RECTANGLE':
+ elif lamp.shape in {'RECTANGLE', 'ELLIPSE'}:
sub.prop(lamp, "size", text="Size X")
sub.prop(lamp, "size_y", text="Size Y")
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 1edee555eb6..2ebea02ed21 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -1591,7 +1591,7 @@ class VIEW3D_MT_object_specials(Menu):
props.data_path_item = "data.size"
props.header_text = "Lamp Size X: %.3f"
- if lamp.shape == 'RECTANGLE':
+ if lamp.shape in {'RECTANGLE', 'ELLIPSE'}:
props = layout.operator("wm.context_modal_mouse", text="Size Y")
props.data_path_iter = "selected_editable_objects"
props.data_path_item = "data.size_y"
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c
index 123dabbe226..9f3295e7095 100644
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ b/source/blender/draw/engines/eevee/eevee_lights.c
@@ -591,7 +591,7 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
}
else if (la->type == LA_AREA) {
evli->sizex = max_ff(0.0001f, la->area_size * scale[0] * 0.5f);
- if (la->area_shape == LA_AREA_RECT) {
+ if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) {
evli->sizey = max_ff(0.0001f, la->area_sizey * scale[1] * 0.5f);
}
else {
@@ -602,10 +602,18 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
evli->radius = max_ff(0.001f, la->area_size);
}
+ /* Lamp Type */
+ evli->lamptype = (float)la->type;
+
/* Make illumination power constant */
if (la->type == LA_AREA) {
power = 1.0f / (evli->sizex * evli->sizey * 4.0f * M_PI) * /* 1/(w*h*Pi) */
80.0f; /* XXX : Empirical, Fit cycles power */
+ if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
+ evli->lamptype = LAMPTYPE_AREA_ELLIPSE;
+ /* Scale power to account for the lower area of the ellipse compared to the surrouding rectangle. */
+ power *= 4.0f / M_PI;
+ }
}
else if (la->type == LA_SPOT || la->type == LA_LOCAL) {
power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI) * /* 1/(4*r²*Pi²) */
@@ -620,9 +628,6 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
}
mul_v3_fl(evli->color, power * la->energy);
- /* Lamp Type */
- evli->lamptype = (float)la->type;
-
/* No shadow by default */
evli->shadowid = -1.0f;
}
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index c95e51548d0..a5f4f6d85a8 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -289,6 +289,9 @@ typedef struct EEVEE_Light {
float forwardvec[3], lamptype;
} EEVEE_Light;
+/* Special type for elliptic area lamps, matches lamps_lib.glsl */
+#define LAMPTYPE_AREA_ELLIPSE 100.0f
+
typedef struct EEVEE_Shadow {
float near, far, bias, exp;
float shadow_start, data_start, multi_shadow_count, shadow_blur;
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl
index d5b54097638..aad71b3e48e 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl
@@ -47,6 +47,16 @@ float direct_diffuse_rectangle(LightData ld, vec3 N, vec3 V, vec4 l_vector)
return ltc_evaluate_quad(corners, N);
}
+float direct_diffuse_ellipse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
+{
+ vec3 points[3];
+ points[0] = l_vector.xyz + ld.l_right * -ld.l_sizex + ld.l_up * -ld.l_sizey;
+ points[1] = l_vector.xyz + ld.l_right * ld.l_sizex + ld.l_up * -ld.l_sizey;
+ points[2] = l_vector.xyz + ld.l_right * ld.l_sizex + ld.l_up * ld.l_sizey;
+
+ return ltc_evaluate_disk(N, V, mat3(1), points);
+}
+
float direct_diffuse_unit_disc(LightData ld, vec3 N, vec3 V)
{
float NL = dot(N, -ld.l_forward);
@@ -107,6 +117,26 @@ vec3 direct_ggx_sphere(LightData ld, vec3 N, vec3 V, vec4 l_vector, float roughn
return spec;
}
+vec3 direct_ggx_ellipse(LightData ld, vec3 N, vec3 V, vec4 l_vector, float roughness, vec3 f0)
+{
+ vec3 points[3];
+ points[0] = l_vector.xyz + ld.l_right * -ld.l_sizex + ld.l_up * -ld.l_sizey;
+ points[1] = l_vector.xyz + ld.l_right * ld.l_sizex + ld.l_up * -ld.l_sizey;
+ points[2] = l_vector.xyz + ld.l_right * ld.l_sizex + ld.l_up * ld.l_sizey;
+
+ vec2 uv = lut_coords(dot(N, V), sqrt(roughness));
+ vec3 brdf_lut = texture(utilTex, vec3(uv, 1.0)).rgb;
+ vec4 ltc_lut = texture(utilTex, vec3(uv, 0.0)).rgba;
+ mat3 ltc_mat = ltc_matrix(ltc_lut);
+
+ float bsdf = ltc_evaluate_disk(N, V, ltc_mat, points);
+ bsdf *= brdf_lut.b; /* Bsdf intensity */
+
+ vec3 spec = F_area(f0, brdf_lut.xy) * bsdf;
+
+ return spec;
+}
+
vec3 direct_ggx_rectangle(LightData ld, vec3 N, vec3 V, vec4 l_vector, float roughness, vec3 f0)
{
vec3 corners[4];
diff --git a/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl b/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl
index 198a05ccf97..0e6f976187a 100644
--- a/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl
@@ -15,11 +15,13 @@ layout(std140) uniform light_block {
};
/* type */
-#define POINT 0.0
-#define SUN 1.0
-#define SPOT 2.0
-#define HEMI 3.0
-#define AREA 4.0
+#define POINT 0.0
+#define SUN 1.0
+#define SPOT 2.0
+#define HEMI 3.0
+#define AREA_RECT 4.0
+/* Used to define the area lamp shape, doesn't directly correspond to a Blender lamp type. */
+#define AREA_ELLIPSE 100.0
#if defined(SHADOW_VSM)
#define ShadowSample vec2
@@ -174,7 +176,7 @@ float light_visibility(LightData ld, vec3 W,
vis *= spotmask;
vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
}
- else if (ld.l_type == AREA) {
+ else if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
}
@@ -253,9 +255,12 @@ float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
if (ld.l_type == SUN) {
return direct_diffuse_unit_disc(ld, N, V);
}
- else if (ld.l_type == AREA) {
+ else if (ld.l_type == AREA_RECT) {
return direct_diffuse_rectangle(ld, N, V, l_vector);
}
+ else if (ld.l_type == AREA_ELLIPSE) {
+ return direct_diffuse_ellipse(ld, N, V, l_vector);
+ }
else {
return direct_diffuse_sphere(ld, N, l_vector);
}
@@ -275,9 +280,12 @@ vec3 light_specular(LightData ld, vec3 N, vec3 V, vec4 l_vector, float roughness
if (ld.l_type == SUN) {
return direct_ggx_unit_disc(ld, N, V, roughness, f0);
}
- else if (ld.l_type == AREA) {
+ else if (ld.l_type == AREA_RECT) {
return direct_ggx_rectangle(ld, N, V, l_vector, roughness, f0);
}
+ else if (ld.l_type == AREA_ELLIPSE) {
+ return direct_ggx_ellipse(ld, N, V, l_vector, roughness, f0);
+ }
else {
return direct_ggx_sphere(ld, N, V, l_vector, roughness, f0);
}
@@ -373,8 +381,11 @@ vec3 light_translucent(LightData ld, vec3 W, vec3 N, vec4 l_vector, float scale)
/* XXX : Removing Area Power. */
/* TODO : put this out of the shader. */
float falloff;
- if (ld.l_type == AREA) {
+ if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
vis *= (ld.l_sizex * ld.l_sizey * 4.0 * M_PI) * (1.0 / 80.0);
+ if (ld.l_type == AREA_ELLIPSE) {
+ vis *= M_PI * 0.25;
+ }
vis *= 0.3 * 20.0 * max(0.0, dot(-ld.l_forward, l_vector.xyz / l_vector.w)); /* XXX ad hoc, empirical */
vis /= (l_vector.w * l_vector.w);
falloff = dot(N, l_vector.xyz / l_vector.w);
@@ -412,7 +423,7 @@ vec3 light_translucent(LightData ld, vec3 W, vec3 N, vec4 l_vector, float scale)
vis *= spotmask;
vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
}
- else if (ld.l_type == AREA) {
+ else if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
}
}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
index 1a8167c2830..5f641c5d490 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
@@ -66,8 +66,11 @@ vec3 light_volume(LightData ld, vec4 l_vector)
/* XXX : Removing Area Power. */
/* TODO : put this out of the shader. */
/* See eevee_light_setup(). */
- if (ld.l_type == AREA) {
+ if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
power = (ld.l_sizex * ld.l_sizey * 4.0 * M_PI) * (1.0 / 80.0);
+ if (ld.l_type == AREA_ELLIPSE) {
+ power *= M_PI * 0.25;
+ }
power *= 20.0 * max(0.0, dot(-ld.l_forward, l_vector.xyz / l_vector.w)); /* XXX ad hoc, empirical */
}
else if (ld.l_type == SUN) {
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index 6065e7f0fc5..96edb7cd429 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -72,7 +72,8 @@ static struct DRWShapeCache {
Gwn_Batch *drw_lamp;
Gwn_Batch *drw_lamp_shadows;
Gwn_Batch *drw_lamp_sunrays;
- Gwn_Batch *drw_lamp_area;
+ Gwn_Batch *drw_lamp_area_square;
+ Gwn_Batch *drw_lamp_area_disk;
Gwn_Batch *drw_lamp_hemi;
Gwn_Batch *drw_lamp_spot;
Gwn_Batch *drw_lamp_spot_square;
@@ -1122,9 +1123,9 @@ Gwn_Batch *DRW_cache_lamp_sunrays_get(void)
return SHC.drw_lamp_sunrays;
}
-Gwn_Batch *DRW_cache_lamp_area_get(void)
+Gwn_Batch *DRW_cache_lamp_area_square_get(void)
{
- if (!SHC.drw_lamp_area) {
+ if (!SHC.drw_lamp_area_square) {
float v1[3] = {0.0f, 0.0f, 0.0f};
/* Position Only 3D format */
@@ -1151,9 +1152,40 @@ Gwn_Batch *DRW_cache_lamp_area_get(void)
v1[1] = 0.5f;
GWN_vertbuf_attr_set(vbo, attr_id.pos, 7, v1);
- SHC.drw_lamp_area = GWN_batch_create_ex(GWN_PRIM_LINES, vbo, NULL, GWN_BATCH_OWNS_VBO);
+ SHC.drw_lamp_area_square = GWN_batch_create_ex(GWN_PRIM_LINES, vbo, NULL, GWN_BATCH_OWNS_VBO);
}
- return SHC.drw_lamp_area;
+ return SHC.drw_lamp_area_square;
+}
+
+Gwn_Batch *DRW_cache_lamp_area_disk_get(void)
+{
+#define NSEGMENTS 32
+ if (!SHC.drw_lamp_area_disk) {
+ /* Position Only 3D format */
+ static Gwn_VertFormat format = { 0 };
+ static struct { uint pos; } attr_id;
+ if (format.attrib_ct == 0) {
+ attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+ }
+
+ Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
+ GWN_vertbuf_data_alloc(vbo, 2*NSEGMENTS);
+
+ float v[3] = {0.0f, 0.5f, 0.0f};
+ GWN_vertbuf_attr_set(vbo, attr_id.pos, 0, v);
+ for (int a = 1; a < NSEGMENTS; a++) {
+ v[0] = 0.5f*sinf(2.f * (float)M_PI * a / NSEGMENTS);
+ v[1] = 0.5f*cosf(2.f * (float)M_PI * a / NSEGMENTS);
+ GWN_vertbuf_attr_set(vbo, attr_id.pos, 2*a-1, v);
+ GWN_vertbuf_attr_set(vbo, attr_id.pos, 2*a, v);
+ }
+ copy_v3_fl3(v, 0.0f, 0.5f, 0.0f);
+ GWN_vertbuf_attr_set(vbo, attr_id.pos, 2*NSEGMENTS-1, v);
+
+ SHC.drw_lamp_area_disk = GWN_batch_create_ex(GWN_PRIM_LINES, vbo, NULL, GWN_BATCH_OWNS_VBO);
+ }
+ return SHC.drw_lamp_area_disk;
+#undef NSEGMENTS
}
Gwn_Batch *DRW_cache_lamp_hemi_get(void)
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 8bc609ffe1f..8cf38cdd123 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -78,7 +78,8 @@ struct Gwn_Batch *DRW_cache_field_cone_limit_get(void);
struct Gwn_Batch *DRW_cache_lamp_get(void);
struct Gwn_Batch *DRW_cache_lamp_shadows_get(void);
struct Gwn_Batch *DRW_cache_lamp_sunrays_get(void);
-struct Gwn_Batch *DRW_cache_lamp_area_get(void);
+struct Gwn_Batch *DRW_cache_lamp_area_square_get(void);
+struct Gwn_Batch *DRW_cache_lamp_area_disk_get(void);
struct Gwn_Batch *DRW_cache_lamp_hemi_get(void);
struct Gwn_Batch *DRW_cache_lamp_spot_get(void);
struct Gwn_Batch *DRW_cache_lamp_spot_square_get(void);
diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c
index 58cdfd4e413..40c721e64de 100644
--- a/source/blender/draw/modes/object_mode.c
+++ b/source/blender/draw/modes/object_mode.c
@@ -175,7 +175,8 @@ typedef struct OBJECT_PrivateData {
DRWShadingGroup *lamp_distance;
DRWShadingGroup *lamp_buflimit;
DRWShadingGroup *lamp_buflimit_points;
- DRWShadingGroup *lamp_area;
+ DRWShadingGroup *lamp_area_square;
+ DRWShadingGroup *lamp_area_disk;
DRWShadingGroup *lamp_hemi;
DRWShadingGroup *lamp_spot_cone;
DRWShadingGroup *lamp_spot_blend;
@@ -1164,8 +1165,11 @@ static void OBJECT_cache_init(void *vedata)
stl->g_data->lamp_groundline = shgroup_groundlines_uniform_color(psl->non_meshes, ts.colorLamp);
stl->g_data->lamp_groundpoint = shgroup_groundpoints_uniform_color(psl->non_meshes, ts.colorLamp);
- geom = DRW_cache_lamp_area_get();
- stl->g_data->lamp_area = shgroup_instance(psl->non_meshes, geom);
+ geom = DRW_cache_lamp_area_square_get();
+ stl->g_data->lamp_area_square = shgroup_instance(psl->non_meshes, geom);
+
+ geom = DRW_cache_lamp_area_disk_get();
+ stl->g_data->lamp_area_disk = shgroup_instance(psl->non_meshes, geom);
geom = DRW_cache_lamp_hemi_get();
stl->g_data->lamp_hemi = shgroup_instance(psl->non_meshes, geom);
@@ -1398,13 +1402,18 @@ static void DRW_shgroup_lamp(OBJECT_StorageList *stl, Object *ob, ViewLayer *vie
else if (la->type == LA_AREA) {
float size[3] = {1.0f, 1.0f, 1.0f}, sizemat[4][4];
- if (la->area_shape == LA_AREA_RECT) {
+ if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) {
size[1] = la->area_sizey / la->area_size;
size_to_mat4(sizemat, size);
mul_m4_m4m4(shapemat, shapemat, sizemat);
}
- DRW_shgroup_call_dynamic_add(stl->g_data->lamp_area, color, &la->area_size, shapemat);
+ if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
+ DRW_shgroup_call_dynamic_add(stl->g_data->lamp_area_disk, color, &la->area_size, shapemat);
+ }
+ else {
+ DRW_shgroup_call_dynamic_add(stl->g_data->lamp_area_square, color, &la->area_size, shapemat);
+ }
}
/* Line and point going to the ground */
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 6b22521eedd..77514ca1e8e 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -631,6 +631,10 @@ static int apply_objects_internal(
la->area_shape = LA_AREA_RECT;
la->area_sizey = la->area_size;
}
+ else if ((la->area_shape == LA_AREA_DISK) && !keeps_aspect_ratio) {
+ la->area_shape = LA_AREA_ELLIPSE;
+ la->area_sizey = la->area_size;
+ }
la->area_size *= rsmat[0][0];
la->area_sizey *= rsmat[1][1];
diff --git a/source/blender/editors/space_view3d/view3d_manipulator_lamp.c b/source/blender/editors/space_view3d/view3d_manipulator_lamp.c
index 93b6b69a105..88c36fc2c0b 100644
--- a/source/blender/editors/space_view3d/view3d_manipulator_lamp.c
+++ b/source/blender/editors/space_view3d/view3d_manipulator_lamp.c
@@ -129,7 +129,7 @@ static void manipulator_area_lamp_prop_matrix_get(
const Lamp *la = mpr_prop->custom_func.user_data;
matrix[0][0] = la->area_size;
- matrix[1][1] = (la->area_shape == LA_AREA_RECT) ? la->area_sizey : la->area_size;
+ matrix[1][1] = ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE) ? la->area_sizey : la->area_size;
}
static void manipulator_area_lamp_prop_matrix_set(
@@ -140,7 +140,7 @@ static void manipulator_area_lamp_prop_matrix_set(
BLI_assert(mpr_prop->type->array_length == 16);
Lamp *la = mpr_prop->custom_func.user_data;
- if (la->area_shape == LA_AREA_RECT) {
+ if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) {
la->area_size = len_v3(matrix[0]);
la->area_sizey = len_v3(matrix[1]);
}
@@ -185,9 +185,11 @@ static void WIDGETGROUP_lamp_area_refresh(const bContext *C, wmManipulatorGroup
copy_m4_m4(mpr->matrix_basis, ob->obmat);
- RNA_enum_set(mpr->ptr, "transform",
- ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE |
- ((la->area_shape == LA_AREA_SQUARE) ? ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE_UNIFORM : 0));
+ int flag = ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE;
+ if (ELEM(la->area_shape, LA_AREA_SQUARE, LA_AREA_DISK)) {
+ flag |= ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE_UNIFORM;
+ }
+ RNA_enum_set(mpr->ptr, "transform", flag);
/* need to set property here for undo. TODO would prefer to do this in _init */
WM_manipulator_target_property_def_func(
diff --git a/source/blender/makesdna/DNA_lamp_types.h b/source/blender/makesdna/DNA_lamp_types.h
index 6294afe00e7..3a4b07cc791 100644
--- a/source/blender/makesdna/DNA_lamp_types.h
+++ b/source/blender/makesdna/DNA_lamp_types.h
@@ -148,6 +148,8 @@ typedef struct Lamp {
#define LA_AREA_RECT 1
#define LA_AREA_CUBE 2
#define LA_AREA_BOX 3
+#define LA_AREA_DISK 4
+#define LA_AREA_ELLIPSE 5
#endif /* __DNA_LAMP_TYPES_H__ */
diff --git a/source/blender/makesrna/intern/rna_lamp.c b/source/blender/makesrna/intern/rna_lamp.c
index 5bb24f0270d..d123cdb9773 100644
--- a/source/blender/makesrna/intern/rna_lamp.c
+++ b/source/blender/makesrna/intern/rna_lamp.c
@@ -394,6 +394,8 @@ static void rna_def_area_lamp(BlenderRNA *brna)
static const EnumPropertyItem prop_areashape_items[] = {
{LA_AREA_SQUARE, "SQUARE", 0, "Square", ""},
{LA_AREA_RECT, "RECTANGLE", 0, "Rectangle", ""},
+ {LA_AREA_DISK, "DISK", 0, "Disk", ""},
+ {LA_AREA_ELLIPSE, "ELLIPSE", 0, "Ellipse", ""},
{0, NULL, 0, NULL, NULL}
};