Eevee: Update LTC code

Main change are:
- the fresnel LUT is separated from the main GGX LUT.
- LUTs use sqrt(1.0 - NV) as roughness remapping. Improving precision and
  removes needs for acos().
- LTC LUT is normalized by matrix middle component. Improving precision.
This commit is contained in:
Clément Foucault 2019-03-11 13:11:46 +01:00
parent 881782213d
commit a808b58e07
5 changed files with 3118 additions and 2326 deletions

File diff suppressed because it is too large Load Diff

View File

@ -441,7 +441,7 @@ static void eevee_init_noise_texture(void)
static void eevee_init_util_texture(void)
{
const int layers = 3 + 16;
const int layers = 4 + 16;
float (*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels");
float (*texels_layer)[4] = texels;
@ -450,12 +450,12 @@ static void eevee_init_util_texture(void)
texels_layer += 64 * 64;
/* Copy bsdf_split_sum_ggx into 2nd layer red and green channels.
Copy ltc_mag_ggx into 2nd layer blue channel. */
Copy ltc_mag_ggx into 2nd layer blue and alpha channel. */
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = bsdf_split_sum_ggx[i * 2 + 0];
texels_layer[i][1] = bsdf_split_sum_ggx[i * 2 + 1];
texels_layer[i][2] = ltc_mag_ggx[i];
texels_layer[i][3] = ltc_disk_integral[i];
texels_layer[i][2] = ltc_mag_ggx[i * 2 + 0];
texels_layer[i][3] = ltc_mag_ggx[i * 2 + 1];
}
texels_layer += 64 * 64;
@ -468,13 +468,22 @@ static void eevee_init_util_texture(void)
}
texels_layer += 64 * 64;
/* Copy Refraction GGX LUT in layer 4 - 20 */
/* Copy ltc_disk_integral in 4th layer */
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = ltc_disk_integral[i];
texels_layer[i][1] = 0.0; /* UNUSED */
texels_layer[i][2] = 0.0; /* UNUSED */
texels_layer[i][3] = 0.0; /* UNUSED */
}
texels_layer += 64 * 64;
/* Copy Refraction GGX LUT in layer 5 - 21 */
for (int j = 0; j < 16; ++j) {
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = btdf_split_sum_ggx[j * 2][i];
texels_layer[i][1] = btdf_split_sum_ggx[j * 2][i];
texels_layer[i][2] = btdf_split_sum_ggx[j * 2][i];
texels_layer[i][3] = btdf_split_sum_ggx[j * 2][i];
texels_layer[i][1] = 0.0; /* UNUSED */
texels_layer[i][2] = 0.0; /* UNUSED */
texels_layer[i][3] = 0.0; /* UNUSED */
}
texels_layer += 64 * 64;
}

View File

