Merge branch 'master' into blender2.8
[blender.git] / intern / cycles / kernel / kernel_light.h
index b5a777efa783c176fc2c54bf966ed062e54bc502..0165eaa606fa5e96a0fa8d67581472250b5b7d9b 100644 (file)
@@ -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,
@@ -118,6 +118,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__
@@ -291,11 +345,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) {
@@ -345,15 +407,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--;
@@ -454,55 +527,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,
@@ -597,26 +621,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);
+                       }
                }
        }
 
@@ -727,7 +764,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;
 
@@ -750,14 +788,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 {