Cycles: multiple importance sampling for lamps, which helps reduce noise for
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Wed, 9 Jan 2013 21:09:20 +0000 (21:09 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Wed, 9 Jan 2013 21:09:20 +0000 (21:09 +0000)
big lamps and sharp glossy reflections. This was already supported for mesh
lights and the background, so lamps should do it too.

This is not for free and it's a bit slower than I hoped even though there is
no extra BVH ray intersection. I'll try to optimize it more later.

* Area lights look a bit different now, they had the wrong shape before.
* Also fixes a sampling issue in the non-progressive integrator.
* Only enabled for the CPU, will test on the GPU later.
* An option to disable this will be added for situations where it does not help.

Same time comparison before/after:
http://www.pasteall.org/pic/show.php?id=43313
http://www.pasteall.org/pic/show.php?id=43314

intern/cycles/blender/blender_object.cpp
intern/cycles/kernel/kernel_camera.h
intern/cycles/kernel/kernel_emission.h
intern/cycles/kernel/kernel_light.h
intern/cycles/kernel/kernel_path.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/osl/osl_services.cpp
intern/cycles/kernel/osl/osl_shader.cpp
intern/cycles/render/light.cpp
intern/cycles/util/util_math.h

index ad701266c5bb5a4e62a8cbb6c20c0b6e0edcf578..e9bcea70ab6860a584853b2540a50c1daa41cbff 100644 (file)
@@ -127,8 +127,8 @@ void BlenderSync::sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSI
                case BL::Lamp::type_AREA: {
                        BL::AreaLamp b_area_lamp(b_lamp);
                        light->size = 1.0f;
-                       light->axisu = make_float3(tfm.x.x, tfm.y.x, tfm.z.x);
-                       light->axisv = make_float3(tfm.x.y, tfm.y.y, tfm.z.y);
+                       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();
@@ -140,8 +140,8 @@ void BlenderSync::sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSI
        }
 
        /* location and (inverted!) direction */
-       light->co = make_float3(tfm.x.w, tfm.y.w, tfm.z.w);
-       light->dir = -make_float3(tfm.x.z, tfm.y.z, tfm.z.z);
+       light->co = transform_get_column(&tfm, 3);
+       light->dir = -transform_get_column(&tfm, 2);
 
        /* shader */
        vector<uint> used_shaders;
index 97d37a8b3f4a3033704aaeca9505c8fb00a3a214..cd896ffe133120f1a76051992833783867fb8df6 100644 (file)
@@ -199,7 +199,6 @@ __device void camera_sample_panorama(KernelGlobals *kg, float raster_x, float ra
 
        Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f));
        ray->dD.dy = normalize(transform_direction(&cameratoworld, panorama_to_direction(kg, Pcamera.x, Pcamera.y))) - ray->D;
-
 #endif
 }
 
index d5506ad1dd0de52b8c0f6274b6e4188ef3c8070a..54bc0717b60306e0332c0db17f3f32c6d491f02a 100644 (file)
@@ -66,6 +66,8 @@ __device float3 direct_emissive_eval(KernelGlobals *kg, float rando,
                else
                        eval = make_float3(0.0f, 0.0f, 0.0f);
        }
+       
+       eval *= ls->eval_fac;
 
        shader_release(kg, &sd);
 
