Cycles: Implement texture coordinates for Point, Spot and Area Lamps

When using the Normal output of the Texture Coordinate node on Point and Spot lamps, the coordinates now depend on the rotation of the lamp.
On Area lamps, the Parametric output of the Geometry node now returns UV coordinates on the area lamp.

Credit for the Area lamp part goes to Stefan Werner (from D1995).
This commit is contained in:
Lukas Stockner 2016-10-29 18:54:42 +02:00 committed by Sergey Sharybin
parent 468022403a
commit 5306621998
12 changed files with 106 additions and 19 deletions

View File

@ -153,6 +153,7 @@ void BlenderSync::sync_light(BL::Object& b_parent,
/* location and (inverted!) direction */
light->co = transform_get_column(&tfm, 3);
light->dir = -transform_get_column(&tfm, 2);
light->tfm = tfm;
/* shader */
vector<Shader*> used_shaders;

View File

@ -55,6 +55,21 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object
return tfm;
}
/* Lamp to world space transformation */
ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bool inverse)
{
int offset = lamp*LIGHT_SIZE + (inverse? 8 : 5);
Transform tfm;
tfm.x = kernel_tex_fetch(__light_data, offset + 0);
tfm.y = kernel_tex_fetch(__light_data, offset + 1);
tfm.z = kernel_tex_fetch(__light_data, offset + 2);
tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
return tfm;
}
/* Object to world space transformation for motion vectors */
ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int object, enum ObjectVectorTransform type)

View File

@ -320,7 +320,8 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
P, Ng, Ng,
shader, object, prim,
u, v, 1.0f, 0.5f,
!(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED));
!(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED),
LAMP_NONE);
sd.I = sd.N;
/* update differentials */

View File

@ -65,7 +65,7 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg,
shader_setup_from_sample(kg, emission_sd,
ls->P, ls->Ng, I,
ls->shader, ls->object, ls->prim,
ls->u, ls->v, t, time, false);
ls->u, ls->v, t, time, false, ls->lamp);
ls->Ng = ccl_fetch(emission_sd, Ng);

View File

@ -297,7 +297,7 @@ ccl_device_inline float background_portal_pdf(KernelGlobals *kg,
float3 axisu = make_float3(data1.y, data1.z, data1.w);
float3 axisv = make_float3(data2.y, data2.z, data2.w);
if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL))
if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL, NULL, NULL))
continue;
portal_pdf += area_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false);
@ -585,6 +585,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg,
return false;
}
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
}
else {
@ -600,11 +604,16 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg,
return false;
}
float3 inplane = ls->P;
ls->pdf = area_light_sample(P, &ls->P,
axisu, axisv,
randu, randv,
true);
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);
@ -706,6 +715,9 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D,
if(ls->eval_fac == 0.0f)
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
/* compute pdf */
if(ls->t != FLT_MAX)
@ -730,8 +742,10 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D,
float3 light_P = make_float3(data0.y, data0.z, data0.w);
if(!ray_quad_intersect(P, D, 0.0f, t,
light_P, axisu, axisv, Ng, &ls->P, &ls->t))
if(!ray_quad_intersect(P, D, 0.0f, t, light_P,
axisu, axisv, Ng,
&ls->P, &ls->t,
&ls->u, &ls->v))
{
return false;
}
@ -887,4 +901,3 @@ ccl_device int light_select_num_samples(KernelGlobals *kg, int index)
}
CCL_NAMESPACE_END

View File

