Eevee: LUT generation.

We generate a 3D lut to precompute the btdf intensity.
I decided to use a 64*64*16 (N dot V, ior, roughness) because the btdf varies less with roughness than with IOR.
We also remap the ior to better use the space in the LUT.
This commit is contained in:
Clément Foucault 2017-08-04 18:43:02 +02:00
parent 4ec58659ad
commit 8e36089e41
4 changed files with 152 additions and 2 deletions

View File

@ -155,6 +155,7 @@ data_to_c_simple(engines/eevee/shaders/shadow_store_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_store_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_store_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_lut_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/btdf_lut_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_direct_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_common_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/irradiance_lib.glsl SRC)

View File

@ -326,7 +326,7 @@ static struct GPUTexture *create_jitter_texture(int num_samples)
jitter[i][2] = bn * num_samples_inv;
}
UNUSED_VARS(bsdf_split_sum_ggx, ltc_mag_ggx, ltc_mat_ggx);
UNUSED_VARS(bsdf_split_sum_ggx, btdf_split_sum_ggx, ltc_mag_ggx, ltc_mat_ggx);
return DRW_texture_create_2D(64, 64, DRW_TEX_RGB_16, DRW_TEX_FILTER | DRW_TEX_WRAP, &jitter[0][0]);
}

View File

@ -90,6 +90,7 @@ extern char datatoc_default_frag_glsl[];
extern char datatoc_default_world_frag_glsl[];
extern char datatoc_ltc_lib_glsl[];
extern char datatoc_bsdf_lut_frag_glsl[];
extern char datatoc_btdf_lut_frag_glsl[];
extern char datatoc_bsdf_common_lib_glsl[];
extern char datatoc_bsdf_direct_lib_glsl[];
extern char datatoc_bsdf_sampling_lib_glsl[];
@ -169,8 +170,97 @@ static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h))
return tex;
}
#endif
static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h)
{
struct GPUTexture *tex;
struct GPUTexture *hammersley = create_hammersley_sample_texture(8192);
struct GPUFrameBuffer *fb = NULL;
static float samples_ct = 8192.0f;
static float a2 = 0.0f;
static float inv_samples_ct = 1.0f / 8192.0f;
char *frag_str = NULL;
DynStr *ds_vert = BLI_dynstr_new();
BLI_dynstr_append(ds_vert, datatoc_bsdf_common_lib_glsl);
BLI_dynstr_append(ds_vert, datatoc_bsdf_sampling_lib_glsl);
BLI_dynstr_append(ds_vert, datatoc_btdf_lut_frag_glsl);
frag_str = BLI_dynstr_get_cstring(ds_vert);
BLI_dynstr_free(ds_vert);
struct GPUShader *sh = DRW_shader_create_fullscreen(frag_str,
"#define HAMMERSLEY_SIZE 8192\n"
"#define BRDF_LUT_SIZE 64\n"
"#define NOISE_SIZE 64\n"
"#define LUT_SIZE 64\n");
MEM_freeN(frag_str);
DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_float(grp, "a2", &a2, 1);
DRW_shgroup_uniform_float(grp, "sampleCount", &samples_ct, 1);
DRW_shgroup_uniform_float(grp, "invSampleCount", &inv_samples_ct, 1);
DRW_shgroup_uniform_texture(grp, "texHammersley", hammersley);
DRW_shgroup_uniform_texture(grp, "utilTex", e_data.util_tex);
struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call_add(grp, geom, NULL);
float *texels = MEM_mallocN(sizeof(float[2]) * w * h, "lut");
tex = DRW_texture_create_2D(w, h, DRW_TEX_R_16, DRW_TEX_FILTER, (float *)texels);
DRWFboTexture tex_filter = {&tex, DRW_TEX_R_16, DRW_TEX_FILTER};
DRW_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
DRW_framebuffer_bind(fb);
float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
float inc = 1.0f / 31.0f;
float roughness = 1e-8f - inc;
FILE *f = BLI_fopen("btdf_split_sum_ggx.h", "w");
fprintf(f, "static float btdf_split_sum_ggx[32][64 * 64] = {\n");
do {
roughness += inc;
CLAMP(roughness, 1e-4f, 1.0f);
a2 = powf(roughness, 4.0f);
DRW_draw_pass(pass);
DRW_framebuffer_read_data(0, 0, w, h, 3, 0, data);
#if 1
fprintf(f, "\t{\n\t\t");
for (int i = 0; i < w*h * 3; i+=3) {
fprintf(f, "%ff,", data[i]);
if (((i/3)+1) % 12 == 0) fprintf(f, "\n\t\t");
else fprintf(f, " ");
}
fprintf(f, "\n\t},\n");
#else
for (int i = 0; i < w*h * 3; i+=3) {
if (data[i] < 0.01) printf(" ");
else if (data[i] < 0.3) printf(".");
else if (data[i] < 0.6) printf("+");
else if (data[i] < 0.9) printf("%%");
else printf("#");
if ((i/3+1) % 64 == 0) printf("\n");
}
#endif
} while (roughness < 1.0f);
fprintf(f, "\n};\n");
fclose(f);
MEM_freeN(texels);
MEM_freeN(data);
return tex;
}
#endif
/* XXX TODO define all shared resources in a shared place without duplication */
struct GPUTexture *EEVEE_materials_get_util_tex(void)
{

View File

@ -0,0 +1,59 @@
uniform float a2;
out vec4 FragColor;
void main() {
vec3 N, T, B, V;
float x = gl_FragCoord.x / BRDF_LUT_SIZE;
float y = gl_FragCoord.y / BRDF_LUT_SIZE;
/* There is little variation if ior > 1.0 so we
* maximize LUT precision for ior < 1.0 */
x = x * 1.1;
float ior = (x > 1.0) ? ior_from_f0((x-1.0) * 10.0) : sqrt(x);
float NV = (1.0 - (clamp(y, 1e-4, 0.9999)));
N = vec3(0.0, 0.0, 1.0);
T = vec3(1.0, 0.0, 0.0);
B = vec3(0.0, 1.0, 0.0);
V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
setup_noise();
/* Integrating BTDF */
float btdf_accum = 0.0;
for (float i = 0.0; i < sampleCount; i++) {
vec3 H = sample_ggx(i, a2, N, T, B); /* Microfacet normal */
float VH = dot(V, H);
/* Check if there is total internal reflections. */
float c = abs(VH);
float g = ior * ior - 1.0 + c * c;
float eta = 1.0/ior;
if (dot(H, V) < 0.0) {
H = -H;
eta = ior;
}
vec3 L = refract(-V, H, eta);
float NL = -dot(N, L);
if ((NL > 0.0) && (g > 0.0)) {
float LH = dot(L, H);
float G1_l = NL * 2.0 / G1_Smith_GGX(NL, a2); /* Balancing the adjustments made in G1_Smith */
/* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV)
* pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
float btdf = G1_l * abs(VH*LH) / (VH * abs(LH));
btdf_accum += btdf;
}
}
btdf_accum /= sampleCount;
FragColor = vec4(btdf_accum, 0.0, 0.0, 1.0);
}