diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-06-17 01:08:03 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-06-19 11:47:56 +0300 |
commit | 3e4b9d2b5a6479ef71cdedf980bcd714f4f94d56 (patch) | |
tree | 52e63a7ebc87562638a778446540023645050001 | |
parent | 5c67ac2236581532c4ce13448db457d33dd4e863 (diff) |
Eevee: Initial implementation of planar reflections.
Still pretty barebone: No roughness support, No normal distortion support.
7 files changed, 352 insertions, 17 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index 9dfc3e5f07b..da1f5317dab 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -54,6 +54,7 @@ static void eevee_scene_layer_data_free(void *storage) MEM_SAFE_FREE(sldata->probes); DRW_UBO_FREE_SAFE(sldata->probe_ubo); DRW_UBO_FREE_SAFE(sldata->grid_ubo); + DRW_UBO_FREE_SAFE(sldata->planar_ubo); DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_fb); DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_filter_fb); DRW_TEXTURE_FREE_SAFE(sldata->probe_rt); diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index c13351be43c..325758f4eb9 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -57,7 +57,7 @@ static void EEVEE_engine_init(void *ved) EEVEE_materials_init(); EEVEE_lights_init(sldata); - EEVEE_lightprobes_init(sldata); + EEVEE_lightprobes_init(sldata, vedata); EEVEE_effects_init(vedata); } @@ -134,7 +134,7 @@ static void EEVEE_draw_scene(void *vedata) EEVEE_draw_shadows(sldata, psl); /* Refresh Probes */ - EEVEE_lightprobes_refresh(sldata, psl); + EEVEE_lightprobes_refresh(sldata, vedata); /* Attach depth to the hdr buffer and bind it */ DRW_framebuffer_texture_detach(dtxl->depth); diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index bfb1b43e28b..a58c338b14b 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -59,6 +59,7 @@ static struct { struct GPUShader *probe_cube_display_sh; struct GPUTexture *hammersley; + struct GPUTexture *planar_depth; bool update_world; bool world_ready_to_shade; @@ -112,7 +113,46 @@ static struct GPUTexture *create_hammersley_sample_texture(int samples) return tex; } -void EEVEE_lightprobes_init(EEVEE_SceneLayerData *sldata) +static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref) +{ + /* XXX TODO OPTIMISATION : This is a complete waist of texture memory. + * Instead of allocating each planar probe for each viewport, + * only alloc them once using the biggest viewport resolution. */ + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_TextureList *txl = vedata->txl; + + const float *viewport_size = DRW_viewport_size_get(); + + /* TODO get screen percentage from layer setting */ + // const DRWContextState *draw_ctx = DRW_context_state_get(); + // SceneLayer *sl = draw_ctx->sl; + float screen_percentage = 1.0f; + + int width = (int)(viewport_size[0] * screen_percentage); + int height = (int)(viewport_size[1] * screen_percentage); + + /* We need an Array texture so allocate it ourself */ + if (!txl->planar_pool && (num_planar_ref > 0)) { + txl->planar_pool = DRW_texture_create_2D_array(width, height, max_ff(1, num_planar_ref), + DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + } + else if (txl->planar_pool && (num_planar_ref == 0)) { + DRW_TEXTURE_FREE_SAFE(txl->planar_pool); + } + + if (num_planar_ref > 0) { + /* NOTE : Depth buffer is 2D but the planar_pool tex is 2D array. + * DRW_framebuffer_init binds the whole texture making the framebuffer invalid. + * To overcome this, we bind the planar pool ourselves later */ + + DRWFboTexture tex = {&e_data.planar_depth, DRW_TEX_DEPTH_24, DRW_TEX_TEMP}; + + DRW_framebuffer_init(&fbl->planarref_fb, &draw_engine_eevee_type, + width, height, &tex, 1); + } +} + +void EEVEE_lightprobes_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *UNUSED(vedata)) { /* Shaders */ if (!e_data.probe_filter_glossy_sh) { @@ -195,6 +235,7 @@ void EEVEE_lightprobes_init(EEVEE_SceneLayerData *sldata) sldata->probes->specular_toggle = true; sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL); sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL); + sldata->planar_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR, NULL); } /* Setup Render Target Cubemap */ @@ -215,8 +256,10 @@ void EEVEE_lightprobes_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList * pinfo->num_cube = 1; /* at least one for the world */ pinfo->num_grid = 1; + pinfo->num_planar = 0; memset(pinfo->probes_cube_ref, 0, sizeof(pinfo->probes_cube_ref)); memset(pinfo->probes_grid_ref, 0, sizeof(pinfo->probes_grid_ref)); + memset(pinfo->probes_planar_ref, 0, sizeof(pinfo->probes_planar_ref)); { psl->probe_background = DRW_pass_create("World Probe Pass", DRW_STATE_WRITE_COLOR); @@ -356,17 +399,122 @@ void EEVEE_lightprobes_cache_add(EEVEE_SceneLayerData *sldata, Object *ob) pinfo->probes_cube_ref[pinfo->num_cube] = ob; pinfo->num_cube++; } + else if (probe->type == LIGHTPROBE_TYPE_PLANAR) { + pinfo->probes_planar_ref[pinfo->num_planar] = ob; + pinfo->num_planar++; + } else { /* GRID */ pinfo->probes_grid_ref[pinfo->num_grid] = ob; pinfo->num_grid++; } } +/* TODO find a nice name to push it to math_matrix.c */ +static void scale_m4_v3(float R[4][4], float v[3]) +{ + for (int i = 0; i < 4; ++i) + mul_v3_v3(R[i], v); +} + +static void EEVEE_planar_reflections_updates(EEVEE_SceneLayerData *sldata) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + Object *ob; + float mtx[4][4], normat[4][4], imat[4][4], rangemat[4][4]; + + float viewmat[4][4], winmat[4][4]; + DRW_viewport_matrix_get(viewmat, DRW_MAT_VIEW); + DRW_viewport_matrix_get(winmat, DRW_MAT_WIN); + + zero_m4(rangemat); + rangemat[0][0] = rangemat[1][1] = rangemat[2][2] = 0.5f; + rangemat[3][0] = rangemat[3][1] = rangemat[3][2] = 0.5f; + rangemat[3][3] = 1.0f; + + /* PLANAR REFLECTION */ + for (int i = 0; (ob = pinfo->probes_planar_ref[i]) && (i < MAX_PLANAR); i++) { + LightProbe *probe = (LightProbe *)ob->data; + EEVEE_PlanarReflection *eplanar = &pinfo->planar_data[i]; + EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(ob); + + /* Computing mtx : matrix that mirror position around object's XY plane. */ + normalize_m4_m4(normat, ob->obmat); /* object > world */ + invert_m4_m4(imat, normat); /* world > object */ + + float reflect[3] = {1.0f, 1.0f, -1.0f}; /* XY reflection plane */ + scale_m4_v3(imat, reflect); /* world > object > mirrored obj */ + mul_m4_m4m4(mtx, normat, imat); /* world > object > mirrored obj > world */ + + /* Reflect Camera Matrix. */ + mul_m4_m4m4(ped->viewmat, viewmat, mtx); + + /* TODO FOV margin */ + float winmat_fov[4][4]; + copy_m4_m4(winmat_fov, winmat); + + /* Apply Perspective Matrix. */ + mul_m4_m4m4(ped->persmat, winmat_fov, ped->viewmat); + + /* This is the matrix used to reconstruct texture coordinates. + * We use the original view matrix because it does not create + * visual artifacts if receiver is not perfectly aligned with + * the planar reflection probe. */ + mul_m4_m4m4(eplanar->reflectionmat, winmat_fov, viewmat); /* TODO FOV margin */ + /* Convert from [-1, 1] to [0, 1] (NDC to Texture coord). */ + mul_m4_m4m4(eplanar->reflectionmat, rangemat, eplanar->reflectionmat); + + /* TODO frustum check. */ + ped->need_update = true; + + /* Compute clip plane equation / normal. */ + float refpoint[3]; + copy_v3_v3(eplanar->plane_equation, ob->obmat[2]); + normalize_v3(eplanar->plane_equation); /* plane normal */ + mul_v3_v3fl(refpoint, eplanar->plane_equation, -probe->clipsta); + add_v3_v3(refpoint, ob->obmat[3]); + eplanar->plane_equation[3] = -dot_v3v3(eplanar->plane_equation, refpoint); + + /* Compute XY clip planes. */ + normalize_v3_v3(eplanar->clip_vec_x, ob->obmat[0]); + normalize_v3_v3(eplanar->clip_vec_y, ob->obmat[1]); + + float vec[3] = {0.0f, 0.0f, 0.0f}; + vec[0] = 1.0f; vec[1] = 0.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_x_pos = dot_v3v3(eplanar->clip_vec_x, vec); + + vec[0] = 0.0f; vec[1] = 1.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_y_pos = dot_v3v3(eplanar->clip_vec_y, vec); + + vec[0] = -1.0f; vec[1] = 0.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_x_neg = dot_v3v3(eplanar->clip_vec_x, vec); + + vec[0] = 0.0f; vec[1] = -1.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec); + + /* Facing factors */ + float max_angle = max_ff(1e-2f, probe->falloff) * M_PI * 0.5f; + float min_angle = 0.0f; + eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle)); + eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale; + + /* Distance factors */ + float max_dist = probe->distinf; + float min_dist = min_ff(1.0f - 1e-8f, 1.0f - probe->falloff) * probe->distinf; + eplanar->attenuation_scale = -1.0f / max_ff(1e-8f, max_dist - min_dist); + eplanar->attenuation_bias = max_dist * -eplanar->attenuation_scale; + } +} + static void EEVEE_lightprobes_updates(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, EEVEE_StorageList *stl) { EEVEE_LightProbesInfo *pinfo = sldata->probes; Object *ob; + /* CUBE REFLECTION */ for (int i = 1; (ob = pinfo->probes_cube_ref[i]) && (i < MAX_PROBE); i++) { LightProbe *probe = (LightProbe *)ob->data; EEVEE_LightProbe *eprobe = &pinfo->probe_data[i]; @@ -406,6 +554,7 @@ static void EEVEE_lightprobes_updates(EEVEE_SceneLayerData *sldata, EEVEE_PassLi } } + /* IRRADIANCE GRID */ int offset = 1; /* to account for the world probe */ for (int i = 1; (ob = pinfo->probes_grid_ref[i]) && (i < MAX_GRID); i++) { LightProbe *probe = (LightProbe *)ob->data; @@ -485,6 +634,14 @@ void EEVEE_lightprobes_cache_finish(EEVEE_SceneLayerData *sldata, EEVEE_Data *ve DRW_TEXTURE_FREE_SAFE(sldata->probe_pool); } + if (pinfo->num_planar != pinfo->cache_num_planar) { + DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_pool); + pinfo->cache_num_planar = pinfo->num_planar; + } + + /* XXX this should be run each frame as it ensure planar_depth is set */ + planar_pool_ensure_alloc(vedata, pinfo->num_planar); + if (!sldata->probe_pool) { sldata->probe_pool = DRW_texture_create_2D_array(PROBE_OCTAHEDRON_SIZE, PROBE_OCTAHEDRON_SIZE, max_ff(1, pinfo->num_cube), DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); @@ -511,6 +668,7 @@ void EEVEE_lightprobes_cache_finish(EEVEE_SceneLayerData *sldata, EEVEE_Data *ve DRW_framebuffer_init(&sldata->probe_filter_fb, &draw_engine_eevee_type, PROBE_OCTAHEDRON_SIZE, PROBE_OCTAHEDRON_SIZE, &tex_filter, 1); + #ifdef IRRADIANCE_SH_L2 /* we need a signed format for Spherical Harmonics */ int irradiance_format = DRW_TEX_RGBA_16; @@ -544,9 +702,11 @@ void EEVEE_lightprobes_cache_finish(EEVEE_SceneLayerData *sldata, EEVEE_Data *ve } EEVEE_lightprobes_updates(sldata, vedata->psl, vedata->stl); + EEVEE_planar_reflections_updates(sldata); DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); + DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data); } /* Glossy filter probe_rt to probe_pool at index probe_idx */ @@ -709,8 +869,7 @@ static void render_scene_to_probe( DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, i, 0); DRW_framebuffer_viewport_size(sldata->probe_fb, 0, 0, PROBE_RT_SIZE, PROBE_RT_SIZE); - float clear[4] = {1.0f, 0.0f, 0.0f, 1.0f}; - DRW_framebuffer_clear(true, true, false, clear, 1.0); + DRW_framebuffer_clear(false, true, false, NULL, 1.0); /* Setup custom matrices */ mul_m4_m4m4(viewmat, cubefacemat[i], posmat); @@ -751,6 +910,67 @@ static void render_scene_to_probe( sldata->probes->specular_toggle = true; } +static void render_scene_to_planar( + EEVEE_Data *vedata, int layer, + float (*viewmat)[4], float (*persmat)[4], + float clip_plane[4]) +{ + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_TextureList *txl = vedata->txl; + EEVEE_PassList *psl = vedata->psl; + + float viewinv[4][4]; + float persinv[4][4]; + + invert_m4_m4(viewinv, viewmat); + invert_m4_m4(persinv, persmat); + + /* Attach depth here since it's a DRW_TEX_TEMP */ + DRW_framebuffer_texture_attach(fbl->planarref_fb, e_data.planar_depth, 0, 0); + DRW_framebuffer_texture_layer_attach(fbl->planarref_fb, txl->planar_pool, 0, layer, 0); + DRW_framebuffer_bind(fbl->planarref_fb); + + DRW_framebuffer_clear(false, true, false, NULL, 1.0); + + /* Avoid using the texture attached to framebuffer when rendering. */ + GPUTexture *tmp_planar_pool = txl->planar_pool; + txl->planar_pool = NULL; + DRW_viewport_matrix_override_set(persmat, DRW_MAT_PERS); + DRW_viewport_matrix_override_set(persinv, DRW_MAT_PERSINV); + DRW_viewport_matrix_override_set(viewmat, DRW_MAT_VIEW); + DRW_viewport_matrix_override_set(viewinv, DRW_MAT_VIEWINV); + + /* Background */ + DRW_draw_pass(psl->background_pass); + + /* Since we are rendering with an inverted view matrix, we need + * to invert the facing for backface culling to be the same. */ + DRW_state_invert_facing(); + DRW_state_clip_planes_add(clip_plane); + + /* Depth prepass */ + DRW_draw_pass(psl->depth_pass_clip); + DRW_draw_pass(psl->depth_pass_clip_cull); + + /* Shading pass */ + DRW_draw_pass(psl->default_pass); + DRW_draw_pass(psl->default_flat_pass); + DRW_draw_pass(psl->material_pass); + + DRW_state_invert_facing(); + DRW_state_clip_planes_reset(); + + /* Restore */ + txl->planar_pool = tmp_planar_pool; + DRW_viewport_matrix_override_unset(DRW_MAT_PERS); + DRW_viewport_matrix_override_unset(DRW_MAT_PERSINV); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEW); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEWINV); + + DRW_framebuffer_texture_detach(txl->planar_pool); + DRW_framebuffer_texture_detach(e_data.planar_depth); +} + static void render_world_to_probe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl) { EEVEE_LightProbesInfo *pinfo = sldata->probes; @@ -780,8 +1000,9 @@ static void lightprobe_cell_location_get(EEVEE_LightGrid *egrid, int cell_idx, f add_v3_v3(r_pos, tmp); } -void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl) +void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) { + EEVEE_PassList *psl = vedata->psl; EEVEE_LightProbesInfo *pinfo = sldata->probes; Object *ob; const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -814,7 +1035,7 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl /* Only compute probes if not navigating or in playback */ struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C); if (((rv3d->rflag & RV3D_NAVIGATING) != 0) || ED_screen_animation_no_scrub(wm) != NULL) { - return; + goto update_planar; } } @@ -836,7 +1057,9 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl /* Temporary Remove all probes. */ int tmp_num_render_grid = pinfo->num_render_grid; int tmp_num_render_cube = pinfo->num_render_cube; + int tmp_num_planar = pinfo->num_planar; pinfo->num_render_cube = 0; + pinfo->num_planar = 0; /* Use light from previous bounce when capturing radiance. */ if (pinfo->updated_bounce == 0) { @@ -852,6 +1075,7 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl /* Restore */ pinfo->num_render_grid = tmp_num_render_grid; pinfo->num_render_cube = tmp_num_render_cube; + pinfo->num_planar = tmp_num_planar; /* To see what is going on. */ SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); @@ -863,7 +1087,7 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl /* Only do one probe per frame */ DRW_viewport_request_redraw(); - return; + goto update_planar; } } @@ -901,10 +1125,31 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl DRW_viewport_request_redraw(); /* Only do one probe per frame */ - return; + goto update_planar; } } } + +update_planar: + + for (int i = 0; (ob = pinfo->probes_planar_ref[i]) && (i < MAX_PLANAR); i++) { + EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(ob); + + if (ped->need_update) { + EEVEE_PlanarReflection *eplanar = &pinfo->planar_data[i]; + + /* Temporary Remove all planar reflections (avoid lag effect). */ + int tmp_num_planar = pinfo->num_planar; + pinfo->num_planar = 0; + + render_scene_to_planar(vedata, i, ped->viewmat, ped->persmat, eplanar->plane_equation); + + /* Restore */ + pinfo->num_planar = tmp_num_planar; + + ped->need_update = false; + } + } } void EEVEE_lightprobes_free(void) diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 55e608570e2..e4d5e368be0 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -42,6 +42,7 @@ "#define EEVEE_ENGINE\n" \ "#define MAX_PROBE " STRINGIFY(MAX_PROBE) "\n" \ "#define MAX_GRID " STRINGIFY(MAX_GRID) "\n" \ + "#define MAX_PLANAR " STRINGIFY(MAX_PLANAR) "\n" \ "#define MAX_LIGHT " STRINGIFY(MAX_LIGHT) "\n" \ "#define MAX_SHADOW_CUBE " STRINGIFY(MAX_SHADOW_CUBE) "\n" \ "#define MAX_SHADOW_MAP " STRINGIFY(MAX_SHADOW_MAP) "\n" \ @@ -305,19 +306,22 @@ struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma) SHADER_DEFINES "#define MESH_SHADER\n" "#define HAIR_SHADER\n"); } -static void add_standard_uniforms(DRWShadingGroup *shgrp, EEVEE_SceneLayerData *sldata) +static void add_standard_uniforms(DRWShadingGroup *shgrp, EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) { DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_int(shgrp, "light_count", &sldata->lamps->num_light, 1); DRW_shgroup_uniform_int(shgrp, "probe_count", &sldata->probes->num_render_cube, 1); DRW_shgroup_uniform_int(shgrp, "grid_count", &sldata->probes->num_render_grid, 1); + DRW_shgroup_uniform_int(shgrp, "planar_count", &sldata->probes->num_planar, 1); DRW_shgroup_uniform_bool(shgrp, "specToggle", &sldata->probes->specular_toggle, 1); DRW_shgroup_uniform_float(shgrp, "lodMax", &sldata->probes->lodmax, 1); DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); DRW_shgroup_uniform_buffer(shgrp, "probeCubes", &sldata->probe_pool); + DRW_shgroup_uniform_buffer(shgrp, "probePlanars", &vedata->txl->planar_pool); DRW_shgroup_uniform_buffer(shgrp, "irradianceGrid", &sldata->irradiance_pool); DRW_shgroup_uniform_buffer(shgrp, "shadowCubes", &sldata->shadow_depth_cube_pool); DRW_shgroup_uniform_buffer(shgrp, "shadowCascades", &sldata->shadow_depth_cascade_pool); @@ -395,15 +399,15 @@ void EEVEE_materials_cache_init(EEVEE_Data *vedata) DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_WIRE; psl->default_pass = DRW_pass_create("Default Lit Pass", state); DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.default_lit, psl->default_pass); - add_standard_uniforms(shgrp, sldata); + add_standard_uniforms(shgrp, sldata, vedata); psl->default_flat_pass = DRW_pass_create("Default Flat Lit Pass", state); shgrp = DRW_shgroup_create(e_data.default_lit_flat, psl->default_flat_pass); - add_standard_uniforms(shgrp, sldata); + add_standard_uniforms(shgrp, sldata, vedata); psl->default_hair_pass = DRW_pass_create("Default Hair Lit Pass", state); shgrp = DRW_shgroup_create(e_data.default_lit_hair, psl->default_hair_pass); - add_standard_uniforms(shgrp, sldata); + add_standard_uniforms(shgrp, sldata, vedata); } { @@ -477,7 +481,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass); if (shgrp) { - add_standard_uniforms(shgrp, sldata); + add_standard_uniforms(shgrp, sldata, vedata); BLI_ghash_insert(material_hash, ma, shgrp); @@ -549,7 +553,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass); if (shgrp) { - add_standard_uniforms(shgrp, sldata); + add_standard_uniforms(shgrp, sldata, vedata); BLI_ghash_insert(material_hash, ma, shgrp); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 19500cd454e..ddda9cd3bf8 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -33,6 +33,7 @@ extern struct DrawEngineType draw_engine_eevee_type; /* Minimum UBO is 16384 bytes */ #define MAX_PROBE 128 /* TODO : find size by dividing UBO max size by probe data size */ #define MAX_GRID 64 /* TODO : find size by dividing UBO max size by grid data size */ +#define MAX_PLANAR 16 /* TODO : find size by dividing UBO max size by grid data size */ #define MAX_LIGHT 128 /* TODO : find size by dividing UBO max size by light data size */ #define MAX_SHADOW_CUBE 42 /* TODO : Make this depends on GL_MAX_ARRAY_TEXTURE_LAYERS */ #define MAX_SHADOW_MAP 64 @@ -90,6 +91,8 @@ typedef struct EEVEE_FramebufferList { struct GPUFrameBuffer *dof_scatter_far_fb; struct GPUFrameBuffer *dof_scatter_near_fb; + struct GPUFrameBuffer *planarref_fb; + struct GPUFrameBuffer *main; /* HDR */ } EEVEE_FramebufferList; @@ -105,6 +108,8 @@ typedef struct EEVEE_TextureList { struct GPUTexture *bloom_downsample[MAX_BLOOM_STEP]; /* R16_G16_B16 */ struct GPUTexture *bloom_upsample[MAX_BLOOM_STEP-1]; /* R16_G16_B16 */ + struct GPUTexture *planar_pool; + struct GPUTexture *color; /* R16_G16_B16 */ } EEVEE_TextureList; @@ -193,10 +198,21 @@ typedef struct EEVEE_LightGrid { float increment_z[3], pad4; } EEVEE_LightGrid; +typedef struct EEVEE_PlanarReflection { + float plane_equation[4]; + float clip_vec_x[3], attenuation_scale; + float clip_vec_y[3], attenuation_bias; + float clip_edge_x_pos, clip_edge_x_neg; + float clip_edge_y_pos, clip_edge_y_neg; + float facing_scale, facing_bias, pad[2]; + float reflectionmat[4][4]; +} EEVEE_PlanarReflection; + /* ************ PROBE DATA ************* */ typedef struct EEVEE_LightProbesInfo { int num_cube, cache_num_cube; int num_grid, cache_num_grid; + int num_planar, cache_num_planar; int update_flag; int updated_bounce; /* Actual number of probes that have datas. */ @@ -219,9 +235,11 @@ typedef struct EEVEE_LightProbesInfo { /* XXX This is fragile, can get out of sync quickly. */ struct Object *probes_cube_ref[MAX_PROBE]; struct Object *probes_grid_ref[MAX_GRID]; + struct Object *probes_planar_ref[MAX_PLANAR]; /* UBO Storage : data used by UBO */ struct EEVEE_LightProbe probe_data[MAX_PROBE]; struct EEVEE_LightGrid grid_data[MAX_GRID]; + struct EEVEE_PlanarReflection planar_data[MAX_PLANAR]; } EEVEE_LightProbesInfo; /* EEVEE_LightProbesInfo->update_flag */ @@ -296,6 +314,7 @@ typedef struct EEVEE_SceneLayerData { struct GPUUniformBuffer *probe_ubo; struct GPUUniformBuffer *grid_ubo; + struct GPUUniformBuffer *planar_ubo; struct GPUFrameBuffer *probe_fb; struct GPUFrameBuffer *probe_filter_fb; @@ -322,6 +341,9 @@ typedef struct EEVEE_LightProbeEngineData { int updated_cells; int num_cell; int probe_id; /* Only used for display data */ + /* For planar reflection rendering */ + float viewmat[4][4]; + float persmat[4][4]; struct ListBase captured_object_list; } EEVEE_LightProbeEngineData; @@ -380,11 +402,11 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl); void EEVEE_lights_free(void); /* eevee_lightprobes.c */ -void EEVEE_lightprobes_init(EEVEE_SceneLayerData *sldata); +void EEVEE_lightprobes_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); void EEVEE_lightprobes_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, EEVEE_StorageList *stl); void EEVEE_lightprobes_cache_add(EEVEE_SceneLayerData *sldata, Object *ob); void EEVEE_lightprobes_cache_finish(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl); +void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); void EEVEE_lightprobes_free(void); /* eevee_effects.c */ 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 16168320453..5ea74f38e83 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -24,6 +24,25 @@ struct ProbeData { #define p_atten_fac attenuation_fac_type.x #define p_atten_type attenuation_fac_type.y +struct PlanarData { + vec4 plane_equation; + vec4 clip_vec_x_fade_scale; + vec4 clip_vec_y_fade_bias; + vec4 clip_edges; + vec4 facing_scale_bias; + mat4 reflectionmat; /* transform world space into reflection texture space */ +}; + +#define pl_plane_eq plane_equation +#define pl_normal plane_equation.xyz +#define pl_facing_scale facing_scale_bias.x +#define pl_facing_bias facing_scale_bias.y +#define pl_fade_scale clip_vec_x_fade_scale.w +#define pl_fade_bias clip_vec_y_fade_bias.w +#define pl_clip_pos_x clip_vec_x_fade_scale.xyz +#define pl_clip_pos_y clip_vec_y_fade_bias.xyz +#define pl_clip_edges clip_edges + struct GridData { mat4 localmat; ivec4 resolution_offset; 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 7a1d2dae479..06fcffb6244 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -2,9 +2,12 @@ uniform int light_count; uniform int probe_count; uniform int grid_count; +uniform int planar_count; uniform mat4 ProjectionMatrix; uniform mat4 ViewMatrixInverse; +uniform sampler2DArray probePlanars; + uniform sampler2DArray probeCubes; uniform float lodMax; uniform bool specToggle; @@ -25,6 +28,10 @@ layout(std140) uniform grid_block { GridData grids_data[MAX_GRID]; }; +layout(std140) uniform planar_block { + PlanarData planars_data[MAX_PLANAR]; +}; + layout(std140) uniform light_block { LightData lights_data[MAX_LIGHT]; }; @@ -275,6 +282,25 @@ float probe_attenuation(vec3 W, ProbeData pd) return fac; } +float planar_attenuation(vec3 W, vec3 N, PlanarData pd) +{ + float fac; + + /* Normal Facing */ + fac = saturate(dot(pd.pl_normal, N) * pd.pl_facing_scale + pd.pl_facing_bias); + + /* Distance from plane */ + fac *= saturate(abs(dot(pd.pl_plane_eq, vec4(W, 1.0))) * pd.pl_fade_scale + pd.pl_fade_bias); + + /* Fancy fast clipping calculation */ + vec2 dist_to_clip; + dist_to_clip.x = dot(pd.pl_clip_pos_x, W); + dist_to_clip.y = dot(pd.pl_clip_pos_y, W); + fac *= step(2.0, dot(step(pd.pl_clip_edges, dist_to_clip.xxyy), vec2(-1.0, 1.0).xyxy)); /* compare and add all tests */ + + return fac; +} + vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, float ao) { roughness = clamp(roughness, 1e-8, 0.9999); @@ -325,6 +351,24 @@ vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, vec4 spec_accum = vec4(0.0); vec4 diff_accum = vec4(0.0); + /* Planar Reflections */ + for (int i = 0; i < MAX_PLANAR && i < planar_count && spec_accum.a < 0.999; ++i) { + PlanarData pd = planars_data[i]; + + float influence = planar_attenuation(sd.W, sd.N, pd); + + if (influence > 0.0) { + float influ_spec = min(influence, (1.0 - spec_accum.a)); + + vec4 refco = pd.reflectionmat * vec4(sd.W, 1.0); + refco.xy /= refco.w; + vec3 sample = textureLod(probePlanars, vec3(refco.xy, i), 0.0).rgb; + + spec_accum.rgb += sample * influ_spec; + spec_accum.a += influ_spec; + } + } + /* Specular probes */ /* Start at 1 because 0 is world probe */ for (int i = 1; i < MAX_PROBE && i < probe_count && spec_accum.a < 0.999; ++i) { |