@ -242,7 +242,8 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
int shader, int object, int prim,
float u, float v, float t,
float time,
bool object_space)
bool object_space,
int lamp)
{
/* vectors */
ccl_fetch(sd, P) = P;
@ -250,7 +251,12 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
ccl_fetch(sd, Ng) = Ng;
ccl_fetch(sd, I) = I;
ccl_fetch(sd, shader) = shader;
ccl_fetch(sd, type) = (prim == PRIM_NONE)? PRIMITIVE_NONE: PRIMITIVE_TRIANGLE;
if(prim != PRIM_NONE)
ccl_fetch(sd, type) = PRIMITIVE_TRIANGLE;
else if(lamp != LAMP_NONE)
ccl_fetch(sd, type) = PRIMITIVE_LAMP;
else
ccl_fetch(sd, type) = PRIMITIVE_NONE;
/* primitive */
#ifdef __INSTANCING__
@ -270,11 +276,15 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
#ifdef __OBJECT_MOTION__
shader_setup_object_transforms(kg, sd, time);
}
ccl_fetch(sd, time) = time;
#else
}
else if(lamp != LAMP_NONE) {
ccl_fetch(sd, ob_tfm) = lamp_fetch_transform(kg, lamp, false);
ccl_fetch(sd, ob_itfm) = lamp_fetch_transform(kg, lamp, true);
}
#ifdef __OBJECT_MOTION__
ccl_fetch(sd, time) = time;
#endif
/* transform into world space */
@ -357,7 +367,8 @@ ccl_device void shader_setup_from_displace(KernelGlobals *kg, ShaderData *sd,
P, Ng, I,
shader, object, prim,
u, v, 0.0f, 0.5f,
!(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED));
!(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED),
LAMP_NONE);
}
/* ShaderData setup from ray into background */

View File

@ -37,7 +37,7 @@ CCL_NAMESPACE_BEGIN
/* constants */
#define OBJECT_SIZE 12
#define OBJECT_VECTOR_SIZE 6
#define LIGHT_SIZE 5
#define LIGHT_SIZE 11
#define FILTER_TABLE_SIZE 1024
#define RAMP_TABLE_SIZE 256
#define SHUTTER_TABLE_SIZE 256
@ -552,6 +552,8 @@ typedef enum PrimitiveType {
PRIMITIVE_MOTION_TRIANGLE = 2,
PRIMITIVE_CURVE = 4,
PRIMITIVE_MOTION_CURVE = 8,
/* Lamp primitive is not included below on purpose, since it is no real traceable primitive */
PRIMITIVE_LAMP = 16,
PRIMITIVE_ALL_TRIANGLE = (PRIMITIVE_TRIANGLE|PRIMITIVE_MOTION_TRIANGLE),
PRIMITIVE_ALL_CURVE = (PRIMITIVE_CURVE|PRIMITIVE_MOTION_CURVE),

View File

@ -166,6 +166,12 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result
tfm = transform_transpose(tfm);
COPY_MATRIX44(&result, &tfm);
return true;
}
else if(sd->type == PRIMITIVE_LAMP) {
Transform tfm = transform_transpose(sd->ob_tfm);
COPY_MATRIX44(&result, &tfm);
return true;
}
}
@ -196,6 +202,12 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44
itfm = transform_transpose(itfm);
COPY_MATRIX44(&result, &itfm);
return true;
}
else if(sd->type == PRIMITIVE_LAMP) {
Transform tfm = transform_transpose(sd->ob_itfm);
COPY_MATRIX44(&result, &tfm);
return true;
}
}
@ -285,6 +297,12 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result
tfm = transform_transpose(tfm);
COPY_MATRIX44(&result, &tfm);
return true;
}
else if(sd->type == PRIMITIVE_LAMP) {
Transform tfm = transform_transpose(sd->ob_tfm);
COPY_MATRIX44(&result, &tfm);
return true;
}
}
@ -310,6 +328,12 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44
tfm = transform_transpose(tfm);
COPY_MATRIX44(&result, &tfm);
return true;
}
else if(sd->type == PRIMITIVE_LAMP) {
Transform tfm = transform_transpose(sd->ob_itfm);
COPY_MATRIX44(&result, &tfm);
return true;
}
}

View File

