From 80db7092500abef075d981408cdc75f615ce0a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 18 Apr 2017 21:02:02 +0200 Subject: [PATCH] Eevee: Spherical Harmonic diffuse. For now it's done each frame and it's rather slow (16ms) but result will be cached soon. --- source/blender/draw/CMakeLists.txt | 1 + source/blender/draw/engines/eevee/eevee.c | 40 +++++-- .../draw/engines/eevee/eevee_private.h | 7 +- .../blender/draw/engines/eevee/eevee_probes.c | 12 +- .../eevee/shaders/bsdf_common_lib.glsl | 35 ++++++ .../eevee/shaders/lit_surface_frag.glsl | 5 +- .../engines/eevee/shaders/probe_sh_frag.glsl | 106 ++++++++++++++++++ source/blender/draw/intern/DRW_render.h | 1 + source/blender/draw/intern/draw_manager.c | 15 +++ 9 files changed, 207 insertions(+), 15 deletions(-) create mode 100644 source/blender/draw/engines/eevee/shaders/probe_sh_frag.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 22acc98a5ca..75dc9cadb60 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -105,6 +105,7 @@ data_to_c_simple(engines/clay/shaders/ssao_groundtruth.glsl SRC) data_to_c_simple(engines/eevee/shaders/lit_surface_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lit_surface_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/probe_filter_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/probe_sh_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/probe_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/probe_geom.glsl SRC) data_to_c_simple(engines/eevee/shaders/probe_vert.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee.c b/source/blender/draw/engines/eevee/eevee.c index acfc51f83de..b496344478a 100644 --- a/source/blender/draw/engines/eevee/eevee.c +++ b/source/blender/draw/engines/eevee/eevee.c @@ -44,6 +44,7 @@ static struct { /* Temp : use world shader */ struct GPUShader *probe_sh; struct GPUShader *probe_filter_sh; + struct GPUShader *probe_spherical_harmonic_sh; struct GPUTexture *ltc_mat; struct GPUTexture *brdf_lut; @@ -65,6 +66,7 @@ extern char datatoc_shadow_frag_glsl[]; extern char datatoc_shadow_geom_glsl[]; extern char datatoc_shadow_vert_glsl[]; extern char datatoc_probe_filter_frag_glsl[]; +extern char datatoc_probe_sh_frag_glsl[]; extern char datatoc_probe_frag_glsl[]; extern char datatoc_probe_geom_glsl[]; extern char datatoc_probe_vert_glsl[]; @@ -238,19 +240,24 @@ static void EEVEE_engine_init(void *ved) } if (!e_data.probe_filter_sh) { - char *lib_str = NULL; + char *shader_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); - lib_str = BLI_dynstr_get_cstring(ds_vert); - BLI_dynstr_free(ds_vert); + DynStr *ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_bsdf_sampling_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_probe_filter_frag_glsl); + shader_str = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); - e_data.probe_filter_sh = DRW_shader_create_with_lib(datatoc_probe_vert_glsl, datatoc_probe_geom_glsl, datatoc_probe_filter_frag_glsl, lib_str, - "#define HAMMERSLEY_SIZE 8192\n" - "#define NOISE_SIZE 64\n"); + e_data.probe_filter_sh = DRW_shader_create(datatoc_probe_vert_glsl, datatoc_probe_geom_glsl, shader_str, + "#define HAMMERSLEY_SIZE 8192\n" + "#define NOISE_SIZE 64\n"); - MEM_freeN(lib_str); + MEM_freeN(shader_str); + } + + if (!e_data.probe_spherical_harmonic_sh) { + e_data.probe_spherical_harmonic_sh = DRW_shader_create_fullscreen(datatoc_probe_sh_frag_glsl, NULL); } if (!e_data.tonemap) { @@ -340,6 +347,17 @@ static void EEVEE_cache_init(void *vedata) DRW_shgroup_uniform_texture(grp, "probeHdr", txl->probe_rt, 3); } + { + psl->probe_sh_compute = DRW_pass_create("Probe SH Compute", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_spherical_harmonic_sh, psl->probe_sh_compute); + DRW_shgroup_uniform_int(grp, "probeSize", &stl->probes->shres, 1); + DRW_shgroup_uniform_texture(grp, "probeHdr", txl->probe_rt, 0); + + struct Batch *geom = DRW_cache_fullscreen_quad_get(); + DRW_shgroup_call_add(grp, geom, NULL); + } + { psl->depth_pass = DRW_pass_create("Depth Pass", DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.depth_sh, psl->depth_pass); @@ -357,6 +375,7 @@ static void EEVEE_cache_init(void *vedata) DRW_shgroup_uniform_block(stl->g_data->default_lit_grp, "shadow_block", stl->shadow_ubo, 1); DRW_shgroup_uniform_int(stl->g_data->default_lit_grp, "light_count", &stl->lamps->num_light, 1); DRW_shgroup_uniform_float(stl->g_data->default_lit_grp, "lodMax", &stl->probes->lodmax, 1); + DRW_shgroup_uniform_vec3(stl->g_data->default_lit_grp, "shCoefs[0]", (float *)stl->probes->shcoefs, 9); DRW_shgroup_uniform_vec3(stl->g_data->default_lit_grp, "cameraPos", e_data.camera_pos, 1); DRW_shgroup_uniform_texture(stl->g_data->default_lit_grp, "ltcMat", e_data.ltc_mat, 0); DRW_shgroup_uniform_texture(stl->g_data->default_lit_grp, "brdfLut", e_data.brdf_lut, 1); @@ -455,6 +474,7 @@ static void EEVEE_engine_free(void) DRW_SHADER_FREE_SAFE(e_data.shadow_sh); DRW_SHADER_FREE_SAFE(e_data.probe_sh); DRW_SHADER_FREE_SAFE(e_data.probe_filter_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_spherical_harmonic_sh); DRW_SHADER_FREE_SAFE(e_data.tonemap); DRW_TEXTURE_FREE_SAFE(e_data.ltc_mat); DRW_TEXTURE_FREE_SAFE(e_data.brdf_lut); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index c98e0b6529b..8be09091df8 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -39,7 +39,7 @@ typedef struct EEVEE_PassList { /* Probes */ struct DRWPass *probe_background; struct DRWPass *probe_prefilter; - struct DRWPass *probe_sh; + struct DRWPass *probe_sh_compute; struct DRWPass *depth_pass; struct DRWPass *depth_pass_cull; @@ -69,6 +69,7 @@ typedef struct EEVEE_TextureList { struct GPUTexture *probe_rt; /* R16_G16_B16 */ struct GPUTexture *probe_depth_rt; struct GPUTexture *probe_pool; /* R11_G11_B10 */ + struct GPUTexture *probe_sh; /* R16_G16_B16 */ struct GPUTexture *color; /* R16_G16_B16 */ } EEVEE_TextureList; @@ -82,6 +83,7 @@ typedef struct EEVEE_StorageList { /* Probes */ struct EEVEE_ProbesInfo *probes; + struct GPUUniformBuffer *probe_ubo; struct g_data *g_data; } EEVEE_StorageList; @@ -144,6 +146,9 @@ typedef struct EEVEE_ProbesInfo { float roughness; float lodfactor; float lodmax; + int shres; + int shnbr; + float shcoefs[9][3]; /* Temp */ struct GPUTexture *backgroundtex; } EEVEE_ProbesInfo; diff --git a/source/blender/draw/engines/eevee/eevee_probes.c b/source/blender/draw/engines/eevee/eevee_probes.c index 78e1a0cf52f..809e175938e 100644 --- a/source/blender/draw/engines/eevee/eevee_probes.c +++ b/source/blender/draw/engines/eevee/eevee_probes.c @@ -98,6 +98,11 @@ void EEVEE_probes_init(EEVEE_Data *vedata) DRWFboTexture tex_filter = {&txl->probe_pool, DRW_BUF_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}; DRW_framebuffer_init(&fbl->probe_filter_fb, PROBE_SIZE, PROBE_SIZE, &tex_filter, 1); + + /* Spherical Harmonic Buffer */ + DRWFboTexture tex_sh = {&txl->probe_sh, DRW_BUF_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}; + + DRW_framebuffer_init(&fbl->probe_sh_fb, 9, 1, &tex_sh, 1); } void EEVEE_probes_cache_init(EEVEE_Data *UNUSED(vedata)) @@ -203,7 +208,8 @@ void EEVEE_refresh_probe(EEVEE_Data *vedata) DRW_framebuffer_texture_attach(fbl->probe_filter_fb, txl->probe_pool, 0, 0); /* 4 - Compute spherical harmonics */ - /* TODO */ - // DRW_framebuffer_bind(fbl->probe_sh_fb); - // DRW_draw_pass(psl->probe_sh); + pinfo->shres = 64; + DRW_framebuffer_bind(fbl->probe_sh_fb); + DRW_draw_pass(psl->probe_sh_compute); + DRW_framebuffer_read_data(0, 0, 9, 1, 3, 0, (float *)pinfo->shcoefs); } 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 0f47a341764..b907adc2323 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -173,6 +173,41 @@ float buffer_depth(bool is_persp, float z, float zf, float zn) } } +#define spherical_harmonics spherical_harmonics_L2 + +/* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */ +vec3 spherical_harmonics_L1(vec3 N, vec3 shcoefs[9]) +{ + vec3 sh = vec3(0.0); + + sh += 0.282095 * shcoefs[0]; + + sh += -0.488603 * N.z * shcoefs[1]; + sh += 0.488603 * N.y * shcoefs[2]; + sh += -0.488603 * N.x * shcoefs[3]; + + return sh; +} + +vec3 spherical_harmonics_L2(vec3 N, vec3 shcoefs[9]) +{ + vec3 sh = vec3(0.0); + + sh += 0.282095 * shcoefs[0]; + + sh += -0.488603 * N.z * shcoefs[1]; + sh += 0.488603 * N.y * shcoefs[2]; + sh += -0.488603 * N.x * shcoefs[3]; + + sh += 1.092548 * N.x * N.z * shcoefs[4]; + sh += -1.092548 * N.z * N.y * shcoefs[5]; + sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * shcoefs[6]; + sh += -1.092548 * N.x * N.y * shcoefs[7]; + sh += 0.546274 * (N.x * N.x - N.z * N.z) * shcoefs[8]; + + return sh; +} + float rectangle_solid_angle(AreaData ad) { vec3 n0 = normalize(cross(ad.corner[0], ad.corner[1])); 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 598d34941eb..189cdcf10fb 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -6,6 +6,7 @@ uniform mat4 ProjectionMatrix; uniform samplerCube probeFiltered; uniform float lodMax; +uniform vec3 shCoefs[9]; #ifndef USE_LTC uniform sampler2D brdfLut; @@ -198,7 +199,7 @@ void main() sd.R = reflect(-sd.V, sd.N); /* hardcoded test vars */ - vec3 albedo = vec3(0.0); + vec3 albedo = mix(vec3(0.0, 0.0, 0.0), vec3(0.8, 0.8, 0.8), saturate(worldPosition.y/2)); vec3 f0 = mix(vec3(0.83, 0.5, 0.1), vec3(0.03, 0.03, 0.03), saturate(worldPosition.y/2)); vec3 specular = mix(f0, vec3(1.0), pow(max(0.0, 1.0 - dot(sd.N, sd.V)), 5.0)); float roughness = saturate(worldPosition.x/lodMax); @@ -229,5 +230,7 @@ void main() vec3 Li = textureLod(probeFiltered, sd.spec_dominant_dir, roughness * lodMax).rgb; radiance += Li * brdf_lut.y + f0 * Li * brdf_lut.x; + radiance += spherical_harmonics(sd.N, shCoefs) * albedo; + fragColor = vec4(radiance, 1.0); } \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/probe_sh_frag.glsl b/source/blender/draw/engines/eevee/shaders/probe_sh_frag.glsl new file mode 100644 index 00000000000..068a5bbf8b2 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/probe_sh_frag.glsl @@ -0,0 +1,106 @@ + +uniform samplerCube probeHdr; +uniform int probeSize; + +in vec3 worldPosition; + +out vec4 FragColor; + +#define M_4PI 12.5663706143591729 + +const mat3 CUBE_ROTATIONS[6] = mat3[]( + mat3(vec3( 0.0, 0.0, -1.0), + vec3( 0.0, -1.0, 0.0), + vec3(-1.0, 0.0, 0.0)), + mat3(vec3( 0.0, 0.0, 1.0), + vec3( 0.0, -1.0, 0.0), + vec3( 1.0, 0.0, 0.0)), + mat3(vec3( 1.0, 0.0, 0.0), + vec3( 0.0, 0.0, 1.0), + vec3( 0.0, -1.0, 0.0)), + mat3(vec3( 1.0, 0.0, 0.0), + vec3( 0.0, 0.0, -1.0), + vec3( 0.0, 1.0, 0.0)), + mat3(vec3( 1.0, 0.0, 0.0), + vec3( 0.0, -1.0, 0.0), + vec3( 0.0, 0.0, -1.0)), + mat3(vec3(-1.0, 0.0, 0.0), + vec3( 0.0, -1.0, 0.0), + vec3( 0.0, 0.0, 1.0))); + +vec3 get_cubemap_vector(vec2 co, int face) +{ + return normalize(CUBE_ROTATIONS[face] * vec3(co * 2.0 - 1.0, 1.0)); +} + +float area_element(float x, float y) +{ + return atan(x * y, sqrt(x * x + y * y + 1)); +} + +float texel_solid_angle(vec2 co, float halfpix) +{ + vec2 v1 = (co - vec2(halfpix)) * 2.0 - 1.0; + vec2 v2 = (co + vec2(halfpix)) * 2.0 - 1.0; + + return area_element(v1.x, v1.y) - area_element(v1.x, v2.y) - area_element(v2.x, v1.y) + area_element(v2.x, v2.y); +} + +void main() +{ + float pixstep = 1.0 / probeSize; + float halfpix = pixstep / 2.0; + + float weight_accum = 0.0; + vec3 sh = vec3(0.0); + + int shnbr = int(floor(gl_FragCoord.x)); + + for (int face = 0; face < 6; ++face) { + for (float x = halfpix; x < 1.0; x += pixstep) { + for (float y = halfpix; y < 1.0; y += pixstep) { + float shcoef; + + vec2 facecoord = vec2(x,y); + vec3 cubevec = get_cubemap_vector(facecoord, face); + float weight = texel_solid_angle(facecoord, halfpix); + + if (shnbr == 0) { + shcoef = 0.282095; + } + else if (shnbr == 1) { + shcoef = -0.488603 * cubevec.z * 2.0 / 3.0; + } + else if (shnbr == 2) { + shcoef = 0.488603 * cubevec.y * 2.0 / 3.0; + } + else if (shnbr == 3) { + shcoef = -0.488603 * cubevec.x * 2.0 / 3.0; + } + else if (shnbr == 4) { + shcoef = 1.092548 * cubevec.x * cubevec.z * 1.0 / 4.0; + } + else if (shnbr == 5) { + shcoef = -1.092548 * cubevec.z * cubevec.y * 1.0 / 4.0; + } + else if (shnbr == 6) { + shcoef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0; + } + else if (shnbr == 7) { + shcoef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0; + } + else { /* (shnbr == 8) */ + shcoef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0; + } + + vec4 sample = textureCube(probeHdr, cubevec); + sh += sample.rgb * shcoef * weight; + weight_accum += weight; + } + } + } + + sh *= M_4PI / weight_accum; + + gl_FragColor = vec4(sh, 1.0); +} \ No newline at end of file diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 54ab5ee9002..b4baa6d7b4c 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -193,6 +193,7 @@ typedef struct DRWFboTexture { void DRW_framebuffer_init(struct GPUFrameBuffer **fb, int width, int height, DRWFboTexture textures[MAX_FBO_TEX], int texnbr); void DRW_framebuffer_bind(struct GPUFrameBuffer *fb); void DRW_framebuffer_clear(bool color, bool depth, bool stencil, float clear_col[4], float clear_depth); +void DRW_framebuffer_read_data(int x, int y, int w, int h, int channels, int slot, float *data); void DRW_framebuffer_texture_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip); void DRW_framebuffer_texture_detach(struct GPUTexture *tex); void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer *fb_write, bool depth); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index f648a8dfe50..50e5b541290 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1302,6 +1302,21 @@ void DRW_framebuffer_clear(bool color, bool depth, bool stencil, float clear_col ((stencil) ? GL_STENCIL_BUFFER_BIT : 0)); } +void DRW_framebuffer_read_data(int x, int y, int w, int h, int channels, int slot, float *data) +{ + GLenum type; + switch (channels) { + case 1: type = GL_RED; break; + case 2: type = GL_RG; break; + case 3: type = GL_RGB; break; + case 4: type = GL_RGBA; break; + default: + BLI_assert(false && "wrong number of read channels"); + } + glReadBuffer(GL_COLOR_ATTACHMENT0 + slot); + glReadPixels(x, y, w, h, type, GL_FLOAT, data); +} + void DRW_framebuffer_texture_attach(struct GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip) { GPU_framebuffer_texture_attach(fb, tex, slot, mip);