@@ -74,29 +76,29 @@ __device float3 direct_emissive_eval(KernelGlobals *kg, float rando,
 
 __device bool direct_emission(KernelGlobals *kg, ShaderData *sd, int lindex,
        float randt, float rando, float randu, float randv, Ray *ray, BsdfEval *eval,
-       bool *is_lamp)
+       int *lamp)
 {
        LightSample ls;
 
-       float pdf = -1.0f;
-
 #ifdef __NON_PROGRESSIVE__
        if(lindex != -1) {
                /* sample position on a specified light */
-               light_select(kg, lindex, randu, randv, sd->P, &ls, &pdf);
+               light_select(kg, lindex, randu, randv, sd->P, &ls);
        }
        else
 #endif
        {
                /* sample a light and position on int */
-               light_sample(kg, randt, randu, randv, sd->time, sd->P, &ls, &pdf);
+               light_sample(kg, randt, randu, randv, sd->time, sd->P, &ls);
        }
 
-       /* compute pdf */
-       if(pdf < 0.0f)
-               pdf = light_sample_pdf(kg, &ls, -ls.D, ls.t);
+       /* return lamp index for MIS */
+       if(ls.use_mis)
+               *lamp = ls.lamp;
+       else
+               *lamp= ~0;
 
-       if(pdf == 0.0f)
+       if(ls.pdf == 0.0f)
                return false;
 
        /* evaluate closure */
@@ -112,13 +114,13 @@ __device bool direct_emission(KernelGlobals *kg, ShaderData *sd, int lindex,
 
        shader_bsdf_eval(kg, sd, ls.D, eval, &bsdf_pdf);
 
-       if(ls.prim != ~0 || ls.type == LIGHT_BACKGROUND) {
+       if(ls.use_mis) {
                /* multiple importance sampling */
-               float mis_weight = power_heuristic(pdf, bsdf_pdf);
+               float mis_weight = power_heuristic(ls.pdf, bsdf_pdf);
                light_eval *= mis_weight;
        }
        
-       bsdf_eval_mul(eval, light_eval*(ls.eval_fac/pdf));
+       bsdf_eval_mul(eval, light_eval/ls.pdf);
 
        if(bsdf_eval_is_zero(eval))
                return false;
@@ -144,14 +146,12 @@ __device bool direct_emission(KernelGlobals *kg, ShaderData *sd, int lindex,
                ray->t = 0.0f;
        }
 
-       *is_lamp = (ls.prim == ~0);
-
        return true;
 }
 
-/* Indirect Emission */
+/* Indirect Primitive Emission */
 
-__device float3 indirect_emission(KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf)
+__device float3 indirect_primitive_emission(KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf)
 {
        /* evaluate emissive closure */
        float3 L = shader_emissive_eval(kg, sd);
@@ -172,6 +172,35 @@ __device float3 indirect_emission(KernelGlobals *kg, ShaderData *sd, float t, in
        return L;
 }
 
+/* Indirect Lamp Emission */
+
+__device bool indirect_lamp_emission(KernelGlobals *kg, Ray *ray, int path_flag, float bsdf_pdf, float randt, float3 *emission)
+{
+       LightSample ls;
+       int lamp = lamp_light_eval_sample(kg, randt);
+
+       if(lamp == ~0)
+               return false;
+
+       if(!lamp_light_eval(kg, lamp, ray->P, ray->D, ray->t, &ls))
+               return false;
+       
+       /* todo: missing texture coordinates */
+       float u = 0.0f;
+       float v = 0.0f;
+       float3 L = direct_emissive_eval(kg, 0.0f, &ls, u, v, -ray->D, ls.t, ray->time);
+
+       if(!(path_flag & PATH_RAY_MIS_SKIP)) {
+               /* multiple importance sampling, get regular light pdf,
+                * and compute weight with respect to BSDF pdf */
+               float mis_weight = power_heuristic(bsdf_pdf, ls.pdf);
+               L *= mis_weight;
+       }
+
+       *emission = L;
+       return true;
+}
+
 /* Indirect Background */
 
 __device float3 indirect_background(KernelGlobals *kg, Ray *ray, int path_flag, float bsdf_pdf)
index ea0e4d014fe56a0efb6cc37ee9c905f81ba63c0d..df5acca6e655f8cec7f460c290395e00ee4220eb 100644 (file)
 
 CCL_NAMESPACE_BEGIN
 
+/* Light Sample result */
+
 typedef struct LightSample {
-       float3 P;
-       float3 D;
-       float3 Ng;
-       float t;
-       float eval_fac;
-       int object;
-       int prim;
-       int shader;
-       LightType type;
+       float3 P;                       /* position on light, or direction for distant light */
+       float3 Ng;                      /* normal on light */
+       float3 D;                       /* direction from shading point to light */
+       float t;                        /* distance to light (FLT_MAX for distant light) */
+       float pdf;                      /* light sampling probability density function */
+       float eval_fac;         /* intensity multiplier */
+       int object;                     /* object id for triangle/curve lights */
+       int prim;                       /* primitive id for triangle/curve ligths */
+       int shader;                     /* shader id */
+       int lamp;                       /* lamp id */
+       int use_mis;            /* for lamps with size zero */
+       LightType type;         /* type of light */
 } LightSample;
 
-/* Regular Light */
-
-__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;
-}
-
-__device float3 distant_light_sample(float3 D, float size, float randu, float randv)
-{
-       return normalize(D + disk_light_sample(D, randu, randv)*size);
-}
-
-__device float3 sphere_light_sample(float3 P, float3 center, float size, float randu, float randv)
-{
-       return disk_light_sample(normalize(P - center), randu, randv)*size;
-}
-
-__device float3 area_light_sample(float3 axisu, float3 axisv, float randu, float randv)
-{
-       randu = randu - 0.5f;
-       randv = randv - 0.5f;
-
-       return axisu*randu + axisv*randv;
-}
+/* Background Light */
 
 #ifdef __BACKGROUND_MIS__
+
 __device 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
@@ -169,33 +147,108 @@ __device float background_light_pdf(KernelGlobals *kg, float3 direction)
 }
 #endif
 
-__device void regular_light_sample(KernelGlobals *kg, int point,
-       float randu, float randv, float3 P, LightSample *ls, float *pdf)
+/* Regular Light */
+
+__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;
+}
+
+__device float3 distant_light_sample(float3 D, float radius, float randu, float randv)
+{
+       return normalize(D + disk_light_sample(D, randu, randv)*radius);
+}
+
+__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;
+}
+
+__device float3 area_light_sample(float3 axisu, float3 axisv, float randu, float randv)
+{
+       randu = randu - 0.5f;
+       randv = randv - 0.5f;
+
+       return axisu*randu + axisv*randv;
+}
+
+__device float spot_light_attenuation(float4 data1, float4 data2, LightSample *ls)
 {
-       float4 data0 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 0);
-       float4 data1 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 1);
+       float3 dir = make_float3(data2.y, data2.z, data2.w);
+       float3 I = ls->Ng;
+
+       float spot_angle = data1.w;
+       float spot_smooth = data2.x;
+
+       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;
+}
+
+__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;
+}
+
+__device void lamp_light_sample(KernelGlobals *kg, int lamp,
+       float randu, float randv, float3 P, LightSample *ls)
+{
+       float4 data0 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 0);
+       float4 data1 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 1);
 
        LightType type = (LightType)__float_as_int(data0.x);
        ls->type = type;