@ -49,7 +49,7 @@ ccl_device void svm_node_tex_coord(KernelGlobals *kg,
}
case NODE_TEXCO_NORMAL: {
data = ccl_fetch(sd, N);
if(ccl_fetch(sd, object) != OBJECT_NONE)
if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP))
object_inverse_normal_transform(kg, sd, &data);
break;
}
@ -131,7 +131,7 @@ ccl_device void svm_node_tex_coord_bump_dx(KernelGlobals *kg,
}
case NODE_TEXCO_NORMAL: {
data = ccl_fetch(sd, N);
if(ccl_fetch(sd, object) != OBJECT_NONE)
if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP))
object_inverse_normal_transform(kg, sd, &data);
break;
}
@ -216,7 +216,7 @@ ccl_device void svm_node_tex_coord_bump_dy(KernelGlobals *kg,
}
case NODE_TEXCO_NORMAL: {
data = ccl_fetch(sd, N);
if(ccl_fetch(sd, object) != OBJECT_NONE)
if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP))
object_inverse_normal_transform(kg, sd, &data);
break;
}

View File

@ -126,6 +126,8 @@ NODE_DEFINE(Light)
SOCKET_FLOAT(spot_angle, "Spot Angle", M_PI_4_F);
SOCKET_FLOAT(spot_smooth, "Spot Smooth", 0.0f);
SOCKET_TRANSFORM(tfm, "Transform", transform_identity());
SOCKET_BOOLEAN(cast_shadow, "Cast Shadow", true);
SOCKET_BOOLEAN(use_mis, "Use Mis", false);
SOCKET_BOOLEAN(use_diffuse, "Use Diffuse", true);
@ -762,6 +764,11 @@ void LightManager::device_update_points(Device *device,
light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f);
Transform tfm = light->tfm;
Transform itfm = transform_inverse(tfm);
memcpy(&light_data[light_index*LIGHT_SIZE + 5], &tfm, sizeof(float4)*3);
memcpy(&light_data[light_index*LIGHT_SIZE + 8], &itfm, sizeof(float4)*3);
light_index++;
}
@ -788,6 +795,11 @@ void LightManager::device_update_points(Device *device,
light_data[light_index*LIGHT_SIZE + 3] = make_float4(-1, dir.x, dir.y, dir.z);
light_data[light_index*LIGHT_SIZE + 4] = make_float4(-1, 0.0f, 0.0f, 0.0f);
Transform tfm = light->tfm;
Transform itfm = transform_inverse(tfm);
memcpy(&light_data[light_index*LIGHT_SIZE + 5], &tfm, sizeof(float4)*3);
memcpy(&light_data[light_index*LIGHT_SIZE + 8], &itfm, sizeof(float4)*3);
light_index++;
}

View File

@ -50,6 +50,8 @@ public:
float3 axisv;
float sizev;
Transform tfm;
int map_resolution;
float spot_angle;

View File

@ -1579,7 +1579,7 @@ ccl_device_inline bool ray_triangle_intersect_uv(
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)
float3 *isect_P, float *isect_t, float *isect_u, float *isect_v)
{
float t = -(dot(ray_P, quad_n) - dot(quad_P, quad_n)) / dot(ray_D, quad_n);
if(t < ray_mint || t > ray_maxt)
@ -1587,13 +1587,19 @@ ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D, float ray_mint, f
float3 hit = ray_P + t*ray_D;
float3 inplane = hit - quad_P;
if(fabsf(dot(inplane, quad_u) / dot(quad_u, quad_u)) > 0.5f)
float u = dot(inplane, quad_u) / dot(quad_u, quad_u) + 0.5f;
if(u < 0.0f || u > 1.0f)
return false;
if(fabsf(dot(inplane, quad_v) / dot(quad_v, quad_v)) > 0.5f)
float v = dot(inplane, quad_v) / dot(quad_v, quad_v) + 0.5f;
if(v < 0.0f || v > 1.0f)
return false;
if(isect_P) *isect_P = hit;
if(isect_t) *isect_t = t;
if(isect_u) *isect_u = u;
if(isect_v) *isect_v = v;
return true;
}