diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-11-14 02:49:54 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-11-14 02:49:54 +0300 |
commit | f8b14305668ff7b1f3ba6f886b9e1881c764b201 (patch) | |
tree | 5f773bf0b3a723e7a3b895e0f41bcaecd93f75ad /source/blender/draw | |
parent | 89e9f6ea79078f846d78b6effda2ae8a8a32de84 (diff) |
Eevee: Initial Separable Subsurface Scattering implementation.
How to use:
- Enable subsurface scattering in the render options.
- Add Subsurface BSDF to your shader.
- Check "Screen Space Subsurface Scattering" in the material panel options.
This initial implementation has a few limitations:
- only supports gaussian SSS.
- Does not support principled shader.
- The radius parameters is baked down to a number of samples and then put into an UBO. This means the radius input socket cannot be used. You need to tweak the default vector directly.
- The "texture blur" is considered as always set to 1
Diffstat (limited to 'source/blender/draw')
8 files changed, 424 insertions, 31 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 1fd731c0c41..c3ae8050491 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -93,6 +93,7 @@ set(SRC engines/eevee/eevee_motion_blur.c engines/eevee/eevee_occlusion.c engines/eevee/eevee_screen_raytrace.c + engines/eevee/eevee_subsurface.c engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c engines/external/external_engine.c @@ -156,6 +157,7 @@ data_to_c_simple(engines/eevee/shaders/effect_gtao_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_motion_blur_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_ssr_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/effect_subsurface_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_temporal_aa.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index e8ccd886630..86d767df313 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -115,6 +115,7 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata); effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata); effects->enabled_effects |= EEVEE_occlusion_init(sldata, vedata); + effects->enabled_effects |= EEVEE_subsurface_init(sldata, vedata); effects->enabled_effects |= EEVEE_screen_raytrace_init(sldata, vedata); effects->enabled_effects |= EEVEE_volumes_init(sldata, vedata); @@ -410,29 +411,30 @@ void EEVEE_draw_effects(EEVEE_Data *vedata) DRW_transform_to_display(effects->source_buffer); /* Debug : Ouput buffer to view. */ - if ((G.debug_value > 0) && (G.debug_value <= 6)) { - switch (G.debug_value) { - case 1: - if (txl->maxzbuffer) DRW_transform_to_display(txl->maxzbuffer); - break; - case 2: - if (stl->g_data->ssr_hit_output[0]) DRW_transform_to_display(stl->g_data->ssr_hit_output[0]); - break; - case 3: - if (txl->ssr_normal_input) DRW_transform_to_display(txl->ssr_normal_input); - break; - case 4: - if (txl->ssr_specrough_input) DRW_transform_to_display(txl->ssr_specrough_input); - break; - case 5: - if (txl->color_double_buffer) DRW_transform_to_display(txl->color_double_buffer); - break; - case 6: - if (stl->g_data->gtao_horizons_debug) DRW_transform_to_display(stl->g_data->gtao_horizons_debug); - break; - default: - break; - } + switch (G.debug_value) { + case 1: + if (txl->maxzbuffer) DRW_transform_to_display(txl->maxzbuffer); + break; + case 2: + if (stl->g_data->ssr_hit_output[0]) DRW_transform_to_display(stl->g_data->ssr_hit_output[0]); + break; + case 3: + if (txl->ssr_normal_input) DRW_transform_to_display(txl->ssr_normal_input); + break; + case 4: + if (txl->ssr_specrough_input) DRW_transform_to_display(txl->ssr_specrough_input); + break; + case 5: + if (txl->color_double_buffer) DRW_transform_to_display(txl->color_double_buffer); + break; + case 6: + if (stl->g_data->gtao_horizons_debug) DRW_transform_to_display(stl->g_data->gtao_horizons_debug); + break; + case 7: + if (txl->sss_data) DRW_transform_to_display(txl->sss_data); + break; + default: + break; } /* If no post processes is enabled, buffers are still not swapped, do it now. */ diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 78c22ee261c..217dc2f0227 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -95,6 +95,7 @@ static void EEVEE_cache_init(void *vedata) EEVEE_motion_blur_cache_init(sldata, vedata); EEVEE_occlusion_cache_init(sldata, vedata); EEVEE_screen_raytrace_cache_init(sldata, vedata); + EEVEE_subsurface_cache_init(sldata, vedata); EEVEE_temporal_sampling_cache_init(sldata, vedata); EEVEE_volumes_cache_init(sldata, vedata); } @@ -207,7 +208,7 @@ static void EEVEE_draw_scene(void *vedata) DRW_framebuffer_texture_detach(dtxl->depth); DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0); DRW_framebuffer_bind(fbl->main); - DRW_framebuffer_clear(false, true, false, NULL, 1.0f); + DRW_framebuffer_clear(false, true, true, NULL, 1.0f); if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) && stl->effects->taa_current_sample > 1) { DRW_viewport_matrix_override_set(stl->effects->overide_persmat, DRW_MAT_PERS); @@ -235,9 +236,11 @@ static void EEVEE_draw_scene(void *vedata) DRW_draw_pass(psl->background_pass); EEVEE_draw_default_passes(psl); DRW_draw_pass(psl->material_pass); + EEVEE_subsurface_data_render(sldata, vedata); DRW_stats_group_end(); /* Effects pre-transparency */ + EEVEE_subsurface_compute(sldata, vedata); EEVEE_reflection_compute(sldata, vedata); EEVEE_occlusion_draw_debug(sldata, vedata); DRW_draw_pass(psl->probe_display); @@ -294,6 +297,7 @@ static void EEVEE_engine_free(void) EEVEE_motion_blur_free(); EEVEE_occlusion_free(); EEVEE_screen_raytrace_free(); + EEVEE_subsurface_free(); EEVEE_temporal_sampling_free(); EEVEE_volumes_free(); } @@ -318,6 +322,8 @@ static void EEVEE_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDPr BKE_collection_engine_property_add_int(props, "taa_samples", 8); + BKE_collection_engine_property_add_bool(props, "sss_enable", false); + BKE_collection_engine_property_add_bool(props, "ssr_enable", false); BKE_collection_engine_property_add_bool(props, "ssr_refraction", false); BKE_collection_engine_property_add_bool(props, "ssr_halfres", true); diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index ddbe51ee3e8..f7985fb7ddf 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -58,6 +58,8 @@ static struct { * Packing enables us to same precious textures slots. */ struct GPUTexture *util_tex; + unsigned int sss_count; + float viewvecs[2][4]; } e_data = {NULL}; /* Engine data */ @@ -302,6 +304,9 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_REFRACT) != 0) { BLI_dynstr_appendf(ds, "#define USE_REFRACTION\n"); } + if ((options & VAR_MAT_SSS) != 0) { + BLI_dynstr_appendf(ds, "#define USE_SSS\n"); + } if ((options & VAR_MAT_VSM) != 0) { BLI_dynstr_appendf(ds, "#define SHADOW_VSM\n"); } @@ -630,7 +635,7 @@ struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, World * struct GPUMaterial *EEVEE_material_mesh_get( struct Scene *scene, Material *ma, EEVEE_Data *vedata, - bool use_blend, bool use_multiply, bool use_refract, int shadow_method) + bool use_blend, bool use_multiply, bool use_refract, bool use_sss, int shadow_method) { const void *engine = &DRW_engine_viewport_eevee_type; int options = VAR_MAT_MESH; @@ -638,6 +643,7 @@ struct GPUMaterial *EEVEE_material_mesh_get( if (use_blend) options |= VAR_MAT_BLEND; if (use_multiply) options |= VAR_MAT_MULT; if (use_refract) options |= VAR_MAT_REFRACT; + if (use_sss) options |= VAR_MAT_SSS; if (vedata->stl->effects->use_volumetrics && use_blend) options |= VAR_MAT_VOLUME; options |= eevee_material_shadow_option(shadow_method); @@ -919,6 +925,12 @@ void EEVEE_materials_cache_init(EEVEE_Data *vedata) } { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE | DRW_STATE_WRITE_STENCIL; + psl->sss_pass = DRW_pass_create("Subsurface Pass", state); + e_data.sss_count = 0; + } + + { DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE; psl->transparent_pass = DRW_pass_create("Material Transparent Pass", state); } @@ -963,6 +975,7 @@ static void material_opaque( const bool use_gpumat = (ma->use_nodes && ma->nodetree); const bool use_refract = ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && ((stl->effects->enabled_effects & EFFECT_REFRACT) != 0); + const bool use_sss = ((ma->blend_flag & MA_BL_SS_SUBSURFACE) != 0) && ((stl->effects->enabled_effects & EFFECT_SSS) != 0); EeveeMaterialShadingGroups *emsg = BLI_ghash_lookup(material_hash, (const void *)ma); @@ -973,7 +986,7 @@ static void material_opaque( /* This will have been created already, just perform a lookup. */ *gpumat = (use_gpumat) ? EEVEE_material_mesh_get( - scene, ma, vedata, false, false, use_refract, linfo->shadow_method) : NULL; + scene, ma, vedata, false, false, use_refract, use_sss, linfo->shadow_method) : NULL; *gpumat_depth = (use_gpumat) ? EEVEE_material_mesh_depth_get( scene, ma, (ma->blend_method == MA_BM_HASHED), false) : NULL; return; @@ -981,14 +994,25 @@ static void material_opaque( if (use_gpumat) { /* Shading */ - *gpumat = EEVEE_material_mesh_get(scene, ma, vedata, false, false, use_refract, linfo->shadow_method); + *gpumat = EEVEE_material_mesh_get(scene, ma, vedata, false, false, use_refract, use_sss, linfo->shadow_method); - *shgrp = DRW_shgroup_material_create(*gpumat, use_refract ? psl->refract_pass : psl->material_pass); + *shgrp = DRW_shgroup_material_create(*gpumat, + (use_refract) ? psl->refract_pass : + (use_sss) ? psl->sss_pass : psl->material_pass); if (*shgrp) { static int no_ssr = -1; static int first_ssr = 0; int *ssr_id = (stl->effects->use_ssr && !use_refract) ? &first_ssr : &no_ssr; add_standard_uniforms(*shgrp, sldata, vedata, ssr_id, &ma->refract_depth, use_refract, false); + + if (use_sss) { + struct GPUUniformBuffer *sss_profile = GPU_material_sss_profile_get(*gpumat); + if (sss_profile) { + DRW_shgroup_stencil_mask(*shgrp, e_data.sss_count + 1); + EEVEE_subsurface_add_pass(vedata, e_data.sss_count + 1, sss_profile); + e_data.sss_count++; + } + } } else { /* Shader failed : pink color */ @@ -1072,7 +1096,8 @@ static void material_transparent( if (ma->use_nodes && ma->nodetree) { /* Shading */ - *gpumat = EEVEE_material_mesh_get(scene, ma, vedata, true, (ma->blend_method == MA_BM_MULTIPLY), use_refract, linfo->shadow_method); + *gpumat = EEVEE_material_mesh_get(scene, ma, vedata, true, (ma->blend_method == MA_BM_MULTIPLY), use_refract, + false, linfo->shadow_method); *shgrp = DRW_shgroup_material_create(*gpumat, psl->transparent_pass); if (*shgrp) { diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index ff4d194fb01..43540762a52 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -115,6 +115,7 @@ enum { VAR_MAT_SHADOW = (1 << 11), VAR_MAT_REFRACT = (1 << 12), VAR_MAT_VOLUME = (1 << 13), + VAR_MAT_SSS = (1 << 14), }; /* Shadow Technique */ @@ -161,6 +162,8 @@ typedef struct EEVEE_PassList { struct DRWPass *volumetric_resolve_ps; struct DRWPass *ssr_raytrace; struct DRWPass *ssr_resolve; + struct DRWPass *sss_blur_ps; + struct DRWPass *sss_resolve_ps; struct DRWPass *color_downsample_ps; struct DRWPass *color_downsample_cube_ps; struct DRWPass *taa_resolve; @@ -184,6 +187,7 @@ typedef struct EEVEE_PassList { struct DRWPass *refract_depth_pass_clip; struct DRWPass *refract_depth_pass_clip_cull; struct DRWPass *default_pass[VAR_MAT_MAX]; + struct DRWPass *sss_pass; struct DRWPass *material_pass; struct DRWPass *refract_pass; struct DRWPass *transparent_pass; @@ -199,6 +203,8 @@ typedef struct EEVEE_FramebufferList { struct GPUFrameBuffer *bloom_blit_fb; struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP]; struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP - 1]; + struct GPUFrameBuffer *sss_blur_fb; + struct GPUFrameBuffer *sss_clear_fb; struct GPUFrameBuffer *dof_down_fb; struct GPUFrameBuffer *dof_scatter_far_fb; struct GPUFrameBuffer *dof_scatter_near_fb; @@ -244,6 +250,10 @@ typedef struct EEVEE_TextureList { struct GPUTexture *gtao_horizons; + struct GPUTexture *sss_data; + struct GPUTexture *sss_blur; + struct GPUTexture *sss_stencil; + struct GPUTexture *maxzbuffer; struct GPUTexture *color; /* R16_G16_B16 */ @@ -496,6 +506,7 @@ enum { EFFECT_TAA = (1 << 8), EFFECT_POST_BUFFER = (1 << 9), /* Not really an effect but a feature */ EFFECT_NORMAL_BUFFER = (1 << 10), /* Not really an effect but a feature */ + EFFECT_SSS = (1 << 11), }; /* ************** SCENE LAYER DATA ************** */ @@ -624,7 +635,7 @@ struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, str struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, struct World *wo); struct GPUMaterial *EEVEE_material_mesh_get( struct Scene *scene, Material *ma, EEVEE_Data *vedata, - bool use_blend, bool use_multiply, bool use_refract, int shadow_method); + bool use_blend, bool use_multiply, bool use_refract, bool use_sss, int shadow_method); struct GPUMaterial *EEVEE_material_mesh_volume_get(struct Scene *scene, Material *ma); struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow); struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, int shadow_method); @@ -681,6 +692,14 @@ void EEVEE_refraction_compute(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); void EEVEE_reflection_compute(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); void EEVEE_screen_raytrace_free(void); +/* eevee_subsurface.c */ +int EEVEE_subsurface_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); +void EEVEE_subsurface_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); +void EEVEE_subsurface_add_pass(EEVEE_Data *vedata, unsigned int sss_id, struct GPUUniformBuffer *sss_profile); +void EEVEE_subsurface_data_render(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); +void EEVEE_subsurface_compute(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); +void EEVEE_subsurface_free(void); + /* eevee_motion_blur.c */ int EEVEE_motion_blur_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); void EEVEE_motion_blur_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c new file mode 100644 index 00000000000..b2f3d1fee48 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_subsurface.c @@ -0,0 +1,235 @@ +/* + * Copyright 2016, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Blender Institute + * + */ + +/* Screen space reflections and refractions techniques. + */ + +/** \file eevee_subsurface.c + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "BLI_dynstr.h" + +#include "eevee_private.h" +#include "GPU_texture.h" + +/* SSR shader variations */ +enum { + SSR_SAMPLES = (1 << 0) | (1 << 1), + SSR_RESOLVE = (1 << 2), + SSR_FULL_TRACE = (1 << 3), + SSR_MAX_SHADER = (1 << 4), +}; + +static struct { + /* Screen Space SubSurfaceScattering */ + struct GPUShader *sss_sh[2]; +} e_data = {NULL}; /* Engine data */ + +extern char datatoc_effect_subsurface_frag_glsl[]; + +static void eevee_create_shader_subsurface(void) +{ + e_data.sss_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_subsurface_frag_glsl, "#define FIRST_PASS\n"); + e_data.sss_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_subsurface_frag_glsl, "#define SECOND_PASS\n"); +} + +int EEVEE_subsurface_init(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *vedata) +{ + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_TextureList *txl = vedata->txl; + const float *viewport_size = DRW_viewport_size_get(); + + const DRWContextState *draw_ctx = DRW_context_state_get(); + SceneLayer *scene_layer = draw_ctx->scene_layer; + IDProperty *props = BKE_scene_layer_engine_evaluated_get(scene_layer, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_EEVEE); + + if (BKE_collection_engine_property_value_get_bool(props, "sss_enable")) { + + /* Shaders */ + if (!e_data.sss_sh[0]) { + eevee_create_shader_subsurface(); + } + + /* NOTE : we need another stencil because the stencil buffer is on the same texture + * as the depth buffer we are sampling from. This could be avoided if the stencil is + * a separate texture but that needs OpenGL 4.4 or ARB_texture_stencil8. + * OR OpenGL 4.3 / ARB_ES3_compatibility if using a renderbuffer instead */ + DRWFboTexture texs[2] = {{&txl->sss_stencil, DRW_TEX_DEPTH_24_STENCIL_8, 0}, + {&txl->sss_blur, DRW_TEX_RGBA_16, DRW_TEX_FILTER}}; + + DRW_framebuffer_init(&fbl->sss_blur_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1], + texs, 2); + + DRWFboTexture tex_data = {&txl->sss_data, DRW_TEX_RGBA_16, DRW_TEX_FILTER}; + DRW_framebuffer_init(&fbl->sss_clear_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1], + &tex_data, 1); + + return EFFECT_SSS; + } + + /* Cleanup to release memory */ + DRW_TEXTURE_FREE_SAFE(txl->sss_data); + DRW_TEXTURE_FREE_SAFE(txl->sss_blur); + DRW_TEXTURE_FREE_SAFE(txl->sss_stencil); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->sss_blur_fb); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->sss_clear_fb); + + return 0; +} + +void EEVEE_subsurface_cache_init(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *vedata) +{ + EEVEE_PassList *psl = vedata->psl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_EffectsInfo *effects = stl->effects; + + if ((effects->enabled_effects & EFFECT_SSS) != 0) { + /** Screen Space SubSurface Scattering overview + * TODO + */ + psl->sss_blur_ps = DRW_pass_create("Blur Horiz", DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL); + + psl->sss_resolve_ps = DRW_pass_create("Blur Vert", DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE | DRW_STATE_STENCIL_EQUAL); + } +} + +void EEVEE_subsurface_add_pass(EEVEE_Data *vedata, unsigned int sss_id, struct GPUUniformBuffer *sss_profile) +{ + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + EEVEE_TextureList *txl = vedata->txl; + EEVEE_PassList *psl = vedata->psl; + struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get(); + + DRWShadingGroup *grp = DRW_shgroup_create(e_data.sss_sh[0], psl->sss_blur_ps); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)vedata->stl->g_data->viewvecs, 2); + DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); + DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_uniform_buffer(grp, "sssData", &txl->sss_data); + DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile); + DRW_shgroup_stencil_mask(grp, sss_id); + DRW_shgroup_call_add(grp, quad, NULL); + + grp = DRW_shgroup_create(e_data.sss_sh[1], psl->sss_resolve_ps); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)vedata->stl->g_data->viewvecs, 2); + DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); + DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_uniform_buffer(grp, "sssData", &txl->sss_blur); + DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile); + DRW_shgroup_stencil_mask(grp, sss_id); + DRW_shgroup_call_add(grp, quad, NULL); +} + +void EEVEE_subsurface_data_render(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *vedata) +{ + EEVEE_PassList *psl = vedata->psl; + EEVEE_TextureList *txl = vedata->txl; + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_EffectsInfo *effects = stl->effects; + + if ((effects->enabled_effects & EFFECT_SSS) != 0) { + float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + /* Clear sss_data texture only... can this be done in a more clever way? */ + DRW_framebuffer_bind(fbl->sss_clear_fb); + DRW_framebuffer_clear(true, false, false, clear, 0.0f); + + if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) { + DRW_framebuffer_texture_detach(txl->ssr_normal_input); + } + if ((effects->enabled_effects & EFFECT_SSR) != 0) { + DRW_framebuffer_texture_detach(txl->ssr_specrough_input); + } + if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) { + DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 2, 0); + } + if ((effects->enabled_effects & EFFECT_SSR) != 0) { + DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 3, 0); + } + DRW_framebuffer_texture_detach(txl->sss_data); + DRW_framebuffer_texture_attach(fbl->main, txl->sss_data, 1, 0); + DRW_framebuffer_bind(fbl->main); + + DRW_draw_pass(psl->sss_pass); + + if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) { + DRW_framebuffer_texture_detach(txl->ssr_normal_input); + } + if ((effects->enabled_effects & EFFECT_SSR) != 0) { + DRW_framebuffer_texture_detach(txl->ssr_specrough_input); + } + DRW_framebuffer_texture_detach(txl->sss_data); + if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) { + DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0); + } + if ((effects->enabled_effects & EFFECT_SSR) != 0) { + DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0); + } + DRW_framebuffer_texture_attach(fbl->sss_clear_fb, txl->sss_data, 0, 0); + } +} + +void EEVEE_subsurface_compute(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *vedata) +{ + EEVEE_PassList *psl = vedata->psl; + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_TextureList *txl = vedata->txl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_EffectsInfo *effects = stl->effects; + + if ((effects->enabled_effects & EFFECT_SSS) != 0) { + float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + DRW_stats_group_start("SSS"); + + /* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */ + DRW_framebuffer_blit(fbl->main, fbl->sss_blur_fb, false, true); + + DRW_framebuffer_texture_detach(dtxl->depth); + + /* First horizontal pass */ + DRW_framebuffer_bind(fbl->sss_blur_fb); + DRW_framebuffer_clear(true, false, false, clear, 0.0f); + DRW_draw_pass(psl->sss_blur_ps); + + /* First vertical pass + Resolve */ + DRW_framebuffer_texture_detach(txl->sss_stencil); + DRW_framebuffer_texture_attach(fbl->main, txl->sss_stencil, 0, 0); + DRW_framebuffer_bind(fbl->main); + DRW_draw_pass(psl->sss_resolve_ps); + + /* Restore */ + DRW_framebuffer_texture_detach(txl->sss_stencil); + DRW_framebuffer_texture_attach(fbl->sss_blur_fb, txl->sss_stencil, 0, 0); + DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0); + + DRW_stats_group_end(); + } +} + +void EEVEE_subsurface_free(void) +{ + DRW_SHADER_FREE_SAFE(e_data.sss_sh[0]); + DRW_SHADER_FREE_SAFE(e_data.sss_sh[1]); +} 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 bbb69d557c4..179d2f9096b 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -605,7 +605,8 @@ Closure closure_mix(Closure cl1, Closure cl2, float fac) Closure cl; #ifdef USE_SSS - cl.sss_data = mix(cl1.sss_data, cl2.sss_data, fac); + cl.sss_data.rgb = mix(cl1.sss_data.rgb, cl2.sss_data.rgb, fac); + cl.sss_data.a = (cl1.sss_data.a > 0.0) ? cl1.sss_data.a : cl2.sss_data.a; #endif if (cl1.ssr_id == outputSsrId) { @@ -640,8 +641,14 @@ Closure closure_add(Closure cl1, Closure cl2) #if defined(MESH_SHADER) && !defined(USE_ALPHA_HASH) && !defined(USE_ALPHA_CLIP) && !defined(SHADOW_SHADER) && !defined(USE_MULTIPLY) layout(location = 0) out vec4 fragColor; +#ifdef USE_SSS +layout(location = 1) out vec4 sssData; +layout(location = 2) out vec4 ssrNormals; +layout(location = 3) out vec4 ssrData; +#else layout(location = 1) out vec4 ssrNormals; layout(location = 2) out vec4 ssrData; +#endif Closure nodetree_exec(void); /* Prototype */ @@ -665,6 +672,9 @@ void main() ssrNormals = cl.ssr_normal.xyyy; ssrData = cl.ssr_data; +#ifdef USE_SSS + sssData = cl.sss_data; +#endif } #endif /* MESH_SHADER && !SHADOW_SHADER */ diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl new file mode 100644 index 00000000000..5cc47796ec0 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl @@ -0,0 +1,94 @@ + +/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ + +#define SSS_SAMPLES 25 +layout(std140) uniform sssProfile { + vec4 kernel[SSS_SAMPLES]; + vec4 radii_max_radius; +}; + +uniform sampler2D depthBuffer; +uniform sampler2D sssData; +uniform sampler2DArray utilTex; + +out vec4 FragColor; + +uniform mat4 ProjectionMatrix; +uniform vec4 viewvecs[2]; + +float get_view_z_from_depth(float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = 2.0 * depth - 1.0; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); + } + else { + return viewvecs[0].z + depth * viewvecs[1].z; + } +} + +vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz) * get_view_z_from_depth(depth); + } + else { + return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz; + } +} + +#define LUT_SIZE 64 +#define M_PI_2 1.5707963267948966 /* pi/2 */ +#define M_2PI 6.2831853071795865 /* 2*pi */ + +void main(void) +{ + vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO precompute */ + vec2 uvs = gl_FragCoord.xy * pixel_size; + vec4 sss_data = texture(sssData, uvs).rgba; + float depth_view = get_view_z_from_depth(texture(depthBuffer, uvs).r); + + float rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2), 0).r; +#ifdef FIRST_PASS + float angle = M_2PI * rand + M_PI_2; + vec2 dir = vec2(1.0, 0.0); +#else /* SECOND_PASS */ + float angle = M_2PI * rand; + vec2 dir = vec2(0.0, 1.0); +#endif + vec2 dir_rand = vec2(cos(angle), sin(angle)); + + /* Compute kernel bounds in 2D. */ + float homcoord = ProjectionMatrix[2][3] * depth_view + ProjectionMatrix[3][3]; + vec2 scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * sss_data.aa / homcoord; + vec2 finalStep = scale * radii_max_radius.w; + finalStep *= 0.5; /* samples range -1..1 */ + + /* Center sample */ + vec3 accum = sss_data.rgb * kernel[0].rgb; + + for (int i = 1; i < SSS_SAMPLES; i++) { + /* Rotate samples that are near the kernel center. */ + vec2 sample_uv = uvs + kernel[i].a * finalStep * ((abs(kernel[i].a) > 0.3) ? dir : dir_rand); + vec3 color = texture(sssData, sample_uv).rgb; + float sample_depth = texture(depthBuffer, sample_uv).r; + sample_depth = get_view_z_from_depth(sample_depth); + + /* Depth correction factor. */ + float depth_delta = depth_view - sample_depth; + float s = clamp(1.0 - exp(-(depth_delta * depth_delta) / (2.0 * sss_data.a)), 0.0, 1.0); + + /* Out of view samples. */ + if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) { + s = 1.0; + } + + accum += kernel[i].rgb * mix(color, sss_data.rgb, s); + } + +#ifdef FIRST_PASS + FragColor = vec4(accum, sss_data.a); +#else /* SECOND_PASS */ + FragColor = vec4(accum, 1.0); +#endif +} |