235 lines
4.9 KiB
GLSL
235 lines
4.9 KiB
GLSL
/* Mainly From https://eheitzresearch.wordpress.com/415-2/ */
|
|
|
|
#define USE_LTC
|
|
|
|
#ifndef UTIL_TEX
|
|
#define UTIL_TEX
|
|
uniform sampler2DArray utilTex;
|
|
#endif /* UTIL_TEX */
|
|
|
|
/* from Real-Time Area Lighting: a Journey from Research to Production
|
|
* Stephen Hill and Eric Heitz */
|
|
float edge_integral(vec3 p1, vec3 p2)
|
|
{
|
|
#if 0
|
|
/* more accurate replacement of acos */
|
|
float x = dot(p1, p2);
|
|
float y = abs(x);
|
|
|
|
float a = 5.42031 + (3.12829 + 0.0902326 * y) * y;
|
|
float b = 3.45068 + (4.18814 + y) * y;
|
|
float theta_sintheta = a / b;
|
|
|
|
if (x < 0.0) {
|
|
theta_sintheta = (M_PI / sqrt(1.0 - x * x)) - theta_sintheta;
|
|
}
|
|
vec3 u = cross(p1, p2);
|
|
return theta_sintheta * dot(u, N);
|
|
#endif
|
|
float cos_theta = dot(p1, p2);
|
|
cos_theta = clamp(cos_theta, -0.9999, 0.9999);
|
|
|
|
float theta = acos(cos_theta);
|
|
vec3 u = normalize(cross(p1, p2));
|
|
return theta * cross(p1, p2).z / sin(theta);
|
|
}
|
|
|
|
int clip_quad_to_horizon(inout vec3 L[5])
|
|
{
|
|
/* detect clipping config */
|
|
int config = 0;
|
|
if (L[0].z > 0.0) config += 1;
|
|
if (L[1].z > 0.0) config += 2;
|
|
if (L[2].z > 0.0) config += 4;
|
|
if (L[3].z > 0.0) config += 8;
|
|
|
|
/* clip */
|
|
int n = 0;
|
|
|
|
if (config == 0)
|
|
{
|
|
/* clip all */
|
|
}
|
|
else if (config == 1) /* V1 clip V2 V3 V4 */
|
|
{
|
|
n = 3;
|
|
L[1] = -L[1].z * L[0] + L[0].z * L[1];
|
|
L[2] = -L[3].z * L[0] + L[0].z * L[3];
|
|
}
|
|
else if (config == 2) /* V2 clip V1 V3 V4 */
|
|
{
|
|
n = 3;
|
|
L[0] = -L[0].z * L[1] + L[1].z * L[0];
|
|
L[2] = -L[2].z * L[1] + L[1].z * L[2];
|
|
}
|
|
else if (config == 3) /* V1 V2 clip V3 V4 */
|
|
{
|
|
n = 4;
|
|
L[2] = -L[2].z * L[1] + L[1].z * L[2];
|
|
L[3] = -L[3].z * L[0] + L[0].z * L[3];
|
|
}
|
|
else if (config == 4) /* V3 clip V1 V2 V4 */
|
|
{
|
|
n = 3;
|
|
L[0] = -L[3].z * L[2] + L[2].z * L[3];
|
|
L[1] = -L[1].z * L[2] + L[2].z * L[1];
|
|
}
|
|
else if (config == 5) /* V1 V3 clip V2 V4) impossible */
|
|
{
|
|
n = 0;
|
|
}
|
|
else if (config == 6) /* V2 V3 clip V1 V4 */
|
|
{
|
|
n = 4;
|
|
L[0] = -L[0].z * L[1] + L[1].z * L[0];
|
|
L[3] = -L[3].z * L[2] + L[2].z * L[3];
|
|
}
|
|
else if (config == 7) /* V1 V2 V3 clip V4 */
|
|
{
|
|
n = 5;
|
|
L[4] = -L[3].z * L[0] + L[0].z * L[3];
|
|
L[3] = -L[3].z * L[2] + L[2].z * L[3];
|
|
}
|
|
else if (config == 8) /* V4 clip V1 V2 V3 */
|
|
{
|
|
n = 3;
|
|
L[0] = -L[0].z * L[3] + L[3].z * L[0];
|
|
L[1] = -L[2].z * L[3] + L[3].z * L[2];
|
|
L[2] = L[3];
|
|
}
|
|
else if (config == 9) /* V1 V4 clip V2 V3 */
|
|
{
|
|
n = 4;
|
|
L[1] = -L[1].z * L[0] + L[0].z * L[1];
|
|
L[2] = -L[2].z * L[3] + L[3].z * L[2];
|
|
}
|
|
else if (config == 10) /* V2 V4 clip V1 V3) impossible */
|
|
{
|
|
n = 0;
|
|
}
|
|
else if (config == 11) /* V1 V2 V4 clip V3 */
|
|
{
|
|
n = 5;
|
|
L[4] = L[3];
|
|
L[3] = -L[2].z * L[3] + L[3].z * L[2];
|
|
L[2] = -L[2].z * L[1] + L[1].z * L[2];
|
|
}
|
|
else if (config == 12) /* V3 V4 clip V1 V2 */
|
|
{
|
|
n = 4;
|
|
L[1] = -L[1].z * L[2] + L[2].z * L[1];
|
|
L[0] = -L[0].z * L[3] + L[3].z * L[0];
|
|
}
|
|
else if (config == 13) /* V1 V3 V4 clip V2 */
|
|
{
|
|
n = 5;
|
|
L[4] = L[3];
|
|
L[3] = L[2];
|
|
L[2] = -L[1].z * L[2] + L[2].z * L[1];
|
|
L[1] = -L[1].z * L[0] + L[0].z * L[1];
|
|
}
|
|
else if (config == 14) /* V2 V3 V4 clip V1 */
|
|
{
|
|
n = 5;
|
|
L[4] = -L[0].z * L[3] + L[3].z * L[0];
|
|
L[0] = -L[0].z * L[1] + L[1].z * L[0];
|
|
}
|
|
else if (config == 15) /* V1 V2 V3 V4 */
|
|
{
|
|
n = 4;
|
|
}
|
|
|
|
if (n == 3)
|
|
L[3] = L[0];
|
|
if (n == 4)
|
|
L[4] = L[0];
|
|
|
|
return n;
|
|
}
|
|
|
|
mat3 ltc_matrix(vec4 lut)
|
|
{
|
|
/* load inverse matrix */
|
|
mat3 Minv = mat3(
|
|
vec3( 1, 0, lut.y),
|
|
vec3( 0, lut.z, 0),
|
|
vec3(lut.w, 0, lut.x)
|
|
);
|
|
|
|
return Minv;
|
|
}
|
|
|
|
float ltc_evaluate(vec3 N, vec3 V, mat3 Minv, vec3 corners[4])
|
|
{
|
|
/* construct orthonormal basis around N */
|
|
vec3 T1, T2;
|
|
T1 = normalize(V - N*dot(V, N));
|
|
T2 = cross(N, T1);
|
|
|
|
/* rotate area light in (T1, T2, R) basis */
|
|
Minv = Minv * transpose(mat3(T1, T2, N));
|
|
|
|
/* polygon (allocate 5 vertices for clipping) */
|
|
vec3 L[5];
|
|
L[0] = Minv * corners[0];
|
|
L[1] = Minv * corners[1];
|
|
L[2] = Minv * corners[2];
|
|
L[3] = Minv * corners[3];
|
|
|
|
int n = clip_quad_to_horizon(L);
|
|
|
|
if (n == 0)
|
|
return 0.0;
|
|
|
|
/* project onto sphere */
|
|
L[0] = normalize(L[0]);
|
|
L[1] = normalize(L[1]);
|
|
L[2] = normalize(L[2]);
|
|
L[3] = normalize(L[3]);
|
|
L[4] = normalize(L[4]);
|
|
|
|
/* integrate */
|
|
float sum = 0.0;
|
|
|
|
sum += edge_integral(L[0], L[1]);
|
|
sum += edge_integral(L[1], L[2]);
|
|
sum += edge_integral(L[2], L[3]);
|
|
if (n >= 4)
|
|
sum += edge_integral(L[3], L[4]);
|
|
if (n == 5)
|
|
sum += edge_integral(L[4], L[0]);
|
|
|
|
return abs(sum);
|
|
}
|
|
|
|
/* Aproximate circle with an octogone */
|
|
#define LTC_CIRCLE_RES 8
|
|
float ltc_evaluate_circle(vec3 N, vec3 V, mat3 Minv, vec3 p[LTC_CIRCLE_RES])
|
|
{
|
|
/* construct orthonormal basis around N */
|
|
vec3 T1, T2;
|
|
T1 = normalize(V - N*dot(V, N));
|
|
T2 = cross(N, T1);
|
|
|
|
/* rotate area light in (T1, T2, R) basis */
|
|
Minv = Minv * transpose(mat3(T1, T2, N));
|
|
|
|
for (int i = 0; i < LTC_CIRCLE_RES; ++i) {
|
|
p[i] = Minv * p[i];
|
|
/* clip to horizon */
|
|
p[i].z = max(0.0, p[i].z);
|
|
/* project onto sphere */
|
|
p[i] = normalize(p[i]);
|
|
}
|
|
|
|
/* integrate */
|
|
float sum = 0.0;
|
|
for (int i = 0; i < LTC_CIRCLE_RES - 1; ++i) {
|
|
sum += edge_integral(p[i], p[i + 1]);
|
|
}
|
|
sum += edge_integral(p[LTC_CIRCLE_RES - 1], p[0]);
|
|
|
|
return max(0.0, sum);
|
|
}
|
|
|