diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-03-30 00:45:07 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-03-30 00:45:44 +0300 |
commit | 4743fa52ac9545cdd6b815671761081cda10f807 (patch) | |
tree | 27a3faa193432d516793402a1723bd6657df038b | |
parent | ccb9f683e52ced60ba055545637f9775f761aa98 (diff) |
Eevee: Diffuse Light (2/2) and GGX low quality lights
GGX is missing sun lamps area.
6 files changed, 318 insertions, 33 deletions
diff --git a/release/scripts/startup/bl_ui/properties_data_lamp.py b/release/scripts/startup/bl_ui/properties_data_lamp.py index ab84eb8dd60..31ad299f924 100644 --- a/release/scripts/startup/bl_ui/properties_data_lamp.py +++ b/release/scripts/startup/bl_ui/properties_data_lamp.py @@ -96,6 +96,7 @@ class DATA_PT_lamp(DataButtonsPanel, Panel): sub.label(text="Falloff:") sub.prop(lamp, "falloff_type", text="") sub.prop(lamp, "distance") + sub.prop(lamp, "shadow_soft_size", text="Radius") if lamp.falloff_type == 'LINEAR_QUADRATIC_WEIGHTED': col.label(text="Attenuation Factors:") diff --git a/source/blender/draw/engines/eevee/eevee.c b/source/blender/draw/engines/eevee/eevee.c index 5ab14537919..f5c4f2385af 100644 --- a/source/blender/draw/engines/eevee/eevee.c +++ b/source/blender/draw/engines/eevee/eevee.c @@ -37,6 +37,7 @@ static struct { struct GPUShader *default_lit; struct GPUShader *depth_sh; struct GPUShader *tonemap; + float camera_pos[3]; } e_data = {NULL}; /* Engine data */ extern char datatoc_bsdf_common_lib_glsl[]; @@ -86,6 +87,12 @@ static void EEVEE_engine_init(void *vedata) EEVEE_lights_init(stl); // EEVEE_lights_update(stl); + { + float viewinvmat[4][4]; + DRW_viewport_matrix_get(viewinvmat, DRW_MAT_VIEWINV); + + copy_v3_v3(e_data.camera_pos, viewinvmat[3]); + } } static void EEVEE_cache_init(void *vedata) @@ -126,6 +133,7 @@ static void EEVEE_cache_init(void *vedata) stl->g_data->default_lit_grp = DRW_shgroup_create(e_data.default_lit, psl->pass); DRW_shgroup_uniform_block(stl->g_data->default_lit_grp, "light_block", stl->lights_ubo, 0); DRW_shgroup_uniform_int(stl->g_data->default_lit_grp, "light_count", &stl->lights_info->light_count, 1); + DRW_shgroup_uniform_vec3(stl->g_data->default_lit_grp, "cameraPos", e_data.camera_pos, 1); } { diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index c073175a0b2..3b781db90c2 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -123,9 +123,13 @@ void EEVEE_lights_update(EEVEE_StorageList *stl) evli->spotsize = cosf(la->spotsize * 0.5f); evli->spotblend = (1.0f - evli->spotsize) * la->spotblend; } - // else if (la->type == LA_SPOT) { - - // } + else if (la->type == LA_AREA) { + evli->sizex = la->area_size * scale[0] * 0.5f; + evli->sizey = la->area_sizey * scale[1] * 0.5f; + } + else { + evli->sizex = la->area_size * scale[0] * 0.5f; + } /* Lamp Type */ evli->lamptype = (float)la->type; diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index 0ed35509061..b0b48e94809 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -1,3 +1,144 @@ #define M_PI 3.14159265358979323846 /* pi */ -#define M_1_PI 0.318309886183790671538 /* 1/pi */
\ No newline at end of file +#define M_1_PI 0.318309886183790671538 /* 1/pi */ + +/* ------- 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 rectangle_solid_angle(vec3 p0, vec3 p1, vec3 p2, vec3 p3) +{ + vec3 n0 = normalize(cross(p0, p1)); + vec3 n1 = normalize(cross(p1, p2)); + vec3 n2 = normalize(cross(p2, p3)); + vec3 n3 = normalize(cross(p3, p0)); + + 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)); +} + + +/* ------- Energy Conversion for lights ------- */ +/* from Sebastien Lagarde + * course_notes_moving_frostbite_to_pbr.pdf */ + +float sphere_energy(float radius) +{ + radius = max(radius, 1e-8); + return 0.25 / (radius*radius * M_PI * M_PI) /* 1/(4*r²*Pi²) */ + * M_PI * M_PI * 10.0; /* XXX : Empirical, Fit cycles power */ +} + +float rectangle_energy(float width, float height) +{ + return M_1_PI / (width*height) /* 1/(w*h*Pi) */ + * 20.0; /* XXX : Empirical, Fit cycles power */ +} + +/* From UE4 paper */ +void mrp_sphere( + float radius, float dist, vec3 R, inout vec3 L, + inout float roughness, inout float energy_conservation) +{ + L = dist * L; + + /* Sphere Light */ + roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */ + + /* energy preservation */ + float sphere_angle = saturate(radius / dist); + energy_conservation *= pow(roughness / saturate(roughness + 0.5 * sphere_angle), 2.0); + + /* sphere light */ + float inter_dist = dot(L, R); + vec3 closest_point_on_ray = inter_dist * R; + vec3 center_to_ray = closest_point_on_ray - L; + + /* closest point on sphere */ + L = L + center_to_ray * saturate(radius * inverse_distance(center_to_ray)); + + L = normalize(L); +} + +void mrp_area(vec3 R, vec3 N, vec3 W, vec3 Lpos, vec3 Lx, vec3 Ly, vec3 Lz, float sizeX, float sizeY, float dist, inout vec3 L) +{ + vec3 refproj = line_plane_intersect(W, R, Lpos, Lz); + vec3 norproj = line_plane_intersect(W, N, Lpos, Lz); + + vec2 area_half_size = vec2(sizeX, sizeY); + + /* Find the closest point to the rectangular light shape */ + vec3 refdir = refproj - Lpos; + vec2 mrp = vec2(dot(refdir, Lx), dot(refdir, Ly)); + + /* clamp to corners */ + mrp = clamp(mrp, -area_half_size, area_half_size); + + L = dist * L; + L = L + mrp.x * Lx + mrp.y * Ly ; + + L = normalize(L); +} + +/* 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 index 4f66b79024d..953d4e0a551 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl @@ -2,6 +2,7 @@ /* 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 @@ -14,20 +15,69 @@ float direct_diffuse_point(vec3 N, vec3 L, float Ldist) { float bsdf = max(0.0, dot(N, L)); bsdf /= Ldist * Ldist; - bsdf *= M_1_PI; /* Normalize */ + bsdf *= M_PI / 2.0; /* Normalize */ return bsdf; } -#if 0 -float direct_diffuse_sphere(vec3 N, vec3 L) + +/* 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(vec3 N, vec3 L, float Ldist, float radius) { + radius = max(radius, 0.0001); + float costheta = clamp(dot(N, L), -0.999, 0.999); + float h = min(radius / Ldist , 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); + } + + /* Energy conservation + cycle matching */ + bsdf = max(bsdf, 0.0); + bsdf *= M_1_PI; + bsdf *= sphere_energy(radius); + return bsdf; } -float direct_diffuse_rectangle(vec3 N, vec3 L) +/* From Frostbite PBR Course + * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ +float direct_diffuse_rectangle( + vec3 W, vec3 N, vec3 L, + float Ldist, vec3 Lx, vec3 Ly, vec3 Lz, float Lsizex, float Lsizey) { + vec3 lco = L * Ldist; + + /* Surface to corner vectors */ + vec3 p0 = lco + Lx * -Lsizex + Ly * Lsizey; + vec3 p1 = lco + Lx * -Lsizex + Ly * -Lsizey; + vec3 p2 = lco + Lx * Lsizex + Ly * -Lsizey; + vec3 p3 = lco + Lx * Lsizex + Ly * Lsizey; + + float solidAngle = rectangle_solid_angle(p0, p1, p2, p3); + float bsdf = solidAngle * 0.2 * ( + max(0.0, dot(normalize(p0), N)) + + max(0.0, dot(normalize(p1), N)) + + max(0.0, dot(normalize(p2), N)) + + max(0.0, dot(normalize(p3), N)) + + max(0.0, dot(L, N)) + ); + + bsdf *= rectangle_energy(Lsizex * 2.0, Lsizey * 2.0); + + return bsdf; } -#endif /* infinitly far away point source, no decay */ float direct_diffuse_sun(vec3 N, vec3 L) @@ -42,25 +92,55 @@ float direct_diffuse_unit_disc(vec3 N, vec3 L) { } +#endif /* ----------- GGx ------------ */ -float direct_ggx_point(vec3 N, vec3 L) +float direct_ggx_point(vec3 N, vec3 L, vec3 V, float roughness) { - float bsdf = max(0.0, dot(N, L)); - bsdf *= M_1_PI; /* Normalize */ - return bsdf; + return bsdf_ggx(N, L, V, roughness); } -float direct_ggx_sphere(vec3 N, vec3 L) +float direct_ggx_sphere(vec3 N, vec3 L, vec3 V, float Ldist, float radius, float roughness) { + vec3 R = reflect(V, N); + + float energy_conservation = 1.0; + mrp_sphere(radius, Ldist, R, L, roughness, energy_conservation); + float bsdf = bsdf_ggx(N, L, V, roughness); + bsdf *= energy_conservation / (Ldist * Ldist); + bsdf *= sphere_energy(radius) * max(radius * radius, 1e-16); /* radius is already inside energy_conservation */ + bsdf *= M_PI; + + return bsdf; } -float direct_ggx_rectangle(vec3 N, vec3 L) +float direct_ggx_rectangle( + vec3 W, vec3 N, vec3 L, vec3 V, + float Ldist, vec3 Lx, vec3 Ly, vec3 Lz, float Lsizex, float Lsizey, float roughness) { + vec3 lco = L * Ldist; + + /* Surface to corner vectors */ + vec3 p0 = lco + Lx * -Lsizex + Ly * Lsizey; + vec3 p1 = lco + Lx * -Lsizex + Ly * -Lsizey; + vec3 p2 = lco + Lx * Lsizex + Ly * -Lsizey; + vec3 p3 = lco + Lx * Lsizex + Ly * Lsizey; + float solidAngle = rectangle_solid_angle(p0, p1, p2, p3); + + vec3 R = reflect(V, N); + mrp_area(R, N, W, W + lco, Lx, Ly, Lz, Lsizex, Lsizey, Ldist, L); + + float bsdf = bsdf_ggx(N, L, V, roughness) * solidAngle; + + bsdf *= pow(max(0.0, dot(R, Lz)), 0.5); /* fade mrp artifacts */ + bsdf *= rectangle_energy(Lsizex * 2.0, Lsizey * 2.0); + + return bsdf; } +#if 0 float direct_ggx_disc(vec3 N, vec3 L) { diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl index 19498a611e8..f2c7b9579dd 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -1,5 +1,8 @@ uniform int light_count; +uniform vec3 cameraPos; +uniform vec3 eye; +uniform mat4 ProjectionMatrix; struct LightData { vec4 positionAndInfluence; /* w : InfluenceRadius */ @@ -40,18 +43,52 @@ out vec4 fragColor; #define HEMI 3.0 #define AREA 4.0 -vec3 light_diffuse(LightData ld, vec3 N, vec3 W, vec3 color) { - vec3 light, wL, L; +vec3 light_diffuse(LightData ld, vec3 N, vec3 W, vec3 wL, vec3 L, float Ldist, vec3 color) +{ + vec3 light; if (ld.lampType == SUN) { L = -ld.lampForward; light = color * direct_diffuse_sun(N, L) * ld.lampColor; } + else if (ld.lampType == AREA) { + light = color * direct_diffuse_rectangle(W, N, L, Ldist, + ld.lampRight, ld.lampUp, ld.lampForward, + ld.lampSizeX, ld.lampSizeY) * ld.lampColor; + } else { - wL = ld.lampPosition - W; - float dist = length(wL); - light = color * direct_diffuse_point(N, wL / dist, dist) * ld.lampColor; + // light = color * direct_diffuse_point(N, L, Ldist) * ld.lampColor; + light = color * direct_diffuse_sphere(N, L, Ldist, ld.lampSizeX) * ld.lampColor; + } + + return light; +} + +vec3 light_specular( + LightData ld, vec3 V, vec3 N, vec3 W, vec3 wL, + vec3 L, float Ldist, vec3 spec, float roughness) +{ + vec3 light; + + if (ld.lampType == SUN) { + L = -ld.lampForward; + light = spec * direct_ggx_point(N, L, V, roughness) * ld.lampColor; + } + else if (ld.lampType == AREA) { + light = spec * direct_ggx_rectangle(W, N, L, V, Ldist, ld.lampRight, ld.lampUp, ld.lampForward, + ld.lampSizeX, ld.lampSizeY, roughness) * ld.lampColor; } + else { + light = spec * direct_ggx_sphere(N, L, V, Ldist, ld.lampSizeX, roughness) * ld.lampColor; + } + + return light; +} + +float light_visibility( + LightData ld, vec3 V, vec3 N, vec3 W, vec3 wL, vec3 L, float Ldist) +{ + float vis = 1.0; if (ld.lampType == SPOT) { float z = dot(ld.lampForward, wL); @@ -63,28 +100,42 @@ vec3 light_diffuse(LightData ld, vec3 N, vec3 W, vec3 color) { float spotmask = smoothstep(0.0, 1.0, (ellipse - ld.lampSpotSize) / ld.lampSpotBlend); - light *= spotmask; + vis *= spotmask; + } + else if (ld.lampType == AREA) { + vis *= step(0.0, -dot(L, ld.lampForward)); } - return light; + return vis; } -vec3 light_specular(LightData ld, vec3 V, vec3 N, vec3 T, vec3 B, vec3 spec, float roughness) { - vec3 L = normalize(ld.lampPosition - worldPosition); - vec3 light = L; - - return light; -} +void main() +{ + vec3 N = normalize(worldNormal); -void main() { - vec3 n = normalize(worldNormal); - vec3 diffuse = vec3(0.0); + vec3 V; + if (ProjectionMatrix[3][3] == 0.0) { + V = normalize(cameraPos - worldPosition); + } + else { + V = normalize(eye); + } + vec3 radiance = vec3(0.0); vec3 albedo = vec3(1.0, 1.0, 1.0); + vec3 specular = mix(vec3(0.03), vec3(1.0), pow(max(0.0, 1.0 - dot(N,V)), 5.0)); for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) { - diffuse += light_diffuse(lights_data[i], n, worldPosition, albedo); + vec3 wL = lights_data[i].lampPosition - worldPosition; + float dist = length(wL); + vec3 L = wL / dist; + + float vis = light_visibility(lights_data[i], V, N, worldPosition, wL, L, dist); + vec3 spec = light_specular(lights_data[i], V, N, worldPosition, wL, L, dist, vec3(1.0), .2); + vec3 diff = light_diffuse(lights_data[i], N, worldPosition, wL, L, dist, albedo); + + radiance += vis * (diff + spec); } - fragColor = vec4(diffuse,1.0); + fragColor = vec4(radiance, 1.0); }
\ No newline at end of file |