diff options
Diffstat (limited to 'intern/cycles/kernel/kernel_light.h')
-rw-r--r-- | intern/cycles/kernel/kernel_light.h | 459 |
1 files changed, 338 insertions, 121 deletions
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index a2909cec1a1..efab69ee37d 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -102,7 +102,7 @@ ccl_device_inline float area_light_sample(float3 P, float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f); cu = clamp(cu, -1.0f, 1.0f); /* Compute xu. */ - float xu = -(cu * z0) / sqrtf(1.0f - cu * cu); + float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f); xu = clamp(xu, x0, x1); /* Compute yv. */ float z0sq = z0 * z0; @@ -255,11 +255,11 @@ ccl_device_inline bool background_portal_data_fetch_and_check_side(KernelGlobals float3 *lightpos, float3 *dir) { - float4 data0 = kernel_tex_fetch(__light_data, (index + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 0); - float4 data3 = kernel_tex_fetch(__light_data, (index + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 3); + int portal = kernel_data.integrator.portal_offset + index; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); - *lightpos = make_float3(data0.y, data0.z, data0.w); - *dir = make_float3(data3.y, data3.z, data3.w); + *lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]); + *dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); /* Check whether portal is on the right side. */ if(dot(*dir, P - *lightpos) > 1e-4f) @@ -291,11 +291,10 @@ ccl_device_inline float background_portal_pdf(KernelGlobals *kg, } num_possible++; - float4 data1 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 1); - float4 data2 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 2); - - float3 axisu = make_float3(data1.y, data1.z, data1.w); - float3 axisv = make_float3(data2.y, data2.z, data2.w); + int portal = kernel_data.integrator.portal_offset + p; + 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]); if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL, NULL, NULL)) continue; @@ -346,10 +345,10 @@ ccl_device float3 background_portal_sample(KernelGlobals *kg, if(portal == 0) { /* p is the portal to be sampled. */ - float4 data1 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 1); - float4 data2 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 2); - float3 axisu = make_float3(data1.y, data1.z, data1.w); - float3 axisv = make_float3(data2.y, data2.z, data2.w); + int portal = kernel_data.integrator.portal_offset + p; + 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, @@ -396,11 +395,13 @@ ccl_device_inline float3 background_light_sample(KernelGlobals *kg, + (1.0f - portal_sampling_pdf) * cdf_pdf); } return D; - } else { + } + else { /* Sample map, but with nonzero portal_sampling_pdf for MIS. */ randu = (randu - portal_sampling_pdf) / (1.0f - portal_sampling_pdf); } - } else { + } + else { /* We can't sample a portal. * Check if we can sample the map instead. */ @@ -477,14 +478,10 @@ ccl_device float3 sphere_light_sample(float3 P, float3 center, float radius, flo return disk_light_sample(normalize(P - center), randu, randv)*radius; } -ccl_device float spot_light_attenuation(float4 data1, float4 data2, LightSample *ls) +ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot_smooth, LightSample *ls) { - 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) { @@ -516,12 +513,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, 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); + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + LightType type = (LightType)klight->type; ls->type = type; - ls->shader = __float_as_int(data1.x); + ls->shader = klight->shader_id; ls->object = PRIM_NONE; ls->prim = PRIM_NONE; ls->lamp = lamp; @@ -530,10 +525,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, if(type == LIGHT_DISTANT) { /* distant light */ - float3 lightD = make_float3(data0.y, data0.z, data0.w); + float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); float3 D = lightD; - float radius = data1.y; - float invarea = data1.w; + float radius = klight->distant.radius; + float invarea = klight->distant.invarea; if(radius > 0.0f) D = distant_light_sample(D, radius, randu, randv); @@ -545,7 +540,7 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, float costheta = dot(lightD, D); ls->pdf = invarea/(costheta*costheta*costheta); - ls->eval_fac = ls->pdf*kernel_data.integrator.inv_pdf_lights; + ls->eval_fac = ls->pdf; } #ifdef __BACKGROUND_MIS__ else if(type == LIGHT_BACKGROUND) { @@ -557,14 +552,13 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, ls->D = -D; ls->t = FLT_MAX; ls->eval_fac = 1.0f; - ls->pdf *= kernel_data.integrator.pdf_lights; } #endif else { - ls->P = make_float3(data0.y, data0.z, data0.w); + ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]); if(type == LIGHT_POINT || type == LIGHT_SPOT) { - float radius = data1.y; + float radius = klight->spot.radius; if(radius > 0.0f) /* sphere light */ @@ -573,14 +567,19 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, ls->D = normalize_len(ls->P - P, &ls->t); ls->Ng = -ls->D; - float invarea = data1.z; + float invarea = klight->spot.invarea; ls->eval_fac = (0.25f*M_1_PI_F)*invarea; ls->pdf = invarea; if(type == LIGHT_SPOT) { /* spot light attenuation */ - float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2); - ls->eval_fac *= spot_light_attenuation(data1, data2, ls); + float3 dir = make_float3(klight->spot.dir[0], + klight->spot.dir[1], + klight->spot.dir[2]); + ls->eval_fac *= spot_light_attenuation(dir, + klight->spot.spot_angle, + klight->spot.spot_smooth, + ls); if(ls->eval_fac == 0.0f) { return false; } @@ -593,12 +592,15 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, } else { /* area light */ - 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, data1.w); - float3 axisv = make_float3(data2.y, data2.z, data2.w); - float3 D = make_float3(data3.y, data3.z, data3.w); + 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]); + float3 D = make_float3(klight->area.dir[0], + klight->area.dir[1], + klight->area.dir[2]); if(dot(ls->P - P, D) > 0.0f) { return false; @@ -617,24 +619,22 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, ls->Ng = D; ls->D = normalize_len(ls->P - P, &ls->t); - float invarea = data2.x; + float invarea = klight->area.invarea; ls->eval_fac = 0.25f*invarea; } - - ls->eval_fac *= kernel_data.integrator.inv_pdf_lights; } + ls->pdf *= kernel_data.integrator.pdf_lights; + return (ls->pdf > 0.0f); } ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, 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); + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + LightType type = (LightType)klight->type; ls->type = type; - ls->shader = __float_as_int(data1.x); + ls->shader = klight->shader_id; ls->object = PRIM_NONE; ls->prim = PRIM_NONE; ls->lamp = lamp; @@ -647,7 +647,7 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, if(type == LIGHT_DISTANT) { /* distant light */ - float radius = data1.y; + float radius = klight->distant.radius; if(radius == 0.0f) return false; @@ -669,9 +669,9 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, * P */ - float3 lightD = make_float3(data0.y, data0.z, data0.w); + float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); float costheta = dot(-lightD, D); - float cosangle = data1.z; + float cosangle = klight->distant.cosangle; if(costheta < cosangle) return false; @@ -682,13 +682,14 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, ls->t = FLT_MAX; /* compute pdf */ - float invarea = data1.w; + float invarea = klight->distant.invarea; 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; + float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); + + float radius = klight->spot.radius; /* sphere light */ if(radius == 0.0f) @@ -703,14 +704,19 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, ls->Ng = -D; ls->D = D; - float invarea = data1.z; + float invarea = klight->spot.invarea; ls->eval_fac = (0.25f*M_1_PI_F)*invarea; ls->pdf = invarea; if(type == LIGHT_SPOT) { /* spot light attenuation */ - float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2); - ls->eval_fac *= spot_light_attenuation(data1, data2, ls); + float3 dir = make_float3(klight->spot.dir[0], + klight->spot.dir[1], + klight->spot.dir[2]); + ls->eval_fac *= spot_light_attenuation(dir, + klight->spot.spot_angle, + klight->spot.spot_smooth, + ls); if(ls->eval_fac == 0.0f) return false; @@ -725,22 +731,25 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, } 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; + float invarea = klight->area.invarea; 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); + 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]); + float3 Ng = make_float3(klight->area.dir[0], + klight->area.dir[1], + klight->area.dir[2]); /* one sided */ if(dot(D, Ng) >= 0.0f) return false; - float3 light_P = make_float3(data0.y, data0.z, data0.w); + float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); if(!ray_quad_intersect(P, D, 0.0f, t, light_P, axisu, axisv, Ng, @@ -755,86 +764,292 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, ls->pdf = area_light_sample(P, &light_P, axisu, axisv, 0, 0, false); ls->eval_fac = 0.25f*invarea; } - else + else { return false; + } + + ls->pdf *= kernel_data.integrator.pdf_lights; return true; } /* Triangle Light */ -ccl_device void object_transform_light_sample(KernelGlobals *kg, LightSample *ls, int object, float time) +/* returns true if the triangle is has motion blur or an instancing transform applied */ +ccl_device_inline bool triangle_world_space_vertices(KernelGlobals *kg, int object, int prim, float time, float3 V[3]) { + bool has_motion = false; + const int object_flag = kernel_tex_fetch(__object_flag, object); + + if(object_flag & SD_OBJECT_HAS_VERTEX_MOTION && time >= 0.0f) { + motion_triangle_vertices(kg, object, prim, time, V); + has_motion = true; + } + else { + triangle_vertices(kg, prim, V); + } + #ifdef __INSTANCING__ - /* instance transform */ - if(!(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) { + if(!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { # ifdef __OBJECT_MOTION__ - Transform itfm; - Transform tfm = object_fetch_transform_motion_test(kg, object, time, &itfm); + float object_time = (time >= 0.0f) ? time : 0.5f; + Transform tfm = object_fetch_transform_motion_test(kg, object, object_time, NULL); # else Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); # endif - - ls->P = transform_point(&tfm, ls->P); - ls->Ng = normalize(transform_direction(&tfm, ls->Ng)); + V[0] = transform_point(&tfm, V[0]); + V[1] = transform_point(&tfm, V[1]); + V[2] = transform_point(&tfm, V[2]); + has_motion = true; } #endif + return has_motion; } -ccl_device void triangle_light_sample(KernelGlobals *kg, int prim, int object, - float randu, float randv, float time, LightSample *ls) +ccl_device_inline float triangle_light_pdf_area(KernelGlobals *kg, const float3 Ng, const float3 I, float t) { - float u, v; + float pdf = kernel_data.integrator.pdf_triangles; + float cos_pi = fabsf(dot(Ng, I)); - /* compute random point in triangle */ - randu = sqrtf(randu); + if(cos_pi == 0.0f) + return 0.0f; - u = 1.0f - randu; - v = randv*randu; + return t*t*pdf/cos_pi; +} + +ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *sd, float t) +{ + /* A naive heuristic to decide between costly solid angle sampling + * and simple area sampling, comparing the distance to the triangle plane + * to the length of the edges of the triangle. */ + + float3 V[3]; + bool has_motion = triangle_world_space_vertices(kg, sd->object, sd->prim, sd->time, V); + + const float3 e0 = V[1] - V[0]; + const float3 e1 = V[2] - V[0]; + const float3 e2 = V[2] - V[1]; + const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2))); + const float3 N = cross(e0, e1); + const float distance_to_plane = fabsf(dot(N, sd->I * t))/dot(N, N); + + if(longest_edge_squared > distance_to_plane*distance_to_plane) { + /* sd contains the point on the light source + * calculate Px, the point that we're shading */ + const float3 Px = sd->P + sd->I * t; + const float3 v0_p = V[0] - Px; + const float3 v1_p = V[1] - Px; + const float3 v2_p = V[2] - Px; + + const float3 u01 = safe_normalize(cross(v0_p, v1_p)); + const float3 u02 = safe_normalize(cross(v0_p, v2_p)); + const float3 u12 = safe_normalize(cross(v1_p, v2_p)); + + const float alpha = fast_acosf(dot(u02, u01)); + const float beta = fast_acosf(-dot(u01, u12)); + const float gamma = fast_acosf(dot(u02, u12)); + const float solid_angle = alpha + beta + gamma - M_PI_F; + + /* pdf_triangles is calculated over triangle area, but we're not sampling over its area */ + if(UNLIKELY(solid_angle == 0.0f)) { + return 0.0f; + } + else { + float area = 1.0f; + if(has_motion) { + /* get the center frame vertices, this is what the PDF was calculated from */ + triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V); + area = triangle_area(V[0], V[1], V[2]); + } + else { + area = 0.5f * len(N); + } + const float pdf = area * kernel_data.integrator.pdf_triangles; + return pdf / solid_angle; + } + } + else { + float pdf = triangle_light_pdf_area(kg, sd->Ng, sd->I, t); + if(has_motion) { + const float area = 0.5f * len(N); + if(UNLIKELY(area == 0.0f)) { + return 0.0f; + } + /* scale the PDF. + * area = the area the sample was taken from + * area_pre = the are from which pdf_triangles was calculated from */ + triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V); + const float area_pre = triangle_area(V[0], V[1], V[2]); + pdf = pdf * area_pre / area; + } + return pdf; + } +} - /* triangle, so get position, normal, shader */ - triangle_point_normal(kg, object, prim, u, v, &ls->P, &ls->Ng, &ls->shader); +ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, int prim, int object, + float randu, float randv, float time, LightSample *ls, const float3 P) +{ + /* A naive heuristic to decide between costly solid angle sampling + * and simple area sampling, comparing the distance to the triangle plane + * to the length of the edges of the triangle. */ + + float3 V[3]; + bool has_motion = triangle_world_space_vertices(kg, object, prim, time, V); + + const float3 e0 = V[1] - V[0]; + const float3 e1 = V[2] - V[0]; + const float3 e2 = V[2] - V[1]; + const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2))); + const float3 N0 = cross(e0, e1); + float Nl = 0.0f; + ls->Ng = safe_normalize_len(N0, &Nl); + float area = 0.5f * Nl; + + /* flip normal if necessary */ + const int object_flag = kernel_tex_fetch(__object_flag, object); + if(object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { + ls->Ng = -ls->Ng; + } + ls->eval_fac = 1.0f; + ls->shader = kernel_tex_fetch(__tri_shader, prim); ls->object = object; ls->prim = prim; ls->lamp = LAMP_NONE; ls->shader |= SHADER_USE_MIS; - ls->t = 0.0f; - ls->u = u; - ls->v = v; ls->type = LIGHT_TRIANGLE; - ls->eval_fac = 1.0f; - object_transform_light_sample(kg, ls, object, time); -} + float distance_to_plane = fabsf(dot(N0, V[0] - P)/dot(N0, N0)); + + if(longest_edge_squared > distance_to_plane*distance_to_plane) { + /* see James Arvo, "Stratified Sampling of Spherical Triangles" + * http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */ + + /* project the triangle to the unit sphere + * and calculate its edges and angles */ + const float3 v0_p = V[0] - P; + const float3 v1_p = V[1] - P; + const float3 v2_p = V[2] - P; + + const float3 u01 = safe_normalize(cross(v0_p, v1_p)); + const float3 u02 = safe_normalize(cross(v0_p, v2_p)); + const float3 u12 = safe_normalize(cross(v1_p, v2_p)); + + const float3 A = safe_normalize(v0_p); + const float3 B = safe_normalize(v1_p); + const float3 C = safe_normalize(v2_p); + + const float cos_alpha = dot(u02, u01); + const float cos_beta = -dot(u01, u12); + const float cos_gamma = dot(u02, u12); + + /* calculate dihedral angles */ + const float alpha = fast_acosf(cos_alpha); + const float beta = fast_acosf(cos_beta); + const float gamma = fast_acosf(cos_gamma); + /* the area of the unit spherical triangle = solid angle */ + const float solid_angle = alpha + beta + gamma - M_PI_F; + + /* precompute a few things + * these could be re-used to take several samples + * as they are independent of randu/randv */ + const float cos_c = dot(A, B); + const float sin_alpha = fast_sinf(alpha); + const float product = sin_alpha * cos_c; + + /* Select a random sub-area of the spherical triangle + * and calculate the third vertex C_ of that new triangle */ + const float phi = randu * solid_angle - alpha; + float s, t; + fast_sincosf(phi, &s, &t); + const float u = t - cos_alpha; + const float v = s + product; + + const float3 U = safe_normalize(C - dot(C, A) * A); + + float q = 1.0f; + const float det = ((v * s + u * t) * sin_alpha); + if(det != 0.0f) { + q = ((v * t - u * s) * cos_alpha - v) / det; + } + const float temp = max(1.0f - q*q, 0.0f); -ccl_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)); + const float3 C_ = safe_normalize(q * A + sqrtf(temp) * U); - if(cos_pi == 0.0f) - return 0.0f; - - return t*t*pdf/cos_pi; + /* Finally, select a random point along the edge of the new triangle + * That point on the spherical triangle is the sampled ray direction */ + const float z = 1.0f - randv * (1.0f - dot(C_, B)); + ls->D = z * B + safe_sqrtf(1.0f - z*z) * safe_normalize(C_ - dot(C_, B) * B); + + /* calculate intersection with the planar triangle */ + if(!ray_triangle_intersect(P, ls->D, FLT_MAX, +#if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) + (ssef*)V, +#else + V[0], V[1], V[2], +#endif + &ls->u, &ls->v, &ls->t)) { + ls->pdf = 0.0f; + return; + } + + ls->P = P + ls->D * ls->t; + + /* pdf_triangles is calculated over triangle area, but we're sampling over solid angle */ + if(UNLIKELY(solid_angle == 0.0f)) { + ls->pdf = 0.0f; + return; + } + else { + if(has_motion) { + /* get the center frame vertices, this is what the PDF was calculated from */ + triangle_world_space_vertices(kg, object, prim, -1.0f, V); + area = triangle_area(V[0], V[1], V[2]); + } + const float pdf = area * kernel_data.integrator.pdf_triangles; + ls->pdf = pdf / solid_angle; + } + } + else { + /* compute random point in triangle */ + randu = sqrtf(randu); + + const float u = 1.0f - randu; + const float v = randv*randu; + const float t = 1.0f - u - v; + ls->P = u * V[0] + v * V[1] + t * V[2]; + /* compute incoming direction, distance and pdf */ + ls->D = normalize_len(ls->P - P, &ls->t); + ls->pdf = triangle_light_pdf_area(kg, ls->Ng, -ls->D, ls->t); + if(has_motion && area != 0.0f) { + /* scale the PDF. + * area = the area the sample was taken from + * area_pre = the are from which pdf_triangles was calculated from */ + triangle_world_space_vertices(kg, object, prim, -1.0f, V); + const float area_pre = triangle_area(V[0], V[1], V[2]); + ls->pdf = ls->pdf * area_pre / area; + } + ls->u = u; + ls->v = v; + } } /* Light Distribution */ -ccl_device int light_distribution_sample(KernelGlobals *kg, float randt) +ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) { - /* this is basically std::upper_bound as used by pbrt, to find a point light or + /* This is basically std::upper_bound as used by pbrt, to find a point light or * triangle to emit from, proportional to area. a good improvement would be to * also sample proportional to power, though it's not so well defined with - * OSL shaders. */ + * arbitrary shaders. */ int first = 0; int len = kernel_data.integrator.num_distribution + 1; + float r = *randu; while(len > 0) { int half_len = len >> 1; int middle = first + half_len; - if(randt < kernel_tex_fetch(__light_distribution, middle).x) { + if(r < kernel_tex_fetch(__light_distribution, middle).totarea) { len = half_len; } else { @@ -843,21 +1058,27 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float randt) } } - /* clamping should not be needed but float rounding errors seem to - * make this fail on rare occasions */ - return clamp(first-1, 0, kernel_data.integrator.num_distribution-1); + /* Clamping should not be needed but float rounding errors seem to + * make this fail on rare occasions. */ + int index = clamp(first-1, 0, kernel_data.integrator.num_distribution-1); + + /* Rescale to reuse random number. this helps the 2D samples within + * each area light be stratified as well. */ + float distr_min = kernel_tex_fetch(__light_distribution, index).totarea; + float distr_max = kernel_tex_fetch(__light_distribution, index+1).totarea; + *randu = (r - distr_min)/(distr_max - distr_min); + + return index; } /* Generic Light */ ccl_device bool light_select_reached_max_bounces(KernelGlobals *kg, int index, int bounce) { - float4 data4 = kernel_tex_fetch(__light_data, index*LIGHT_SIZE + 4); - return (bounce > __float_as_int(data4.x)); + return (bounce > kernel_tex_fetch(__lights, index).max_bounces); } ccl_device_noinline bool light_sample(KernelGlobals *kg, - float randt, float randu, float randv, float time, @@ -866,20 +1087,17 @@ ccl_device_noinline bool light_sample(KernelGlobals *kg, LightSample *ls) { /* sample index */ - int index = light_distribution_sample(kg, randt); + int index = light_distribution_sample(kg, &randu); /* fetch light data */ - float4 l = kernel_tex_fetch(__light_distribution, index); - int prim = __float_as_int(l.y); + const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch(__light_distribution, index); + int prim = kdistribution->prim; if(prim >= 0) { - int object = __float_as_int(l.w); - int shader_flag = __float_as_int(l.z); + int object = kdistribution->mesh_light.object_id; + int shader_flag = kdistribution->mesh_light.shader_flag; - 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); + triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); ls->shader |= shader_flag; return (ls->pdf > 0.0f); } @@ -896,8 +1114,7 @@ ccl_device_noinline bool light_sample(KernelGlobals *kg, ccl_device int light_select_num_samples(KernelGlobals *kg, int index) { - float4 data3 = kernel_tex_fetch(__light_data, index*LIGHT_SIZE + 3); - return __float_as_int(data3.x); + return kernel_tex_fetch(__lights, index).samples; } CCL_NAMESPACE_END |