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:
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) 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)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels");
float (*texels_layer)[4] = texels; float (*texels_layer)[4] = texels;
@@ -450,12 +450,12 @@ static void eevee_init_util_texture(void)
texels_layer += 64 * 64; texels_layer += 64 * 64;
/* Copy bsdf_split_sum_ggx into 2nd layer red and green channels. /* 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++) { for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = bsdf_split_sum_ggx[i * 2 + 0]; 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][1] = bsdf_split_sum_ggx[i * 2 + 1];
texels_layer[i][2] = ltc_mag_ggx[i]; texels_layer[i][2] = ltc_mag_ggx[i * 2 + 0];
texels_layer[i][3] = ltc_disk_integral[i]; texels_layer[i][3] = ltc_mag_ggx[i * 2 + 1];
} }
texels_layer += 64 * 64; texels_layer += 64 * 64;
@@ -468,13 +468,22 @@ static void eevee_init_util_texture(void)
} }
texels_layer += 64 * 64; 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 j = 0; j < 16; ++j) {
for (int i = 0; i < 64 * 64; i++) { for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = btdf_split_sum_ggx[j * 2][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][1] = 0.0; /* UNUSED */
texels_layer[i][2] = btdf_split_sum_ggx[j * 2][i]; texels_layer[i][2] = 0.0; /* UNUSED */
texels_layer[i][3] = btdf_split_sum_ggx[j * 2][i]; texels_layer[i][3] = 0.0; /* UNUSED */
} }
texels_layer += 64 * 64; 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; 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 -- */ /* -- Tangent Space conversion -- */
vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B) 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) */ /* Fresnel approximation for LTC area lights (not MRP) */
vec3 F_area(vec3 f0, vec2 lut) 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, /* Unreal specular matching : if specular color is below 2% intensity,
* treat as shadowning */ * 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 */ /* Fresnel approximation for IBL */

View File

@@ -198,12 +198,12 @@ void CLOSURE_NAME(
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
#ifdef CLOSURE_GLOSSY #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; vec4 ltc_mat = texture(utilTex, vec3(lut_uv, 0.0)).rgba;
#endif #endif
#ifdef CLOSURE_CLEARCOAT #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; vec4 ltc_mat_clear = texture(utilTex, vec3(lut_uv_clear, 0.0)).rgba;
vec3 out_spec_clear = vec3(0.0); vec3 out_spec_clear = vec3(0.0);
#endif #endif
@@ -241,13 +241,13 @@ void CLOSURE_NAME(
} }
#ifdef CLOSURE_GLOSSY #ifdef CLOSURE_GLOSSY
vec3 brdf_lut_lights = texture(utilTex, vec3(lut_uv, 1.0)).rgb; vec2 brdf_lut_lights = texture(utilTex, vec3(lut_uv, 1.0)).ba;
out_spec *= F_area(f0, brdf_lut_lights.xy) * brdf_lut_lights.z; out_spec *= F_area(f0, brdf_lut_lights.xy);
#endif #endif
#ifdef CLOSURE_CLEARCOAT #ifdef CLOSURE_CLEARCOAT
vec3 brdf_lut_lights_clear = texture(utilTex, vec3(lut_uv_clear, 1.0)).rgb; 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) * brdf_lut_lights_clear.z; out_spec_clear *= F_area(vec3(0.04), brdf_lut_lights_clear.xy);
out_spec += out_spec_clear * C_intensity; out_spec += out_spec_clear * C_intensity;
#endif #endif

View File

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