2017-03-28 00:09:45 +02:00
|
|
|
|
|
|
|
|
#define M_PI 3.14159265358979323846 /* pi */
|
2017-04-18 12:50:09 +02:00
|
|
|
#define M_PI_2 1.57079632679489661923 /* pi/2 */
|
2017-03-29 23:45:07 +02:00
|
|
|
#define M_1_PI 0.318309886183790671538 /* 1/pi */
|
2017-04-03 11:04:42 +02:00
|
|
|
#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */
|
|
|
|
|
#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */
|
2017-03-29 23:45:07 +02:00
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
/* ------- Structures -------- */
|
|
|
|
|
|
|
|
|
|
struct LightData {
|
2017-04-10 12:06:17 +02:00
|
|
|
vec4 position_influence; /* w : InfluenceRadius */
|
|
|
|
|
vec4 color_spec; /* w : Spec Intensity */
|
|
|
|
|
vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */
|
|
|
|
|
vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */
|
|
|
|
|
vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */
|
|
|
|
|
vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Lamp Type */
|
2017-03-31 01:07:30 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* convenience aliases */
|
|
|
|
|
#define l_color color_spec.rgb
|
|
|
|
|
#define l_spec color_spec.a
|
|
|
|
|
#define l_position position_influence.xyz
|
|
|
|
|
#define l_influence position_influence.w
|
|
|
|
|
#define l_sizex rightvec_sizex.w
|
|
|
|
|
#define l_sizey upvec_sizey.w
|
|
|
|
|
#define l_right rightvec_sizex.xyz
|
|
|
|
|
#define l_up upvec_sizey.xyz
|
|
|
|
|
#define l_forward forwardvec_type.xyz
|
|
|
|
|
#define l_type forwardvec_type.w
|
2017-04-10 12:06:17 +02:00
|
|
|
#define l_spot_size spotdata_radius_shadow.x
|
|
|
|
|
#define l_spot_blend spotdata_radius_shadow.y
|
|
|
|
|
#define l_radius spotdata_radius_shadow.z
|
|
|
|
|
#define l_shadowid spotdata_radius_shadow.w
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct ShadowCubeData {
|
|
|
|
|
vec4 near_far_bias;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* convenience aliases */
|
|
|
|
|
#define sh_cube_near near_far_bias.x
|
|
|
|
|
#define sh_cube_far near_far_bias.y
|
|
|
|
|
#define sh_cube_bias near_far_bias.z
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct ShadowMapData {
|
|
|
|
|
mat4 shadowmat;
|
|
|
|
|
vec4 near_far_bias;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* convenience aliases */
|
|
|
|
|
#define sh_map_near near_far_bias.x
|
|
|
|
|
#define sh_map_far near_far_bias.y
|
|
|
|
|
#define sh_map_bias near_far_bias.z
|
|
|
|
|
|
2017-04-18 12:50:09 +02:00
|
|
|
#ifndef MAX_CASCADE_NUM
|
|
|
|
|
#define MAX_CASCADE_NUM 1
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-04-10 12:06:17 +02:00
|
|
|
struct ShadowCascadeData {
|
|
|
|
|
mat4 shadowmat[MAX_CASCADE_NUM];
|
2017-04-21 16:43:14 +02:00
|
|
|
/* arrays of float are not aligned so use vec4 */
|
|
|
|
|
vec4 split_distances;
|
|
|
|
|
vec4 bias;
|
2017-04-10 12:06:17 +02:00
|
|
|
};
|
|
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
struct AreaData {
|
|
|
|
|
vec3 corner[4];
|
|
|
|
|
float solid_angle;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ShadingData {
|
|
|
|
|
vec3 V; /* View vector */
|
|
|
|
|
vec3 N; /* World Normal of the fragment */
|
|
|
|
|
vec3 W; /* World Position of the fragment */
|
|
|
|
|
vec3 R; /* Reflection vector */
|
|
|
|
|
vec3 L; /* Current Light vector (normalized) */
|
|
|
|
|
vec3 spec_dominant_dir; /* dominant direction of the specular rays */
|
|
|
|
|
vec3 l_vector; /* Current Light vector */
|
|
|
|
|
float l_distance; /* distance(l_position, W) */
|
|
|
|
|
AreaData area_data; /* If current light is an area light */
|
|
|
|
|
};
|
|
|
|
|
|
2017-03-29 23:45:07 +02:00
|
|
|
/* ------- Convenience functions --------- */
|
|
|
|
|
|
|
|
|
|
vec3 mul(mat3 m, vec3 v) { return m * v; }
|
|
|
|
|
mat3 mul(mat3 m1, mat3 m2) { return m1 * m2; }
|
|
|
|
|
|
|
|
|
|
float saturate(float a) { return clamp(a, 0.0, 1.0); }
|
|
|
|
|
vec2 saturate(vec2 a) { return vec2(saturate(a.x), saturate(a.y)); }
|
|
|
|
|
vec3 saturate(vec3 a) { return vec3(saturate(a.x), saturate(a.y), saturate(a.z)); }
|
|
|
|
|
vec4 saturate(vec4 a) { return vec4(saturate(a.x), saturate(a.y), saturate(a.z), saturate(a.w)); }
|
|
|
|
|
|
|
|
|
|
float distance_squared(vec2 a, vec2 b) { a -= b; return dot(a, a); }
|
|
|
|
|
float distance_squared(vec3 a, vec3 b) { a -= b; return dot(a, a); }
|
|
|
|
|
|
|
|
|
|
float hypot(float x, float y) { return sqrt(x*x + y*y); }
|
|
|
|
|
|
|
|
|
|
float inverse_distance(vec3 V) { return max( 1 / length(V), 1e-8); }
|
|
|
|
|
|
|
|
|
|
float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
|
|
|
|
|
{
|
2017-04-09 16:34:15 +10:00
|
|
|
return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
|
2017-03-29 23:45:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
|
|
|
|
|
{
|
|
|
|
|
float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
|
|
|
|
|
return lineorigin + linedirection * dist;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-03 11:04:42 +02:00
|
|
|
float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
|
|
|
|
|
{
|
|
|
|
|
/* aligned plane normal */
|
|
|
|
|
vec3 L = planeorigin - lineorigin;
|
|
|
|
|
float diskdist = length(L);
|
|
|
|
|
vec3 planenormal = -normalize(L);
|
|
|
|
|
return -diskdist / dot(planenormal, linedirection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
|
|
|
|
|
{
|
|
|
|
|
float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
|
|
|
|
|
if (dist < 0) {
|
|
|
|
|
/* if intersection is behind we fake the intersection to be
|
|
|
|
|
* really far and (hopefully) not inside the radius of interest */
|
|
|
|
|
dist = 1e16;
|
|
|
|
|
}
|
|
|
|
|
return lineorigin + linedirection * dist;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-18 12:50:09 +02:00
|
|
|
/* -- Tangent Space conversion -- */
|
|
|
|
|
vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B)
|
|
|
|
|
{
|
|
|
|
|
return T * vector.x + B * vector.y + N * vector.z;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B)
|
|
|
|
|
{
|
|
|
|
|
return vec3( dot(T, vector), dot(B, vector), dot(N, vector));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
|
|
|
|
|
{
|
|
|
|
|
vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0,0.0,1.0) : vec3(1.0,0.0,0.0);
|
|
|
|
|
T = normalize( cross(UpVector, N) );
|
|
|
|
|
B = cross(N, T);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---- Opengl Depth conversion ---- */
|
2017-04-18 19:13:18 +02:00
|
|
|
float linear_depth(bool is_persp, float z, float zf, float zn)
|
2017-04-18 12:50:09 +02:00
|
|
|
{
|
2017-04-18 19:13:18 +02:00
|
|
|
if (is_persp) {
|
2017-04-18 12:50:09 +02:00
|
|
|
return (zn * zf) / (z * (zn - zf) + zf);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return (z * 2.0 - 1.0) * zf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-18 19:13:18 +02:00
|
|
|
float buffer_depth(bool is_persp, float z, float zf, float zn)
|
2017-04-18 12:50:09 +02:00
|
|
|
{
|
2017-04-18 19:13:18 +02:00
|
|
|
if (is_persp) {
|
2017-04-18 12:50:09 +02:00
|
|
|
return (zf * (zn - z)) / (z * (zn - zf));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return (z / (zf * 2.0)) + 0.5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-18 21:02:02 +02:00
|
|
|
#define spherical_harmonics spherical_harmonics_L2
|
|
|
|
|
|
|
|
|
|
/* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */
|
|
|
|
|
vec3 spherical_harmonics_L1(vec3 N, vec3 shcoefs[9])
|
|
|
|
|
{
|
|
|
|
|
vec3 sh = vec3(0.0);
|
|
|
|
|
|
|
|
|
|
sh += 0.282095 * shcoefs[0];
|
|
|
|
|
|
|
|
|
|
sh += -0.488603 * N.z * shcoefs[1];
|
|
|
|
|
sh += 0.488603 * N.y * shcoefs[2];
|
|
|
|
|
sh += -0.488603 * N.x * shcoefs[3];
|
|
|
|
|
|
|
|
|
|
return sh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 spherical_harmonics_L2(vec3 N, vec3 shcoefs[9])
|
|
|
|
|
{
|
|
|
|
|
vec3 sh = vec3(0.0);
|
|
|
|
|
|
|
|
|
|
sh += 0.282095 * shcoefs[0];
|
|
|
|
|
|
|
|
|
|
sh += -0.488603 * N.z * shcoefs[1];
|
|
|
|
|
sh += 0.488603 * N.y * shcoefs[2];
|
|
|
|
|
sh += -0.488603 * N.x * shcoefs[3];
|
|
|
|
|
|
|
|
|
|
sh += 1.092548 * N.x * N.z * shcoefs[4];
|
|
|
|
|
sh += -1.092548 * N.z * N.y * shcoefs[5];
|
|
|
|
|
sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * shcoefs[6];
|
|
|
|
|
sh += -1.092548 * N.x * N.y * shcoefs[7];
|
|
|
|
|
sh += 0.546274 * (N.x * N.x - N.z * N.z) * shcoefs[8];
|
|
|
|
|
|
|
|
|
|
return sh;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
float rectangle_solid_angle(AreaData ad)
|
2017-03-29 23:45:07 +02:00
|
|
|
{
|
2017-03-31 01:07:30 +02:00
|
|
|
vec3 n0 = normalize(cross(ad.corner[0], ad.corner[1]));
|
|
|
|
|
vec3 n1 = normalize(cross(ad.corner[1], ad.corner[2]));
|
|
|
|
|
vec3 n2 = normalize(cross(ad.corner[2], ad.corner[3]));
|
|
|
|
|
vec3 n3 = normalize(cross(ad.corner[3], ad.corner[0]));
|
2017-03-29 23:45:07 +02:00
|
|
|
|
|
|
|
|
float g0 = acos(dot(-n0, n1));
|
|
|
|
|
float g1 = acos(dot(-n1, n2));
|
|
|
|
|
float g2 = acos(dot(-n2, n3));
|
|
|
|
|
float g3 = acos(dot(-n3, n0));
|
|
|
|
|
|
|
|
|
|
return max(0.0, (g0 + g1 + g2 + g3 - 2.0 * M_PI));
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
vec3 get_specular_dominant_dir(vec3 N, vec3 R, float roughness)
|
2017-03-29 23:45:07 +02:00
|
|
|
{
|
2017-04-18 12:50:09 +02:00
|
|
|
float smoothness = 1.0 - roughness;
|
|
|
|
|
float fac = smoothness * (sqrt(smoothness) + roughness);
|
|
|
|
|
return normalize(mix(N, R, fac));
|
2017-03-29 23:45:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* From UE4 paper */
|
2017-03-31 01:07:30 +02:00
|
|
|
vec3 mrp_sphere(LightData ld, ShadingData sd, vec3 dir, inout float roughness, out float energy_conservation)
|
2017-03-29 23:45:07 +02:00
|
|
|
{
|
|
|
|
|
roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */
|
|
|
|
|
|
|
|
|
|
/* energy preservation */
|
2017-03-31 01:07:30 +02:00
|
|
|
float sphere_angle = saturate(ld.l_radius / sd.l_distance);
|
|
|
|
|
energy_conservation = pow(roughness / saturate(roughness + 0.5 * sphere_angle), 2.0);
|
2017-03-29 23:45:07 +02:00
|
|
|
|
|
|
|
|
/* sphere light */
|
2017-03-31 01:07:30 +02:00
|
|
|
float inter_dist = dot(sd.l_vector, dir);
|
|
|
|
|
vec3 closest_point_on_ray = inter_dist * dir;
|
|
|
|
|
vec3 center_to_ray = closest_point_on_ray - sd.l_vector;
|
2017-03-29 23:45:07 +02:00
|
|
|
|
|
|
|
|
/* closest point on sphere */
|
2017-03-31 01:07:30 +02:00
|
|
|
vec3 closest_point_on_sphere = sd.l_vector + center_to_ray * saturate(ld.l_radius * inverse_distance(center_to_ray));
|
2017-03-29 23:45:07 +02:00
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
return normalize(closest_point_on_sphere);
|
2017-03-29 23:45:07 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-03 11:04:42 +02:00
|
|
|
vec3 mrp_area(LightData ld, ShadingData sd, vec3 dir, inout float roughness, out float energy_conservation)
|
2017-03-29 23:45:07 +02:00
|
|
|
{
|
2017-04-03 11:04:42 +02:00
|
|
|
roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */
|
|
|
|
|
|
|
|
|
|
/* FIXME : This needs to be fixed */
|
|
|
|
|
energy_conservation = pow(roughness / saturate(roughness + 0.5 * sd.area_data.solid_angle), 2.0);
|
|
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
vec3 refproj = line_plane_intersect(sd.W, dir, ld.l_position, ld.l_forward);
|
2017-03-29 23:45:07 +02:00
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
/* Project the point onto the light plane */
|
|
|
|
|
vec3 refdir = refproj - ld.l_position;
|
|
|
|
|
vec2 mrp = vec2(dot(refdir, ld.l_right), dot(refdir, ld.l_up));
|
2017-03-29 23:45:07 +02:00
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
/* clamp to light shape bounds */
|
|
|
|
|
vec2 area_half_size = vec2(ld.l_sizex, ld.l_sizey);
|
2017-03-29 23:45:07 +02:00
|
|
|
mrp = clamp(mrp, -area_half_size, area_half_size);
|
|
|
|
|
|
2017-03-31 01:07:30 +02:00
|
|
|
/* go back in world space */
|
|
|
|
|
vec3 closest_point_on_rectangle = sd.l_vector + mrp.x * ld.l_right + mrp.y * ld.l_up;
|
2017-03-29 23:45:07 +02:00
|
|
|
|
2017-04-03 11:04:42 +02:00
|
|
|
float len = length(closest_point_on_rectangle);
|
|
|
|
|
energy_conservation /= len * len;
|
|
|
|
|
|
|
|
|
|
return closest_point_on_rectangle / len;
|
2017-03-29 23:45:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* GGX */
|
|
|
|
|
float D_ggx_opti(float NH, float a2)
|
|
|
|
|
{
|
|
|
|
|
float tmp = (NH * a2 - NH) * NH + 1.0;
|
|
|
|
|
return M_PI * tmp*tmp; /* Doing RCP and mul a2 at the end */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float G1_Smith_GGX(float NX, float a2)
|
|
|
|
|
{
|
|
|
|
|
/* Using Brian Karis approach and refactoring by NX/NX
|
|
|
|
|
* this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV
|
|
|
|
|
* Rcp is done on the whole G later
|
|
|
|
|
* Note that this is not convenient for the transmition formula */
|
2017-03-31 01:07:30 +02:00
|
|
|
return NX + sqrt(NX * (NX - NX * a2) + a2);
|
2017-03-29 23:45:07 +02:00
|
|
|
/* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
|
|
|
|
|
{
|
|
|
|
|
float a = roughness;
|
|
|
|
|
float a2 = a * a;
|
|
|
|
|
|
|
|
|
|
vec3 H = normalize(L + V);
|
|
|
|
|
float NH = max(dot(N, H), 1e-8);
|
|
|
|
|
float NL = max(dot(N, L), 1e-8);
|
|
|
|
|
float NV = max(dot(N, V), 1e-8);
|
|
|
|
|
|
|
|
|
|
float G = G1_Smith_GGX(NV, a2) * G1_Smith_GGX(NL, a2); /* Doing RCP at the end */
|
|
|
|
|
float D = D_ggx_opti(NH, a2);
|
|
|
|
|
|
|
|
|
|
/* Denominator is canceled by G1_Smith */
|
|
|
|
|
/* bsdf = D * G / (4.0 * NL * NV); /* Reference function */
|
|
|
|
|
return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
|
|
|
|
|
}
|