@ -272,6 +272,14 @@ vec2 lut_coords(float cosTheta, float roughness)
return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
}
vec2 lut_coords_ltc(float cosTheta, float roughness)
{
vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta));
/* scale and bias coordinates, for correct filtered lookup */
return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
}
/* -- Tangent Space conversion -- */
vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B)
{
@ -572,11 +580,9 @@ vec3 F_schlick(vec3 f0, float cos_theta)
/* Fresnel approximation for LTC area lights (not MRP) */
vec3 F_area(vec3 f0, vec2 lut)
{
vec2 fac = normalize(lut.xy); /* XXX FIXME this does not work!!! */
/* Unreal specular matching : if specular color is below 2% intensity,
* treat as shadowning */
return saturate(50.0 * dot(f0, vec3(0.3, 0.6, 0.1))) * fac.y + fac.x * f0;
return saturate(50.0 * dot(f0, vec3(0.3, 0.6, 0.1))) * lut.y + lut.x * f0;
}
/* Fresnel approximation for IBL */

View File

@ -198,12 +198,12 @@ void CLOSURE_NAME(
/* ---------------------------------------------------------------- */
#ifdef CLOSURE_GLOSSY
vec2 lut_uv = lut_coords(dot(N, V), roughness);
vec2 lut_uv = lut_coords_ltc(dot(N, V), roughness);
vec4 ltc_mat = texture(utilTex, vec3(lut_uv, 0.0)).rgba;
#endif
#ifdef CLOSURE_CLEARCOAT
vec2 lut_uv_clear = lut_coords(dot(C_N, V), C_roughness);
vec2 lut_uv_clear = lut_coords_ltc(dot(C_N, V), C_roughness);
vec4 ltc_mat_clear = texture(utilTex, vec3(lut_uv_clear, 0.0)).rgba;
vec3 out_spec_clear = vec3(0.0);
#endif
@ -241,13 +241,13 @@ void CLOSURE_NAME(
}
#ifdef CLOSURE_GLOSSY
vec3 brdf_lut_lights = texture(utilTex, vec3(lut_uv, 1.0)).rgb;
out_spec *= F_area(f0, brdf_lut_lights.xy) * brdf_lut_lights.z;
vec2 brdf_lut_lights = texture(utilTex, vec3(lut_uv, 1.0)).ba;
out_spec *= F_area(f0, brdf_lut_lights.xy);
#endif
#ifdef CLOSURE_CLEARCOAT
vec3 brdf_lut_lights_clear = texture(utilTex, vec3(lut_uv_clear, 1.0)).rgb;
out_spec_clear *= F_area(vec3(0.04), brdf_lut_lights_clear.xy) * brdf_lut_lights_clear.z;
vec2 brdf_lut_lights_clear = texture(utilTex, vec3(lut_uv_clear, 1.0)).ba;
out_spec_clear *= F_area(vec3(0.04), brdf_lut_lights_clear.xy);
out_spec += out_spec_clear * C_intensity;
#endif

View File

@ -15,17 +15,18 @@ uniform sampler2DArray utilTex;
#endif /* UTIL_TEX */
/* Diffuse *clipped* sphere integral. */
float diffuse_sphere_integral_lut(float avg_dir_z, float form_factor)
float diffuse_sphere_integral(float avg_dir_z, float form_factor)
{
#if 1
/* use tabulated horizon-clipped sphere */
vec2 uv = vec2(avg_dir_z * 0.5 + 0.5, form_factor);
uv = uv * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
return texture(utilTex, vec3(uv, 1.0)).w;
}
float diffuse_sphere_integral_cheap(float avg_dir_z, float form_factor)
{
return texture(utilTex, vec3(uv, 3.0)).x;
#else
/* Cheap approximation. Less smooth and have energy issues. */
return max((form_factor * form_factor + avg_dir_z) / (form_factor + 1.0), 0.0);
#endif
}
/**
@ -143,9 +144,9 @@ 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)
vec3(lut.x, 0, lut.y),
vec3( 0, 1, 0),
vec3(lut.z, 0, lut.w)
);
return Minv;
@ -185,12 +186,7 @@ float ltc_evaluate_quad(vec3 corners[4], vec3 N)
float form_factor = length(avg_dir);
float avg_dir_z = dot(N, avg_dir / form_factor);
#if 1 /* use tabulated horizon-clipped sphere */
return form_factor * diffuse_sphere_integral_lut(avg_dir_z, form_factor);
#else /* Less accurate version, a bit cheaper. */
return form_factor * diffuse_sphere_integral_cheap(avg_dir_z, form_factor);
#endif
return form_factor * diffuse_sphere_integral(avg_dir_z, form_factor);
}
/* If disk does not need to be transformed and is already front facing. */
@ -199,12 +195,7 @@ float ltc_evaluate_disk_simple(float disk_radius, float NL)
float r_sqr = disk_radius * disk_radius;
float one_r_sqr = 1.0 + r_sqr;
float form_factor = r_sqr * inversesqrt(one_r_sqr * one_r_sqr);
#if 1 /* use tabulated horizon-clipped sphere */
return form_factor * diffuse_sphere_integral_lut(NL, form_factor);
#else /* Less accurate version, a bit cheaper. */
return form_factor * diffuse_sphere_integral_cheap(NL, form_factor);
#endif
return form_factor * diffuse_sphere_integral(NL, form_factor);
}
/* disk_points are WS vectors from the shading point to the disk "bounding domain" */
@ -315,10 +306,5 @@ float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3])
/* Find the sphere and compute lighting. */
float form_factor = max(0.0, L1 * L2 * inversesqrt((1.0 + L1 * L1) * (1.0 + L2 * L2)));
#if 1 /* use tabulated horizon-clipped sphere */
return form_factor * diffuse_sphere_integral_lut(avg_dir.z, form_factor);
#else /* Less accurate version, a bit cheaper. */
return form_factor * diffuse_sphere_integral_cheap(avg_dir.z, form_factor);
#endif
return form_factor * diffuse_sphere_integral(avg_dir.z, form_factor);
}