diff options
Diffstat (limited to 'source/blender/draw/engines/eevee/shaders')
6 files changed, 790 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl new file mode 100644 index 00000000000..3a91e284184 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -0,0 +1,197 @@ + +#define M_PI 3.14159265358979323846 /* pi */ +#define M_1_PI 0.318309886183790671538 /* 1/pi */ +#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */ +#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */ + +/* ------- Structures -------- */ + +struct LightData { + vec4 position_influence; /* w : InfluenceRadius */ + vec4 color_spec; /* w : Spec Intensity */ + vec4 spotdata_shadow; /* x : spot size, y : spot blend */ + vec4 rightvec_sizex; /* xyz: Normalized up vector, w: Lamp Type */ + vec4 upvec_sizey; /* xyz: Normalized right vector, w: Lamp Type */ + vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Lamp Type */ +}; + +/* 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_radius 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 +#define l_spot_size spotdata_shadow.x +#define l_spot_blend spotdata_shadow.y + +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 */ +}; + +/* ------- 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) +{ + return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection); +} + +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; +} + +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; +} + +float rectangle_solid_angle(AreaData ad) +{ + 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])); + + 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)); +} + +vec3 get_specular_dominant_dir(vec3 N, vec3 R, float roughness) +{ + return normalize(mix(N, R, 1.0 - roughness * roughness)); +} + +/* From UE4 paper */ +vec3 mrp_sphere(LightData ld, ShadingData sd, vec3 dir, inout float roughness, out float energy_conservation) +{ + roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */ + + /* energy preservation */ + float sphere_angle = saturate(ld.l_radius / sd.l_distance); + energy_conservation = pow(roughness / saturate(roughness + 0.5 * sphere_angle), 2.0); + + /* sphere light */ + 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; + + /* closest point on sphere */ + vec3 closest_point_on_sphere = sd.l_vector + center_to_ray * saturate(ld.l_radius * inverse_distance(center_to_ray)); + + return normalize(closest_point_on_sphere); +} + +vec3 mrp_area(LightData ld, ShadingData sd, vec3 dir, inout float roughness, out float energy_conservation) +{ + 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); + + vec3 refproj = line_plane_intersect(sd.W, dir, ld.l_position, ld.l_forward); + + /* 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)); + + /* clamp to light shape bounds */ + vec2 area_half_size = vec2(ld.l_sizex, ld.l_sizey); + mrp = clamp(mrp, -area_half_size, area_half_size); + + /* go back in world space */ + vec3 closest_point_on_rectangle = sd.l_vector + mrp.x * ld.l_right + mrp.y * ld.l_up; + + float len = length(closest_point_on_rectangle); + energy_conservation /= len * len; + + return closest_point_on_rectangle / len; +} + +/* 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 */ + return NX + sqrt(NX * (NX - NX * a2) + a2); + /* 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 */ +} diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl new file mode 100644 index 00000000000..afa87dfb2c8 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl @@ -0,0 +1,173 @@ +/* Bsdf direct light function */ +/* in other word, how materials react to scene lamps */ + +/* Naming convention + * V View vector (normalized) + * N World Normal (normalized) + * L Outgoing Light Vector (Surface to Light in World Space) (normalized) + * Ldist Distance from surface to the light + * W World Pos + */ + +/* ------------ Diffuse ------------- */ + +float direct_diffuse_point(LightData ld, ShadingData sd) +{ + float bsdf = max(0.0, dot(sd.N, sd.L)); + bsdf /= sd.l_distance * sd.l_distance; + return bsdf; +} + +/* From Frostbite PBR Course + * Analitical irradiance from a sphere with correct horizon handling + * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ +float direct_diffuse_sphere(LightData ld, ShadingData sd) +{ + float radius = max(ld.l_sizex, 0.0001); + float costheta = clamp(dot(sd.N, sd.L), -0.999, 0.999); + float h = min(ld.l_radius / sd.l_distance , 0.9999); + float h2 = h*h; + float costheta2 = costheta * costheta; + float bsdf; + + if (costheta2 > h2) { + bsdf = M_PI * h2 * clamp(costheta, 0.0, 1.0); + } + else { + float sintheta = sqrt(1.0 - costheta2); + float x = sqrt(1.0 / h2 - 1.0); + float y = -x * (costheta / sintheta); + float sinthetasqrty = sintheta * sqrt(1.0 - y * y); + bsdf = (costheta * acos(y) - x * sinthetasqrty) * h2 + atan(sinthetasqrty / x); + } + + bsdf = max(bsdf, 0.0); + bsdf *= M_1_PI2; + + return bsdf; +} + +/* From Frostbite PBR Course + * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ +float direct_diffuse_rectangle(LightData ld, ShadingData sd) +{ +#ifdef USE_LTC + float bsdf = ltc_evaluate(sd.N, sd.V, mat3(1.0), sd.area_data.corner); + bsdf *= M_1_2PI; +#else + float bsdf = sd.area_data.solid_angle * 0.2 * ( + max(0.0, dot(normalize(sd.area_data.corner[0]), sd.N)) + + max(0.0, dot(normalize(sd.area_data.corner[1]), sd.N)) + + max(0.0, dot(normalize(sd.area_data.corner[2]), sd.N)) + + max(0.0, dot(normalize(sd.area_data.corner[3]), sd.N)) + + max(0.0, dot(sd.L, sd.N)) + ); + bsdf *= M_1_PI; +#endif + return bsdf; +} + +/* infinitly far away point source, no decay */ +float direct_diffuse_sun(LightData ld, ShadingData sd) +{ + float bsdf = max(0.0, dot(sd.N, sd.L)); + bsdf *= M_1_PI; /* Normalize */ + return bsdf; +} + +#if 0 +float direct_diffuse_unit_disc(vec3 N, vec3 L) +{ + +} +#endif + +/* ----------- GGx ------------ */ +float direct_ggx_point(ShadingData sd, float roughness) +{ + float bsdf = bsdf_ggx(sd.N, sd.L, sd.V, roughness); + bsdf /= sd.l_distance * sd.l_distance; + return bsdf; +} + +float direct_ggx_sphere(LightData ld, ShadingData sd, float roughness) +{ +#ifdef USE_LTC + vec3 P = line_aligned_plane_intersect(vec3(0.0), sd.spec_dominant_dir, sd.l_vector); + + vec3 Px = normalize(P - sd.l_vector) * ld.l_radius; + vec3 Py = cross(Px, sd.L); + + float NV = max(dot(sd.N, sd.V), 1e-8); + vec2 uv = ltc_coords(NV, sqrt(roughness)); + mat3 ltcmat = ltc_matrix(uv); + +// #define HIGHEST_QUALITY +#ifdef HIGHEST_QUALITY + vec3 Pxy1 = normalize( Px + Py) * ld.l_radius; + vec3 Pxy2 = normalize(-Px + Py) * ld.l_radius; + + /* counter clockwise */ + vec3 points[8]; + points[0] = sd.l_vector + Px; + points[1] = sd.l_vector - Pxy2; + points[2] = sd.l_vector - Py; + points[3] = sd.l_vector - Pxy1; + points[4] = sd.l_vector - Px; + points[5] = sd.l_vector + Pxy2; + points[6] = sd.l_vector + Py; + points[7] = sd.l_vector + Pxy1; + float bsdf = ltc_evaluate_circle(sd.N, sd.V, ltcmat, points); +#else + vec3 points[4]; + points[0] = sd.l_vector + Px; + points[1] = sd.l_vector - Py; + points[2] = sd.l_vector - Px; + points[3] = sd.l_vector + Py; + float bsdf = ltc_evaluate(sd.N, sd.V, ltcmat, points); + /* sqrt(pi/2) difference between square and disk area */ + bsdf *= 1.25331413731; +#endif + + bsdf *= texture(ltcMag, uv).r; /* Bsdf intensity */ + bsdf *= M_1_2PI * M_1_PI; +#else + float energy_conservation; + vec3 L = mrp_sphere(ld, sd, sd.spec_dominant_dir, roughness, energy_conservation); + float bsdf = bsdf_ggx(sd.N, L, sd.V, roughness); + + bsdf *= energy_conservation / (sd.l_distance * sd.l_distance); + bsdf *= max(ld.l_radius * ld.l_radius, 1e-16); /* radius is already inside energy_conservation */ +#endif + return bsdf; +} + +float direct_ggx_rectangle(LightData ld, ShadingData sd, float roughness) +{ +#ifdef USE_LTC + float NV = max(dot(sd.N, sd.V), 1e-8); + vec2 uv = ltc_coords(NV, sqrt(roughness)); + mat3 ltcmat = ltc_matrix(uv); + + float bsdf = ltc_evaluate(sd.N, sd.V, ltcmat, sd.area_data.corner); + bsdf *= texture(ltcMag, uv).r; /* Bsdf intensity */ + bsdf *= M_1_2PI; +#else + float energy_conservation; + vec3 L = mrp_area(ld, sd, sd.spec_dominant_dir, roughness, energy_conservation); + float bsdf = bsdf_ggx(sd.N, L, sd.V, roughness); + + bsdf *= energy_conservation; + /* fade mrp artifacts */ + bsdf *= max(0.0, dot(-sd.spec_dominant_dir, ld.l_forward)); + bsdf *= max(0.0, -dot(L, ld.l_forward)); +#endif + return bsdf; +} + +#if 0 +float direct_ggx_disc(vec3 N, vec3 L) +{ + +} +#endif
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl new file mode 100644 index 00000000000..019f2d522f6 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -0,0 +1,133 @@ + +uniform int light_count; +uniform vec3 cameraPos; +uniform vec3 eye; +uniform mat4 ProjectionMatrix; + + +layout(std140) uniform light_block { + LightData lights_data[MAX_LIGHT]; +}; + +in vec3 worldPosition; +in vec3 worldNormal; + +out vec4 fragColor; + +/* type */ +#define POINT 0.0 +#define SUN 1.0 +#define SPOT 2.0 +#define HEMI 3.0 +#define AREA 4.0 + +float light_diffuse(LightData ld, ShadingData sd) +{ + if (ld.l_type == SUN) { + return direct_diffuse_sun(ld, sd); + } + else if (ld.l_type == AREA) { + return direct_diffuse_rectangle(ld, sd); + } + else { + return direct_diffuse_sphere(ld, sd); + } +} + +float light_specular(LightData ld, ShadingData sd, float roughness) +{ + if (ld.l_type == SUN) { + return direct_ggx_point(sd, roughness); + } + else if (ld.l_type == AREA) { + return direct_ggx_rectangle(ld, sd, roughness); + } + else { + // return direct_ggx_point(sd, roughness); + return direct_ggx_sphere(ld, sd, roughness); + } +} + +float light_visibility(LightData ld, ShadingData sd) +{ + float vis = 1.0; + + if (ld.l_type == SPOT) { + float z = dot(ld.l_forward, sd.l_vector); + vec3 lL = sd.l_vector / z; + float x = dot(ld.l_right, lL) / ld.l_sizex; + float y = dot(ld.l_up, lL) / ld.l_sizey; + + float ellipse = 1.0 / sqrt(1.0 + x * x + y * y); + + float spotmask = smoothstep(0.0, 1.0, (ellipse - ld.l_spot_size) / ld.l_spot_blend); + + vis *= spotmask; + } + else if (ld.l_type == AREA) { + vis *= step(0.0, -dot(sd.L, ld.l_forward)); + } + + return vis; +} + +/* Calculation common to all bsdfs */ +float light_common(inout LightData ld, inout ShadingData sd) +{ + float vis = 1.0; + + if (ld.l_type == SUN) { + sd.L = -ld.l_forward; + } + else { + sd.L = sd.l_vector / sd.l_distance; + } + + if (ld.l_type == AREA) { + sd.area_data.corner[0] = sd.l_vector + ld.l_right * -ld.l_sizex + ld.l_up * ld.l_sizey; + sd.area_data.corner[1] = sd.l_vector + ld.l_right * -ld.l_sizex + ld.l_up * -ld.l_sizey; + sd.area_data.corner[2] = sd.l_vector + ld.l_right * ld.l_sizex + ld.l_up * -ld.l_sizey; + sd.area_data.corner[3] = sd.l_vector + ld.l_right * ld.l_sizex + ld.l_up * ld.l_sizey; +#ifndef USE_LTC + sd.area_data.solid_angle = rectangle_solid_angle(sd.area_data); +#endif + } + + return vis; +} + +void main() +{ + ShadingData sd; + sd.N = normalize(worldNormal); + sd.V = (ProjectionMatrix[3][3] == 0.0) /* if perspective */ + ? normalize(cameraPos - worldPosition) + : normalize(eye); + sd.W = worldPosition; + sd.R = reflect(-sd.V, sd.N); + + /* hardcoded test vars */ + vec3 albedo = vec3(0.0); + vec3 specular = mix(vec3(1.0), vec3(1.0), pow(max(0.0, 1.0 - dot(sd.N, sd.V)), 5.0)); + float roughness = 0.1; + + sd.spec_dominant_dir = get_specular_dominant_dir(sd.N, sd.R, roughness); + + vec3 radiance = vec3(0.0); + for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) { + LightData ld = lights_data[i]; + + sd.l_vector = ld.l_position - worldPosition; + sd.l_distance = length(sd.l_vector); + + light_common(ld, sd); + + float vis = light_visibility(ld, sd); + float spec = light_specular(ld, sd, roughness); + float diff = light_diffuse(ld, sd); + + radiance += vis * (albedo * diff + specular * spec) * ld.l_color; + } + + fragColor = vec4(radiance, 1.0); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl new file mode 100644 index 00000000000..d0a5ee06694 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl @@ -0,0 +1,16 @@ + +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ModelMatrix; +uniform mat3 WorldNormalMatrix; + +in vec3 pos; +in vec3 nor; + +out vec3 worldPosition; +out vec3 worldNormal; + +void main() { + gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); + worldPosition = (ModelMatrix * vec4(pos, 1.0)).xyz; + worldNormal = WorldNormalMatrix * nor; +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl new file mode 100644 index 00000000000..131b0c2de78 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl @@ -0,0 +1,243 @@ +/* Mainly From https://eheitzresearch.wordpress.com/415-2/ */ + +#define USE_LTC +#define LTC_LUT_SIZE 64 + +uniform sampler2D ltcMat; +uniform sampler2D ltcMag; + +/* 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; +} + +vec2 ltc_coords(float cosTheta, float roughness) +{ + float theta = acos(cosTheta); + vec2 coords = vec2(roughness, theta/(0.5*3.14159)); + + /* scale and bias coordinates, for correct filtered lookup */ + return coords * (LTC_LUT_SIZE - 1.0) / LTC_LUT_SIZE + 0.5 / LTC_LUT_SIZE; +} + +mat3 ltc_matrix(vec2 coord) +{ + /* load inverse matrix */ + vec4 t = texture(ltcMat, coord); + mat3 Minv = mat3( + vec3( 1, 0, t.y), + vec3( 0, t.z, 0), + vec3(t.w, 0, t.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); +} + diff --git a/source/blender/draw/engines/eevee/shaders/tonemap_frag.glsl b/source/blender/draw/engines/eevee/shaders/tonemap_frag.glsl new file mode 100644 index 00000000000..cb2ceb5ed07 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/tonemap_frag.glsl @@ -0,0 +1,28 @@ + +uniform sampler2D hdrColorBuf; + +in vec4 uvcoordsvar; + +out vec4 fragColor; + +float linearrgb_to_srgb(float c) +{ + if (c < 0.0031308) + return (c < 0.0) ? 0.0 : c * 12.92; + else + return 1.055 * pow(c, 1.0 / 2.4) - 0.055; +} + +void linearrgb_to_srgb(vec4 col_from, out vec4 col_to) +{ + col_to.r = linearrgb_to_srgb(col_from.r); + col_to.g = linearrgb_to_srgb(col_from.g); + col_to.b = linearrgb_to_srgb(col_from.b); + col_to.a = col_from.a; +} + +void main() { + fragColor = texture(hdrColorBuf, uvcoordsvar.st); + + linearrgb_to_srgb(fragColor, fragColor); +}
\ No newline at end of file |