|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
|
|
|
|
|
|
#pragma BLENDER_REQUIRE(draw_math_geom_lib.glsl)
|
|
|
|
|
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
|
|
|
|
|
#pragma BLENDER_REQUIRE(eevee_ltc_lib.glsl)
|
|
|
|
|
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
|
|
|
|
|
|
|
|
|
@ -102,7 +103,200 @@ float light_spot_attenuation(LightData light, vec3 L)
|
|
|
|
|
return spotmask * step(0.0, -dot(L, -light._back));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float light_attenuation_common(LightData light, const bool is_directional, vec3 L)
|
|
|
|
|
/**
|
|
|
|
|
* Approximate the ratio of the area of intersection of two spherical caps divided by the area of
|
|
|
|
|
* the smallest cap.
|
|
|
|
|
*/
|
|
|
|
|
float light_cone_cone_ratio(float cone_angle_1, float cone_angle_2, float angle_between_axes)
|
|
|
|
|
{
|
|
|
|
|
/* From "Ambient Aperture Lighting" by Chris Oat
|
|
|
|
|
* Slide 15.
|
|
|
|
|
* Simplified since we divide by the solid angle of the smallest cone. */
|
|
|
|
|
return smoothstep(
|
|
|
|
|
cone_angle_1 + cone_angle_2, abs(cone_angle_1 - cone_angle_2), abs(angle_between_axes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float linearstep(float edge0, float edge1, float x)
|
|
|
|
|
{
|
|
|
|
|
return saturate((x - edge0) / (edge1 - edge0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float ratio_cone_cone(float cone_angle_1, float cone_angle_2, float angle_between_axes)
|
|
|
|
|
{
|
|
|
|
|
return light_cone_cone_ratio(cone_angle_1, cone_angle_2, angle_between_axes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float area_cone(float cone_angle)
|
|
|
|
|
{
|
|
|
|
|
return M_TAU * (1.0 - cos(cone_angle));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float area_lune(float lune_angle)
|
|
|
|
|
{
|
|
|
|
|
return linearstep(0.0, M_TAU, abs(lune_angle));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float area_cone_cone(float cone_angle_1, float cone_angle_2, float angle_between_axes)
|
|
|
|
|
{
|
|
|
|
|
return ratio_cone_cone(cone_angle_1, cone_angle_2, angle_between_axes) *
|
|
|
|
|
area_cone(min(cone_angle_1, cone_angle_2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float area_cone_hemisphere(float cone_angle, float angle_between_axes)
|
|
|
|
|
{
|
|
|
|
|
float r = abs(cone_angle);
|
|
|
|
|
float d = abs(angle_between_axes);
|
|
|
|
|
d = clamp(d, M_PI_2 - r + 0.0001, M_PI_2 + r - 0.0001);
|
|
|
|
|
return -2.0 * acos(cos(d) / sin(r)) - 2.0 * acos(-cos(d) * cos(r) / (sin(d) * sin(r))) * cos(r) +
|
|
|
|
|
M_TAU;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float light_cone_lune_area(float spread_half_angle, float edge_angle_1, float edge_angle_2)
|
|
|
|
|
{
|
|
|
|
|
float angle_hemi1 = edge_angle_1 + M_PI_2;
|
|
|
|
|
float angle_hemi2 = edge_angle_2 - M_PI_2;
|
|
|
|
|
float ratio_hemi1_cone_isect = area_cone_hemisphere(spread_half_angle, angle_hemi1) /
|
|
|
|
|
min(area_cone_hemisphere(M_PI_2, angle_hemi1),
|
|
|
|
|
area_cone(spread_half_angle));
|
|
|
|
|
float ratio_hemi2_cone_isect = area_cone_hemisphere(spread_half_angle, angle_hemi2) /
|
|
|
|
|
min(area_cone_hemisphere(M_PI_2, angle_hemi2),
|
|
|
|
|
area_cone(spread_half_angle));
|
|
|
|
|
return min(ratio_hemi1_cone_isect, ratio_hemi2_cone_isect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float light_lune_cone_ratio(float spread_half_angle, float edge_angle_1, float edge_angle_2)
|
|
|
|
|
{
|
|
|
|
|
return light_cone_lune_area(spread_half_angle, edge_angle_1, edge_angle_2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* https://www.shadertoy.com/view/4dVcR1 */
|
|
|
|
|
|
|
|
|
|
vec2 msign(vec2 x)
|
|
|
|
|
{
|
|
|
|
|
return vec2((x.x < 0.0) ? -1.0 : 1.0, (x.y < 0.0) ? -1.0 : 1.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec2 sdEllipseNearestPoint(vec2 p, vec2 ab)
|
|
|
|
|
{
|
|
|
|
|
vec2 p_sign = msign(p);
|
|
|
|
|
// symmetry
|
|
|
|
|
p = abs(p);
|
|
|
|
|
|
|
|
|
|
// find root with Newton solver
|
|
|
|
|
vec2 q = ab * (p - ab);
|
|
|
|
|
|
|
|
|
|
float w = (q.x < q.y) ? 1.570796327 : 0.0;
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
vec2 cs = vec2(cos(w), sin(w));
|
|
|
|
|
vec2 u = ab * vec2(cs.x, cs.y);
|
|
|
|
|
vec2 v = ab * vec2(-cs.y, cs.x);
|
|
|
|
|
w = w + dot(p - u, v) / (dot(p - u, u) + dot(v, v));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ab * vec2(cos(w), sin(w)) * p_sign;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec2 sdEllipseFarthestPoint(vec2 p, vec2 ab)
|
|
|
|
|
{
|
|
|
|
|
vec2 p_sign = msign(p);
|
|
|
|
|
// symmetry
|
|
|
|
|
p = abs(p);
|
|
|
|
|
|
|
|
|
|
// find root with Newton solver
|
|
|
|
|
vec2 q = ab * (p + ab);
|
|
|
|
|
|
|
|
|
|
float w = 3.1415 + ((q.x < q.y) ? 1.570796327 : 0.0);
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
vec2 cs = vec2(cos(w), sin(w));
|
|
|
|
|
vec2 u = ab * vec2(cs.x, cs.y);
|
|
|
|
|
vec2 v = ab * vec2(-cs.y, cs.x);
|
|
|
|
|
w = w + dot(p - u, v) / (dot(p - u, u) + dot(v, v));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ab * vec2(cos(w), sin(w)) * p_sign;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* from Real-Time Area Lighting: a Journey from Research to Production
|
|
|
|
|
* Stephen Hill and Eric Heitz */
|
|
|
|
|
vec3 light_edge_integral_vec(vec3 v1, vec3 v2)
|
|
|
|
|
{
|
|
|
|
|
float x = dot(v1, v2);
|
|
|
|
|
float y = abs(x);
|
|
|
|
|
|
|
|
|
|
float a = 0.8543985 + (0.4965155 + 0.0145206 * y) * y;
|
|
|
|
|
float b = 3.4175940 + (4.1616724 + y) * y;
|
|
|
|
|
float v = a / b;
|
|
|
|
|
|
|
|
|
|
float theta_sintheta = (x > 0.0) ? v : 0.5 * inversesqrt(max(1.0 - x * x, 1e-7)) - v;
|
|
|
|
|
|
|
|
|
|
return cross(v1, v2) * theta_sintheta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float light_spread_angle_attenuation(LightData light, vec3 L, float dist)
|
|
|
|
|
{
|
|
|
|
|
vec3 lL = light_world_to_local(light, L * dist);
|
|
|
|
|
float distance_to_plane = abs(lL.z);
|
|
|
|
|
vec2 area_size = vec2(light._area_size_x, light._area_size_y);
|
|
|
|
|
|
|
|
|
|
if (light.type == LIGHT_RECT) {
|
|
|
|
|
vec2 r1 = lL.xy - area_size;
|
|
|
|
|
vec2 r2 = lL.xy + area_size;
|
|
|
|
|
|
|
|
|
|
vec3 corners[4];
|
|
|
|
|
corners[0] = vec3(area_size.x, -area_size.y, 0.0);
|
|
|
|
|
corners[1] = vec3(area_size.x, area_size.y, 0.0);
|
|
|
|
|
corners[2] = -corners[0];
|
|
|
|
|
corners[3] = -corners[1];
|
|
|
|
|
|
|
|
|
|
corners[0] = normalize(corners[0] + lL);
|
|
|
|
|
corners[1] = normalize(corners[1] + lL);
|
|
|
|
|
corners[2] = normalize(corners[2] + lL);
|
|
|
|
|
corners[3] = normalize(corners[3] + lL);
|
|
|
|
|
|
|
|
|
|
vec3 avg_dir;
|
|
|
|
|
avg_dir = light_edge_integral_vec(corners[0], corners[1]);
|
|
|
|
|
avg_dir += light_edge_integral_vec(corners[1], corners[2]);
|
|
|
|
|
avg_dir += light_edge_integral_vec(corners[2], corners[3]);
|
|
|
|
|
avg_dir += light_edge_integral_vec(corners[3], corners[0]);
|
|
|
|
|
|
|
|
|
|
float form_factor = length(avg_dir);
|
|
|
|
|
float avg_dir_z = (avg_dir / form_factor).z;
|
|
|
|
|
|
|
|
|
|
float half_light_angle = acos(1.0 - form_factor);
|
|
|
|
|
|
|
|
|
|
return light_cone_cone_ratio(light.spread_half_angle, half_light_angle, acos(avg_dir_z));
|
|
|
|
|
|
|
|
|
|
/* angle_1 is min angle of intersection with first edge of the lune.
|
|
|
|
|
* angle_2 is min angle of intersection with second edge of the lune. */
|
|
|
|
|
/* TODO(fclem): Port fast_atanf from cycles. */
|
|
|
|
|
vec2 angle_1 = atan(r1, vec2(distance_to_plane));
|
|
|
|
|
vec2 angle_2 = atan(r2, vec2(distance_to_plane));
|
|
|
|
|
|
|
|
|
|
float x = light_lune_cone_ratio(light.spread_half_angle, angle_1.x, angle_2.x);
|
|
|
|
|
float y = light_lune_cone_ratio(light.spread_half_angle, angle_1.y, angle_2.y);
|
|
|
|
|
return x * y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float r1 = distance(sdEllipseNearestPoint(lL.xy, area_size), lL.xy);
|
|
|
|
|
float r2 = distance(sdEllipseFarthestPoint(lL.xy, area_size), lL.xy);
|
|
|
|
|
|
|
|
|
|
bool inside_ellipse = length_squared(lL.xy / area_size) < 1.0;
|
|
|
|
|
if (inside_ellipse) {
|
|
|
|
|
/* Signed distance. */
|
|
|
|
|
r1 = -r1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO(fclem): Port fast_atanf from cycles. */
|
|
|
|
|
float angle_1 = atan(r1, distance_to_plane);
|
|
|
|
|
float angle_2 = atan(r2, distance_to_plane);
|
|
|
|
|
|
|
|
|
|
float half_light_angle = abs(angle_1 - angle_2) / 2.0;
|
|
|
|
|
float elevation_angle = (angle_1 + angle_2) / 2.0;
|
|
|
|
|
|
|
|
|
|
return light_cone_cone_ratio(light.spread_half_angle, half_light_angle, elevation_angle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float light_attenuation_common(LightData light, const bool is_directional, vec3 L, float dist)
|
|
|
|
|
{
|
|
|
|
|
if (is_directional) {
|
|
|
|
|
return 1.0;
|
|
|
|
@ -111,7 +305,7 @@ float light_attenuation_common(LightData light, const bool is_directional, vec3
|
|
|
|
|
return light_spot_attenuation(light, L);
|
|
|
|
|
}
|
|
|
|
|
if (is_area_light(light.type)) {
|
|
|
|
|
return step(0.0, -dot(L, -light._back));
|
|
|
|
|
return step(0.0, -dot(L, -light._back)) * light_spread_angle_attenuation(light, L, dist);
|
|
|
|
|
}
|
|
|
|
|
return 1.0;
|
|
|
|
|
}
|
|
|
|
@ -144,14 +338,14 @@ vec2 light_attenuation_facing(LightData light, vec3 L, float distance_to_light,
|
|
|
|
|
|
|
|
|
|
vec2 light_attenuation_surface(LightData light, const bool is_directional, vec3 Ng, LightVector lv)
|
|
|
|
|
{
|
|
|
|
|
return light_attenuation_common(light, is_directional, lv.L) *
|
|
|
|
|
return light_attenuation_common(light, is_directional, lv.L, lv.dist) *
|
|
|
|
|
light_attenuation_facing(light, lv.L, lv.dist, Ng) *
|
|
|
|
|
light_influence_attenuation(lv.dist, light.influence_radius_invsqr_surface);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float light_attenuation_volume(LightData light, const bool is_directional, LightVector lv)
|
|
|
|
|
{
|
|
|
|
|
return light_attenuation_common(light, is_directional, lv.L) *
|
|
|
|
|
return light_attenuation_common(light, is_directional, lv.L, lv.dist) *
|
|
|
|
|
light_influence_attenuation(lv.dist, light.influence_radius_invsqr_volume);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -205,7 +399,7 @@ float light_ltc(
|
|
|
|
|
corners[2] = -corners[0];
|
|
|
|
|
corners[3] = -corners[1];
|
|
|
|
|
|
|
|
|
|
vec3 L = lv.L * lv.dist;
|
|
|
|
|
vec3 L = mix(light._back, lv.L * lv.dist, light.spread_mix_fac);
|
|
|
|
|
corners[0] += L;
|
|
|
|
|
corners[1] += L;
|
|
|
|
|
corners[2] += L;
|
|
|
|
@ -242,7 +436,7 @@ float light_ltc(
|
|
|
|
|
points[1] = Px * size.x + Py * -size.y;
|
|
|
|
|
points[2] = -points[0];
|
|
|
|
|
|
|
|
|
|
vec3 L = lv.L * lv.dist;
|
|
|
|
|
vec3 L = mix(light._back, lv.L * lv.dist, light.spread_mix_fac);
|
|
|
|
|
points[0] += L;
|
|
|
|
|
points[1] += L;
|
|
|
|
|
points[2] += L;
|
|
|
|
|