diff options
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} }; |