+#ifdef __LAMP_MIS__
+       ls->use_mis = true;
+#else
+       ls->use_mis = false;
+#endif
 
        if(type == LIGHT_DISTANT) {
                /* distant light */
-               float3 D = make_float3(data0.y, data0.z, data0.w);
-               float size = data1.y;
+               float3 lightD = make_float3(data0.y, data0.z, data0.w);
+               float3 D = lightD;
+               float radius = data1.y;
+               float invarea = data1.w;
 
-               if(size > 0.0f)
-                       D = distant_light_sample(D, size, randu, randv);
+               if(radius > 0.0f)
+                       D = distant_light_sample(D, radius, randu, randv);
+               else
+                       ls->use_mis = false;
 
                ls->P = D;
                ls->Ng = D;
                ls->D = -D;
                ls->t = FLT_MAX;
-               ls->eval_fac = 1.0f;
+
+               float costheta = dot(lightD, D);
+               ls->pdf = invarea/(costheta*costheta*costheta);
+               ls->eval_fac = ls->pdf;
        }
 #ifdef __BACKGROUND_MIS__
        else if(type == LIGHT_BACKGROUND) {
                /* infinite area light (e.g. light dome or env light) */
-               float3 D = background_light_sample(kg, randu, randv, pdf);
+               float3 D = background_light_sample(kg, randu, randv, &ls->pdf);
 
                ls->P = D;
                ls->Ng = D;
@@ -207,127 +260,240 @@ __device void regular_light_sample(KernelGlobals *kg, int point,
        else {
                ls->P = make_float3(data0.y, data0.z, data0.w);
 
-               if(type == LIGHT_POINT) {
-                       float size = data1.y;
-
-                       /* sphere light */
-                       if(size > 0.0f)
-                               ls->P += sphere_light_sample(P, ls->P, size, randu, randv);
-
-                       ls->Ng = normalize(P - ls->P);
-                       ls->eval_fac = 0.25f*M_1_PI_F;
-               }
-               else if(type == LIGHT_SPOT) {
-                       float4 data2 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 2);
-                       float size = data1.y;
-
-                       /* spot light */
-                       if(size > 0.0f)
-                               ls->P += sphere_light_sample(P, ls->P, size, randu, randv);
-
-                       float3 dir = make_float3(data1.z, data1.w, data2.x);
-                       float3 I = normalize(P - ls->P);
+               if(type == LIGHT_POINT || type == LIGHT_SPOT) {
+                       float radius = data1.y;
 
-                       float spot_angle = data2.y;
-                       float spot_smooth = data2.z;
+                       if(radius > 0.0f)
+                               /* sphere light */
+                               ls->P += sphere_light_sample(P, ls->P, radius, randu, randv);
+                       else
+                               ls->use_mis = false;
 
-                       float eval_fac = dot(dir, I);
+                       ls->D = normalize_len(ls->P - P, &ls->t);
+                       ls->Ng = -ls->D;
 
-                       if(eval_fac <= spot_angle) {
-                               eval_fac = 0.0f;
-                       }
-                       else {
-                               float t = eval_fac - spot_angle;
+                       float invarea = data1.z;
+                       ls->eval_fac = (0.25f*M_1_PI_F)*invarea;
+                       ls->pdf = invarea;
 
-                               if(t < spot_smooth && spot_smooth != 0.0f)
-                                       eval_fac *= smoothstepf(t/spot_smooth);
+                       if(type == LIGHT_SPOT) {
+                               /* spot light attentuation */
+                               float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2);
+                               ls->eval_fac *= spot_light_attenuation(data1, data2, ls);
                        }
-
-                       ls->Ng = I;
-                       ls->eval_fac = eval_fac*0.25f*M_1_PI_F;
                }
                else {
                        /* area light */
-                       float4 data2 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 2);
-                       float4 data3 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 3);
+                       float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2);
+                       float4 data3 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 3);
 
-                       float3 axisu = make_float3(data1.y, data1.z, data2.w);
+                       float3 axisu = make_float3(data1.y, data1.z, data1.w);
                        float3 axisv = make_float3(data2.y, data2.z, data2.w);
                        float3 D = make_float3(data3.y, data3.z, data3.w);
 
                        ls->P += area_light_sample(axisu, axisv, randu, randv);
                        ls->Ng = D;
-                       ls->eval_fac = 0.25f;
-               }
+                       ls->D = normalize_len(ls->P - P, &ls->t);
+
+                       float invarea = data2.x;
 
-               ls->t = 0.0f;
+                       if(invarea == 0.0f) {
+                               ls->use_mis = false;
+                               invarea = 1.0f;
+                       }
+
+                       ls->pdf = invarea;
+                       ls->eval_fac = 0.25f*ls->pdf;
+               }
        }
 
        ls->shader = __float_as_int(data1.x);
        ls->object = ~0;
        ls->prim = ~0;
+       ls->lamp = lamp;
+
+       /* compute pdf */
+       if(ls->t != FLT_MAX)
+               ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
+       
+       /* this is a bit weak, but we don't want this as part of the pdf for
+        * multiple importance sampling */
+       ls->eval_fac *= kernel_data.integrator.inv_pdf_lights;
 }
 
-__device float regular_light_pdf(KernelGlobals *kg,
-       const float3 Ng, const float3 I, float t)
+__device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls)
 {
-       float pdf = kernel_data.integrator.pdf_lights;
+       float4 data0 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 0);
+       float4 data1 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 1);
 
-       if(t == FLT_MAX)
-               return pdf;
+       LightType type = (LightType)__float_as_int(data0.x);
+       ls->type = type;
+       ls->shader = __float_as_int(data1.x);
+       ls->object = ~0;
+       ls->prim = ~0;
+       ls->lamp = lamp;
+       ls->use_mis = false; /* flag not used for eval */
 
-       float cos_pi = dot(Ng, I);
+       if(type == LIGHT_DISTANT) {
+               /* distant light */
+               float radius = data1.y;
+
+               if(radius == 0.0f)
+                       return false;
+               if(t != FLT_MAX)
+                       return false;
+
+               /* a distant light is infinitely far away, but equivalent to a disk
+                * shaped light exactly 1 unit away from the current shading point.
+                *
+                *     radius              t^2/cos(theta)
+                *  <---------->           t = sqrt(1^2 + tan(theta)^2)
+                *       tan(th)           area = radius*radius*pi
+                *       <----->
+                *        \    |           (1 + tan(theta)^2)/cos(theta)
+                *         \   |           (1 + tan(acos(cos(theta)))^2)/cos(theta)
+                *       t  \th| 1         simplifies to
+                *           \-|           1/(cos(theta)^3)
+                *            \|           magic!
+                *             P
+                */
+
+               float3 lightD = make_float3(data0.y, data0.z, data0.w);
+               float costheta = dot(-lightD, D);
+               float cosangle = data1.z;
+
+               if(costheta < cosangle)
+                       return false;
+
+               ls->P = -D;
+               ls->Ng = -D;
+               ls->D = D;
+               ls->t = FLT_MAX;
 
-       if(cos_pi <= 0.0f)
-               return 0.0f;
+               float invarea = data1.w;
+               ls->pdf = invarea/(costheta*costheta*costheta);
+               ls->eval_fac = ls->pdf;
+       }
+       else if(type == LIGHT_POINT || type == LIGHT_SPOT) {
+               float3 lightP = make_float3(data0.y, data0.z, data0.w);
+               float radius = data1.y;
 
-       return t*t*pdf/cos_pi;
+               /* sphere light */
+               if(radius == 0.0f)
+                       return false;
+
+               if(!ray_aligned_disk_intersect(P, D, t,
+                       lightP, radius, &ls->P, &ls->t))
+                       return false;
+
+               ls->Ng = -D;
+               ls->D = D;
+
+               float invarea = data1.z;
+               ls->eval_fac = (0.25f*M_1_PI_F)*invarea;
+               ls->pdf = invarea;
+
+               if(type == LIGHT_SPOT) {
+                       /* spot light attentuation */
+                       float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2);
+                       ls->eval_fac *= spot_light_attenuation(data1, data2, ls);
+
+                       if(ls->eval_fac == 0.0f)
+                               return false;
+               }
+       }
+       else if(type == LIGHT_AREA) {
+               /* area light */
+               float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2);
+               float4 data3 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 3);
+
+               float invarea = data2.x;
+               if(invarea == 0.0f)
+                       return false;
+
+               float3 axisu = make_float3(data1.y, data1.z, data1.w);
+               float3 axisv = make_float3(data2.y, data2.z, data2.w);
+               float3 Ng = make_float3(data3.y, data3.z, data3.w);
+
+               /* one sided */
+               if(dot(D, Ng) >= 0.0f)
+                       return false;
+
+               ls->P = make_float3(data0.y, data0.z, data0.w);
+
+               if(!ray_quad_intersect(P, D, t,
+                       ls->P, axisu, axisv, &ls->P, &ls->t))
+                       return false;
+
+               ls->D = D;
+               ls->Ng = Ng;
+               ls->pdf = invarea;
+               ls->eval_fac = 0.25f*ls->pdf;
+       }
+       else
+               return false;
+
+       /* compute pdf */
+       if(ls->t != FLT_MAX)
+               ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
+       ls->eval_fac *= kernel_data.integrator.inv_pdf_lights;
+
+       return true;
 }
 
 /* Triangle Light */
 
-__device void triangle_light_sample(KernelGlobals *kg, int prim, int object,
-       float randu, float randv, float time, LightSample *ls)
+__device void object_transform_light_sample(KernelGlobals *kg, LightSample *ls, int object, float time)
 {
-       /* triangle, so get position, normal, shader */
-       ls->P = triangle_sample_MT(kg, prim, randu, randv);
-       ls->Ng = triangle_normal_MT(kg, prim, &ls->shader);
-       ls->object = object;
-       ls->prim = prim;
-       ls->t = 0.0f;
-       ls->type = LIGHT_AREA;
-       ls->eval_fac = 1.0f;
-
 #ifdef __INSTANCING__
        /* instance transform */
-       if(ls->object >= 0) {
+       if(object >= 0) {
 #ifdef __OBJECT_MOTION__
                Transform itfm;
                Transform tfm = object_fetch_transform_motion_test(kg, object, time, &itfm);
 #else
-               Transform tfm = object_fetch_transform(kg, ls->object, OBJECT_TRANSFORM);
-               Transform itfm = object_fetch_transform(kg, ls->object, OBJECT_INVERSE_TRANSFORM);
+               Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
+               Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
 #endif
 
                ls->P = transform_point(&tfm, ls->P);
-               ls->Ng = normalize(transform_direction_transposed(&itfm, ls->Ng));
+               ls->Ng = normalize(transform_direction(&tfm, ls->Ng));
        }
 #endif
 }
 
+__device void triangle_light_sample(KernelGlobals *kg, int prim, int object,
+       float randu, float randv, float time, LightSample *ls)
+{
+       /* triangle, so get position, normal, shader */
+       ls->P = triangle_sample_MT(kg, prim, randu, randv);
+       ls->Ng = triangle_normal_MT(kg, prim, &ls->shader);
+       ls->object = object;
+       ls->prim = prim;
+       ls->lamp = ~0;
+       ls->use_mis = true;
+       ls->t = 0.0f;
+       ls->type = LIGHT_AREA;
+       ls->eval_fac = 1.0f;
+
+       object_transform_light_sample(kg, ls, object, time);
+}
+
 __device float triangle_light_pdf(KernelGlobals *kg,
        const float3 Ng, const float3 I, float t)
 {
+       float pdf = kernel_data.integrator.pdf_triangles;
        float cos_pi = fabsf(dot(Ng, I));
 
        if(cos_pi == 0.0f)
                return 0.0f;
        
-       return (t*t*kernel_data.integrator.pdf_triangles)/cos_pi;
+       return t*t*pdf/cos_pi;
 }
 
+/* Curve Light */
+
 #ifdef __HAIR__
-/* Strand Light */
 
 __device void curve_segment_light_sample(KernelGlobals *kg, int prim, int object,
        int segment, float randu, float randv, float time, LightSample *ls)
@@ -358,27 +524,16 @@ __device void curve_segment_light_sample(KernelGlobals *kg, int prim, int object
        ls->P = randu * l * tg + (gd * l + r1) * ls->Ng;
        ls->object = object;
        ls->prim = prim;
+       ls->lamp = ~0;
+       ls->use_mis = true;
        ls->t = 0.0f;
        ls->type = LIGHT_STRAND;
        ls->eval_fac = 1.0f;
        ls->shader = __float_as_int(v00.z);
 
-#ifdef __INSTANCING__
-       /* instance transform */
-       if(ls->object >= 0) {
-#ifdef __OBJECT_MOTION__
-               Transform itfm;
-               Transform tfm = object_fetch_transform_motion_test(kg, object, time, &itfm);
-#else
-               Transform tfm = object_fetch_transform(kg, ls->object, OBJECT_TRANSFORM);
-               Transform itfm = object_fetch_transform(kg, ls->object, OBJECT_INVERSE_TRANSFORM);
-#endif
-
-               ls->P = transform_point(&tfm, ls->P);
-               ls->Ng = normalize(transform_direction(&tfm, ls->Ng));
-       }
-#endif
+       object_transform_light_sample(kg, ls, object, time);
 }
+
 #endif
 
 /* Light Distribution */
@@ -412,7 +567,7 @@ __device int light_distribution_sample(KernelGlobals *kg, float randt)
 
 /* Generic Light */
 
-__device void light_sample(KernelGlobals *kg, float randt, float randu, float randv, float time, float3 P, LightSample *ls, float *pdf)
+__device void light_sample(KernelGlobals *kg, float randt, float randu, float randv, float time, float3 P, LightSample *ls)
 {
        /* sample index */
        int index = light_distribution_sample(kg, randt);
@@ -420,12 +575,12 @@ __device void light_sample(KernelGlobals *kg, float randt, float randu, float ra
        /* fetch light data */
        float4 l = kernel_tex_fetch(__light_distribution, index);
        int prim = __float_as_int(l.y);
-#ifdef __HAIR__
-       int segment = __float_as_int(l.z);
-#endif
 
        if(prim >= 0) {
                int object = __float_as_int(l.w);
+#ifdef __HAIR__
+               int segment = __float_as_int(l.z);
+#endif
 
 #ifdef __HAIR__
                if (segment != ~0)
@@ -433,27 +588,15 @@ __device void light_sample(KernelGlobals *kg, float randt, float randu, float ra
                else
 #endif
                        triangle_light_sample(kg, prim, object, randu, randv, time, ls);
+
+               /* compute incoming direction, distance and pdf */
+               ls->D = normalize_len(ls->P - P, &ls->t);
+               ls->pdf = triangle_light_pdf(kg, ls->Ng, -ls->D, ls->t);
        }
        else {
-               int point = -prim-1;
-               regular_light_sample(kg, point, randu, randv, P, ls, pdf);
+               int lamp = -prim-1;
+               lamp_light_sample(kg, lamp, randu, randv, P, ls);
        }
-
-       /* compute incoming direction and distance */
-       if(ls->t != FLT_MAX)
-               ls->D = normalize_len(ls->P - P, &ls->t);
-}
-
-__device float light_sample_pdf(KernelGlobals *kg, LightSample *ls, float3 I, float t)
-{
-       float pdf;
-
-       if(ls->prim != ~0)
-               pdf = triangle_light_pdf(kg, ls->Ng, I, t);
-       else
-               pdf = regular_light_pdf(kg, ls->Ng, I, t);
-       
-       return pdf;
 }
 
 __device int light_select_num_samples(KernelGlobals *kg, int index)
@@ -462,18 +605,26 @@ __device int light_select_num_samples(KernelGlobals *kg, int index)
        return __float_as_int(data3.x);
 }
 
-__device void light_select(KernelGlobals *kg, int index, float randu, float randv, float3 P, LightSample *ls, float *pdf)
+__device void light_select(KernelGlobals *kg, int index, float randu, float randv, float3 P, LightSample *ls)
 {
-       regular_light_sample(kg, index, randu, randv, P, ls, pdf);
-
-       /* compute incoming direction and distance */
-       if(ls->t != FLT_MAX)
-               ls->D = normalize_len(ls->P - P, &ls->t);
+       lamp_light_sample(kg, index, randu, randv, P, ls);
 }
 
-__device float light_select_pdf(KernelGlobals *kg, LightSample *ls, float3 I, float t)
+__device int lamp_light_eval_sample(KernelGlobals *kg, float randt)
 {
-       return regular_light_pdf(kg, ls->Ng, I, t);
+       /* sample index */
+       int index = light_distribution_sample(kg, randt);
+
+       /* fetch light data */
+       float4 l = kernel_tex_fetch(__light_distribution, index);
+       int prim = __float_as_int(l.y);
+
+       if(prim < 0) {
+               int lamp = -prim-1;
+               return lamp;
+       }
+       else
+               return ~0;
 }
 
 CCL_NAMESPACE_END
index 20feaf50a2ec137e550040404093e5d3af25d1c2..87a10e8bba7557edfb5e3e545ffb0b667910c9cc 100644 (file)
@@ -238,6 +238,7 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
 
        float min_ray_pdf = FLT_MAX;
        float ray_pdf = 0.0f;
+       float ray_t = 0.0f;
        PathState state;
        int rng_offset = PRNG_BASE_NUM;
 
@@ -248,8 +249,29 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
                /* intersect scene */
                Intersection isect;
                uint visibility = path_state_ray_visibility(kg, &state);
+               bool hit = scene_intersect(kg, &ray, visibility, &isect);
 
-               if(!scene_intersect(kg, &ray, visibility, &isect)) {
+#ifdef __LAMP_MIS__
+               if(kernel_data.integrator.pdf_lights > 0.0f && !(state.flag & PATH_RAY_CAMERA)) {
+                       /* ray starting from previous non-transparent bounce */
+                       Ray light_ray;
+
+                       light_ray.P = ray.P - ray_t*ray.D;
+                       ray_t += isect.t;
+                       light_ray.D = ray.D;
+                       light_ray.t = ray_t;
+                       light_ray.time = ray.time;
+
+                       /* intersect with lamp */
+                       float light_t = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT);
+                       float3 emission;
+
+                       if(indirect_lamp_emission(kg, &light_ray, state.flag, ray_pdf, light_t, &emission))
+                               path_radiance_accum_emission(&L, throughput, emission, state.bounce);
+               }
+#endif
+
+               if(!hit) {
                        /* eval background shader if nothing hit */
                        if(kernel_data.background.transparent && (state.flag & PATH_RAY_CAMERA)) {
                                L_transparent += average(throughput);
@@ -313,7 +335,8 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
 #ifdef __EMISSION__
                /* emission */
                if(sd.flag & SD_EMISSION) {
-                       float3 emission = indirect_emission(kg, &sd, isect.t, state.flag, ray_pdf);
+                       /* todo: is isect.t wrong here for transparent surfaces? */
+                       float3 emission = indirect_primitive_emission(kg, &sd, isect.t, state.flag, ray_pdf);
                        path_radiance_accum_emission(&L, throughput, emission, state.bounce);
                }
 #endif
@@ -374,18 +397,19 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
 
                                Ray light_ray;
                                BsdfEval L_light;
-                               bool is_lamp;
+                               int lamp;
 
 #ifdef __OBJECT_MOTION__
                                light_ray.time = sd.time;
 #endif
 
-                               if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
+                               if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &lamp)) {
                                        /* trace shadow ray */
                                        float3 shadow;
 
                                        if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
                                                /* accumulate */
+                                               bool is_lamp = (lamp != ~0);
                                                path_radiance_accum_light(&L, throughput, &L_light, shadow, state.bounce, is_lamp);
                                        }
                                }
@@ -422,6 +446,7 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
                /* set labels */
                if(!(label & LABEL_TRANSPARENT)) {
                        ray_pdf = bsdf_pdf;
+                       ray_t = 0.0f;
                        min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
                }
 
@@ -459,13 +484,36 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
 __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray ray, __global float *buffer,
        float3 throughput, float min_ray_pdf, float ray_pdf, PathState state, int rng_offset, PathRadiance *L)
 {
+       float ray_t = 0.0f;
+
        /* path iteration */
        for(;; rng_offset += PRNG_BOUNCE_NUM) {
                /* intersect scene */
                Intersection isect;
                uint visibility = path_state_ray_visibility(kg, &state);
+               bool hit = scene_intersect(kg, &ray, visibility, &isect);
 
-               if(!scene_intersect(kg, &ray, visibility, &isect)) {
+#ifdef __LAMP_MIS__
+               if(kernel_data.integrator.pdf_lights > 0.0f && !(state.flag & PATH_RAY_CAMERA)) {
+                       /* ray starting from previous non-transparent bounce */
+                       Ray light_ray;
+
+                       light_ray.P = ray.P - ray_t*ray.D;
+                       ray_t += isect.t;
+                       light_ray.D = ray.D;
+                       light_ray.t = ray_t;
+                       light_ray.time = ray.time;
+
+                       /* intersect with lamp */
+                       float light_t = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT);
+                       float3 emission;
+
+                       if(indirect_lamp_emission(kg, &light_ray, state.flag, ray_pdf, light_t, &emission))
+                               path_radiance_accum_emission(L, throughput, emission, state.bounce);
+               }
+#endif
+
+               if(!hit) {
 #ifdef __BACKGROUND__
                        /* sample background shader */
                        float3 L_background = indirect_background(kg, &ray, state.flag, ray_pdf);
@@ -496,7 +544,7 @@ __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray
 #ifdef __EMISSION__
                /* emission */
                if(sd.flag & SD_EMISSION) {
-                       float3 emission = indirect_emission(kg, &sd, isect.t, state.flag, ray_pdf);
+                       float3 emission = indirect_primitive_emission(kg, &sd, isect.t, state.flag, ray_pdf);
                        path_radiance_accum_emission(L, throughput, emission, state.bounce);
                }
 #endif
@@ -557,19 +605,20 @@ __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray
 
                                Ray light_ray;
                                BsdfEval L_light;
-                               bool is_lamp;
+                               int lamp;
 
 #ifdef __OBJECT_MOTION__
                                light_ray.time = sd.time;
 #endif
 
                                /* sample random light */
-                               if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
+                               if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &lamp)) {
                                        /* trace shadow ray */
                                        float3 shadow;
 
                                        if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
                                                /* accumulate */
+                                               bool is_lamp = (lamp != ~0);
                                                path_radiance_accum_light(L, throughput, &L_light, shadow, state.bounce, is_lamp);
                                        }
                                }
@@ -606,6 +655,7 @@ __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray
                /* set labels */
                if(!(label & LABEL_TRANSPARENT)) {
                        ray_pdf = bsdf_pdf;
+                       ray_t = 0.0f;
                        min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
                }
 
@@ -697,7 +747,7 @@ __device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sam
 #ifdef __EMISSION__
                /* emission */
                if(sd.flag & SD_EMISSION) {
-                       float3 emission = indirect_emission(kg, &sd, isect.t, state.flag, ray_pdf);
+                       float3 emission = indirect_primitive_emission(kg, &sd, isect.t, state.flag, ray_pdf);
                        path_radiance_accum_emission(&L, throughput, emission, state.bounce);
                }
 #endif
@@ -760,7 +810,7 @@ __device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sam
                if(sd.flag & SD_BSDF_HAS_EVAL) {
                        Ray light_ray;
                        BsdfEval L_light;
-                       bool is_lamp;
+                       int lamp;
 
 #ifdef __OBJECT_MOTION__
                        light_ray.time = sd.time;
@@ -778,12 +828,13 @@ __device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sam
                                        float light_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_U);
                                        float light_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_V);
 
-                                       if(direct_emission(kg, &sd, i, 0.0f, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
+                                       if(direct_emission(kg, &sd, i, 0.0f, 0.0f, light_u, light_v, &light_ray, &L_light, &lamp)) {
                                                /* trace shadow ray */
                                                float3 shadow;
 
                                                if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
                                                        /* accumulate */
+                                                       bool is_lamp = (lamp != ~0);
                                                        path_radiance_accum_light(&L, throughput*num_samples_inv, &L_light, shadow, state.bounce, is_lamp);
                                                }
                                        }
@@ -807,12 +858,13 @@ __device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sam
                                        if(kernel_data.integrator.num_all_lights)
                                                light_t = 0.5f*light_t;
 
-                                       if(direct_emission(kg, &sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
+                                       if(direct_emission(kg, &sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &lamp)) {
                                                /* trace shadow ray */
                                                float3 shadow;
 
                                                if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
                                                        /* accumulate */
+                                                       bool is_lamp = (lamp != ~0);
                                                        path_radiance_accum_light(&L, throughput*num_samples_inv, &L_light, shadow, state.bounce, is_lamp);
                                                }
                                        }
@@ -885,7 +937,7 @@ __device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sam
                                bsdf_ray.time = sd.time;
 #endif
 
-                               kernel_path_indirect(kg, rng, sample*num_samples, bsdf_ray, buffer,
+                               kernel_path_indirect(kg, rng, sample*num_samples + j, bsdf_ray, buffer,
                                        tp*num_samples_inv, min_ray_pdf, bsdf_pdf, ps, rng_offset+PRNG_BOUNCE_NUM, &L);
                        }
                }
index 2bd6b5859f371aaf83e0d82956a52d1f70a13889..102be440978d4e0f71cd85ad420d364814ce264d 100644 (file)
@@ -48,6 +48,7 @@ CCL_NAMESPACE_BEGIN
 #endif
 #define __NON_PROGRESSIVE__
 #define __HAIR__
+#define __LAMP_MIS__
 #endif
 
 #ifdef __KERNEL_CUDA__
@@ -384,7 +385,7 @@ typedef enum AttributeStandard {
 
 /* Closure data */
 
-#define MAX_CLOSURE 8
+#define MAX_CLOSURE 16
 
 typedef struct ShaderClosure {
        ClosureType type;
@@ -636,6 +637,7 @@ typedef struct KernelIntegrator {
        int num_all_lights;
        float pdf_triangles;
        float pdf_lights;
+       float inv_pdf_lights;
        int pdf_background_res;
 
        /* bounces */
@@ -671,7 +673,7 @@ typedef struct KernelIntegrator {
        int transmission_samples;
        int ao_samples;
        int mesh_light_samples;
-       int pad1, pad2;
+       int pad1;
 } KernelIntegrator;
 
 typedef struct KernelBVH {
index 28742d56e3b8da09c372eae4f88be415c38fbbc2..92a023bd7659fa9349ffb9ca024a8642aa2675a7 100644 (file)
@@ -605,7 +605,11 @@ bool OSLRenderServices::get_object_standard_attribute(KernelGlobals *kg, ShaderD
                return set_attribute_int(3, type, derivatives, val);
        }
        else if ((name == u_geom_trianglevertices || name == u_geom_polyvertices)
+#ifdef __HAIR__
                     && sd->segment == ~0) {
+#else
+               ) {
+#endif
                float3 P[3];
                triangle_vertices(kg, sd->prim, P);
 
@@ -675,7 +679,11 @@ bool OSLRenderServices::get_attribute(void *renderstate, bool derivatives, ustri
        else {
                object = sd->object;
                prim = sd->prim;
+#ifdef __HAIR__
                segment = sd->segment;
+#else
+               segment = ~0;
+#endif
 
                if (object == ~0)
                        return get_background_attribute(kg, sd, name, type, derivatives, val);
index 59e307bb40898c96b27914f0b5f0be43a3c34fd9..a32c526a2bee03e3dd47c8d6b017abbe1ad1daa0 100644 (file)
@@ -457,7 +457,11 @@ float3 OSLShader::volume_eval_phase(const ShaderClosure *sc, const float3 omega_
 int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeElement *elem)
 {
        /* for OSL, a hash map is used to lookup the attribute by name. */
-       int object = sd->object*ATTR_PRIM_TYPES + (sd->segment != ~0);
+       int object = sd->object*ATTR_PRIM_TYPES;
+#ifdef __HAIR__
+       if(sd->segment != ~0) object += ATTR_PRIM_CURVE;
+#endif
+
        OSLGlobals::AttributeMap &attr_map = kg->osl->attribute_map[object];
        ustring stdname(std::string("geom:") + std::string(Attribute::standard_name((AttributeStandard)id)));
        OSLGlobals::AttributeMap::const_iterator it = attr_map.find(stdname);
index c8e3e94ec983ad4f112c4691e5da5ae59594e863..1b94d603a262da68b32ee8ca13f8853d25ffc1a1 100644 (file)
@@ -323,6 +323,7 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
                /* precompute pdfs */
                kintegrator->pdf_triangles = 0.0f;
                kintegrator->pdf_lights = 0.0f;
+               kintegrator->inv_pdf_lights = 0.0f;
 
                /* sample one, with 0.5 probability of light or triangle */
                kintegrator->num_all_lights = num_lights;
@@ -337,6 +338,8 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
                        kintegrator->pdf_lights = 1.0f/num_lights;
                        if(trianglearea > 0.0f)
                                kintegrator->pdf_lights *= 0.5f;
+
+                       kintegrator->inv_pdf_lights = 1.0f/kintegrator->pdf_lights;
                }
 
                /* CDF */
@@ -349,6 +352,7 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
                kintegrator->num_all_lights = 0;
                kintegrator->pdf_triangles = 0.0f;
                kintegrator->pdf_lights = 0.0f;
+               kintegrator->inv_pdf_lights = 0.0f;
        }
 }
 
@@ -475,16 +479,25 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
                if(light->type == LIGHT_POINT) {
                        shader_id &= ~SHADER_AREA_LIGHT;
 
+                       float radius = light->size;
+                       float invarea = (radius > 0.0f)? 1.0f/(M_PI_F*radius*radius): 1.0f;
+
                        light_data[i*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), light->size, 0.0f, 0.0f);
+                       light_data[i*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[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
                }
                else if(light->type == LIGHT_DISTANT) {
                        shader_id &= ~SHADER_AREA_LIGHT;
 
+                       float radius = light->size;
+                       float angle = atanf(radius);
+                       float cosangle = cosf(angle);
+                       float area = M_PI_F*radius*radius;
+                       float invarea = (area > 0.0f)? 1.0f/area: 1.0f;
+
                        light_data[i*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), light->size, 0.0f, 0.0f);
+                       light_data[i*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[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
                }
@@ -499,21 +512,25 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
                else if(light->type == LIGHT_AREA) {
                        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: 0.0f;
 
                        light_data[i*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[i*LIGHT_SIZE + 2] = make_float4(0.0f, axisv.x, axisv.y, axisv.z);
+                       light_data[i*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);
                }
                else if(light->type == LIGHT_SPOT) {
                        shader_id &= ~SHADER_AREA_LIGHT;
 
+                       float radius = light->size;
+                       float invarea = (radius > 0.0f)? 1.0f/(M_PI_F*radius*radius): 1.0f;
                        float spot_angle = cosf(light->spot_angle*0.5f);
                        float spot_smooth = (1.0f - spot_angle)*light->spot_smooth;
 
                        light_data[i*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), light->size, dir.x, dir.y);
-                       light_data[i*LIGHT_SIZE + 2] = make_float4(dir.z, spot_angle, spot_smooth, 0.0f);
+                       light_data[i*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[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
                }
        }
index 8932c85db07c5c6c8f51fbcc26a4d0e0e9420745..c37fa1a4dc634d6b0e7daca4e9114c81391bb3d3 100644 (file)
@@ -1161,6 +1161,130 @@ __device float safe_divide(float a, float b)
        return result;
 }
 
+/* Ray Intersection */
+
+__device bool ray_sphere_intersect(
+       float3 ray_P, float3 ray_D, float ray_t,
+       float3 sphere_P, float sphere_radius,
+       float3 *isect_P, float *isect_t)
+{
+       float3 d = sphere_P - ray_P;
+       float radiussq = sphere_radius*sphere_radius;
+       float tsq = dot(d, d);
+
+       if(tsq > radiussq) { /* ray origin outside sphere */
+               float tp = dot(d, ray_D);
+
+               if(tp < 0.0f) /* dir points away from sphere */
+                       return false;
+
+               float dsq = tsq - tp*tp; /* pythagoras */
+
+               if(dsq > radiussq) /* closest point on ray outside sphere */
+                       return false;
+
+               float t = tp - sqrtf(radiussq - dsq); /* pythagoras */
+
+               if(t < ray_t) {
+                       *isect_t = t;
+                       *isect_P = ray_P + ray_D*t;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+__device bool ray_aligned_disk_intersect(
+       float3 ray_P, float3 ray_D, float ray_t,
+       float3 disk_P, float disk_radius,
+       float3 *isect_P, float *isect_t)
+{
+       /* aligned disk normal */
+       float disk_t;
+       float3 disk_N = normalize_len(ray_P - disk_P, &disk_t);
+       float div = dot(ray_D, disk_N);
+
+       if(div == 0.0f)
+               return false;
+
+       /* compute t to intersection point */
+       float t = -disk_t/div;
+       if(t < 0.0f || t > ray_t)
+               return false;
+       
+       /* test if within radius */
+       float3 P = ray_P + ray_D*t;
+       if(len_squared(P - disk_P) > disk_radius*disk_radius)
+               return false;
+
+       *isect_P = P;
+       *isect_t = t;
+
+       return true;
+}
+
+__device bool ray_triangle_intersect(
+       float3 ray_P, float3 ray_D, float ray_t,
+       float3 v0, float3 v1, float3 v2,
+       float3 *isect_P, float *isect_t)
+{
+       /* Calculate intersection */
+       float3 e1 = v1 - v0;
+       float3 e2 = v2 - v0;
+       float3 s1 = cross(ray_D, e2);
+
+       const float divisor = dot(s1, e1);
+       if(divisor == 0.0f)
+               return false;
+
+       const float invdivisor = 1.0f/divisor;
+
+       /* compute first barycentric coordinate */
+       const float3 d = ray_P - v0;
+       const float u = dot(d, s1)*invdivisor;
+       if(u < 0.0f)
+               return false;
+
+       /* Compute second barycentric coordinate */
+       const float3 s2 = cross(d, e1);
+       const float v = dot(ray_D, s2)*invdivisor;
+       if(v < 0.0f)
+               return false;
+
+       const float b0 = 1.0f - u - v;
+       if(b0 < 0.0f)
+               return false;
+
+       /* compute t to intersection point */
+       const float t = dot(e2, s2)*invdivisor;
+       if(t < 0.0f || t > ray_t)
+               return false;
+
+       *isect_t = t;
+       *isect_P = ray_P + ray_D*t;
+
+       return true;
+}
+
+__device bool ray_quad_intersect(
+       float3 ray_P, float3 ray_D, float ray_t,
+       float3 quad_P, float3 quad_u, float3 quad_v,
+       float3 *isect_P, float *isect_t)
+{
+       float3 v0 = quad_P - quad_u*0.5f - quad_v*0.5f;
+       float3 v1 = quad_P + quad_u*0.5f - quad_v*0.5f;
+       float3 v2 = quad_P + quad_u*0.5f + quad_v*0.5f;
+       float3 v3 = quad_P - quad_u*0.5f + quad_v*0.5f;
+
+       if(ray_triangle_intersect(ray_P, ray_D, ray_t, v0, v1, v2, isect_P, isect_t))
+               return true;
+       else if(ray_triangle_intersect(ray_P, ray_D, ray_t, v0, v2, v3, isect_P, isect_t))
+               return true;
+       
+       return false;
+}
+
 CCL_NAMESPACE_END
 
 #endif /* __UTIL_MATH_H__ */