diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 42a132004d4..b914bafaf3f 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -1186,6 +1186,8 @@ class EEVEE_MATERIAL_PT_options(MaterialButtonsPanel, Panel): layout.prop(mat, "use_screen_refraction") layout.prop(mat, "refraction_depth") + layout.prop(mat, "use_screen_subsurface") + classes = ( MATERIAL_MT_sss_presets, diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 6be6bea5a52..65fa38af652 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -757,6 +757,29 @@ class RENDER_PT_eevee_volumetric(RenderButtonsPanel, Panel): col.prop(props, "volumetric_colored_transmittance") +class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel): + bl_label = "Subsurface Scattering" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE'} + + @classmethod + def poll(cls, context): + scene = context.scene + return scene and (scene.view_render.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + scene = context.scene + props = scene.layer_properties['BLENDER_EEVEE'] + self.layout.prop(props, "sss_enable", text="") + + def draw(self, context): + layout = self.layout + scene = context.scene + props = scene.layer_properties['BLENDER_EEVEE'] + + col = layout.column() + + class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel): bl_label = "Screen Space Reflections" bl_options = {'DEFAULT_CLOSED'} @@ -870,6 +893,7 @@ classes = ( RENDER_PT_eevee_sampling, RENDER_PT_eevee_shadows, RENDER_PT_eevee_indirect_lighting, + RENDER_PT_eevee_subsurface_scattering, RENDER_PT_eevee_screen_space_reflections, RENDER_PT_eevee_ambient_occlusion, RENDER_PT_eevee_volumetric, diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py index 270902ea54a..c4013480c74 100644 --- a/release/scripts/startup/bl_ui/properties_render_layer.py +++ b/release/scripts/startup/bl_ui/properties_render_layer.py @@ -318,6 +318,34 @@ class RENDERLAYER_PT_eevee_volumetric(RenderLayerButtonsPanel, Panel): col.template_override_property(layer_props, scene_props, "volumetric_colored_transmittance") +class RENDERLAYER_PT_eevee_subsurface_scattering(RenderLayerButtonsPanel, Panel): + bl_label = "Subsurface Scattering" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE'} + + @classmethod + def poll(cls, context): + scene = context.scene + return scene and (scene.view_render.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + scene = context.scene + scene_props = scene.layer_properties['BLENDER_EEVEE'] + layer = bpy.context.render_layer + layer_props = layer.engine_overrides['BLENDER_EEVEE'] + + self.layout.template_override_property(layer_props, scene_props, "sss_enable", text="") + + def draw(self, context): + layout = self.layout + scene = context.scene + scene_props = scene.layer_properties['BLENDER_EEVEE'] + layer = bpy.context.render_layer + layer_props = layer.engine_overrides['BLENDER_EEVEE'] + + col = layout.column() + + class RENDERLAYER_PT_eevee_screen_space_reflections(RenderLayerButtonsPanel, Panel): bl_label = "Screen Space Reflections" bl_options = {'DEFAULT_CLOSED'} @@ -428,6 +456,7 @@ classes = ( RENDERLAYER_PT_eevee_sampling, RENDERLAYER_PT_eevee_shadows, RENDERLAYER_PT_eevee_indirect_lighting, + RENDERLAYER_PT_eevee_subsurface_scattering, RENDERLAYER_PT_eevee_screen_space_reflections, RENDERLAYER_PT_eevee_ambient_occlusion, RENDERLAYER_PT_eevee_volumetric, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 102d181e503..13e5cfa192e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -264,7 +264,7 @@ shader_node_categories = [ NodeItem("ShaderNodeBsdfAnisotropic", poll=object_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfVelvet", poll=object_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfToon", poll=object_cycles_shader_nodes_poll), - NodeItem("ShaderNodeSubsurfaceScattering", poll=object_cycles_shader_nodes_poll), + NodeItem("ShaderNodeSubsurfaceScattering", poll=object_eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeEmission", poll=object_eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfHair", poll=object_cycles_shader_nodes_poll), NodeItem("ShaderNodeBackground", poll=world_shader_nodes_poll), 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); @@ -918,6 +924,12 @@ void EEVEE_materials_cache_init(EEVEE_Data *vedata) psl->refract_pass = DRW_pass_create("Opaque Refraction Pass", state); } + { + 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 +} diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index de274b87f9e..039adc68e6d 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -235,6 +235,9 @@ void GPU_material_enable_alpha(GPUMaterial *material); GPUBuiltin GPU_get_material_builtins(GPUMaterial *material); GPUBlendMode GPU_material_alpha_blend(GPUMaterial *material, float obcol[4]); +void GPU_material_sss_profile_create(GPUMaterial *material, float *radii); +struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material); + /* High level functions to create and use GPU materials */ GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo); GPUMaterial *GPU_material_from_nodetree_find( diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index cdd3f789cca..1304cfc28a0 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -142,11 +142,15 @@ struct GPUMaterial { int domain; GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ + GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */ + float *sss_radii; /* UBO containing SSS profile. */ + bool sss_dirty; }; enum { GPU_DOMAIN_SURFACE = (1 << 0), - GPU_DOMAIN_VOLUME = (1 << 1) + GPU_DOMAIN_VOLUME = (1 << 1), + GPU_DOMAIN_SSS = (1 << 2) }; /* Forward declaration so shade_light_textures() can use this, while still keeping the code somewhat organized */ @@ -268,6 +272,10 @@ void GPU_material_free(ListBase *gpumaterial) GPU_uniformbuffer_free(material->ubo); } + if (material->sss_profile != NULL) { + GPU_uniformbuffer_free(material->sss_profile); + } + BLI_freelistN(&material->lamps); MEM_freeN(material); @@ -468,9 +476,168 @@ void GPU_material_uniform_buffer_tag_dirty(ListBase *gpumaterials) if (material->ubo != NULL) { GPU_uniformbuffer_tag_dirty(material->ubo); } + if (material->sss_profile != NULL) { + material->sss_dirty = true; + } } } +/* Eevee Subsurface scattering. */ +/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ + +#define SSS_SAMPLES 25 +#define SSS_EXPONENT 2.0f /* Importance sampling exponent */ + +typedef struct GPUSssKernelData { + float kernel[SSS_SAMPLES][4]; + float radii_n[3], max_radius; +} GPUSssKernelData; + +static void sss_calculate_offsets(GPUSssKernelData *kd) +{ + float step = 2.0f / (float)(SSS_SAMPLES - 1); + for (int i = 0; i < SSS_SAMPLES; i++) { + float o = ((float)i) * step - 1.0f; + float sign = (o < 0.0f) ? -1.0f : 1.0f; + float ofs = sign * fabsf(powf(o, SSS_EXPONENT)); + kd->kernel[i][3] = ofs; + } +} + +#if 0 /* Maybe used for other distributions */ +static void sss_calculate_areas(GPUSssKernelData *kd, float areas[SSS_SAMPLES]) +{ + for (int i = 0; i < SSS_SAMPLES; i++) { + float w0 = (i > 0) ? fabsf(kd->kernel[i][3] - kd->kernel[i-1][3]) : 0.0f; + float w1 = (i < SSS_SAMPLES - 1) ? fabsf(kd->kernel[i][3] - kd->kernel[i+1][3]) : 0.0f; + areas[i] = (w0 + w1) / 2.0f; + } +} +#endif + +static float error_function(float x) { + /* Approximation of the error function by Abramowitz and Stegun + * https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions */ + const float a1 = 0.254829592f; + const float a2 = -0.284496736f; + const float a3 = 1.421413741f; + const float a4 = -1.453152027f; + const float a5 = 1.061405429f; + const float p = 0.3275911f; + + float sign = (x < 0.0f) ? -1.0f : 1.0f; + x = fabsf(x); + + float t = 1.0f / (1.0f + p * x); + float y = 1.0f - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * expf(-(x * x)); + + return sign * y; +} + +static float gaussian_primitive(float x) { + const float sigma = 0.3f; /* Contained mostly between -1..1 */ + return 0.5f * error_function(x / ((float)M_SQRT2 * sigma)); +} + +static float gaussian_integral(float x0, float x1) { + return gaussian_primitive(x0) - gaussian_primitive(x1); +} + +static void compute_sss_kernel(GPUSssKernelData *kd, float *radii) +{ + /* Normalize size */ + copy_v3_v3(kd->radii_n, radii); + kd->max_radius = MAX3(kd->radii_n[0], kd->radii_n[1], kd->radii_n[2]); + mul_v3_fl(kd->radii_n, 1.0f / kd->max_radius); + + /* Compute samples locations on the 1d kernel */ + sss_calculate_offsets(kd); + +#if 0 /* Maybe used for other distributions */ + /* Calculate areas (using importance-sampling) */ + float areas[SSS_SAMPLES]; + sss_calculate_areas(&kd, areas); +#endif + + /* Weights sum for normalization */ + float sum[3] = {0.0f, 0.0f, 0.0f}; + + /* Compute interpolated weights */ + for (int i = 0; i < SSS_SAMPLES; i++) { + float x0, x1; + + if (i == 0) { + x0 = kd->kernel[0][3] - abs(kd->kernel[0][3] - kd->kernel[1][3]) / 2.0f; + } + else { + x0 = (kd->kernel[i - 1][3] + kd->kernel[i][3]) / 2.0f; + } + + if (i == SSS_SAMPLES - 1) { + x1 = kd->kernel[SSS_SAMPLES - 1][3] + abs(kd->kernel[SSS_SAMPLES - 2][3] - kd->kernel[SSS_SAMPLES - 1][3]) / 2.0f; + } + else { + x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f; + } + + kd->kernel[i][0] = gaussian_integral(x0 / kd->radii_n[0], x1 / kd->radii_n[0]); + kd->kernel[i][1] = gaussian_integral(x0 / kd->radii_n[1], x1 / kd->radii_n[1]); + kd->kernel[i][2] = gaussian_integral(x0 / kd->radii_n[2], x1 / kd->radii_n[2]); + + sum[0] += kd->kernel[i][0]; + sum[1] += kd->kernel[i][1]; + sum[2] += kd->kernel[i][2]; + } + + /* Normalize */ + for (int i = 0; i < SSS_SAMPLES; i++) { + kd->kernel[i][0] /= sum[0]; + kd->kernel[i][1] /= sum[1]; + kd->kernel[i][2] /= sum[2]; + } + + /* Put center sample at the start of the array (to sample first) */ + float tmpv[4]; + copy_v4_v4(tmpv, kd->kernel[SSS_SAMPLES / 2]); + for (int i = SSS_SAMPLES / 2; i > 0; i--) { + copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]); + } + copy_v4_v4(kd->kernel[0], tmpv); +} + +void GPU_material_sss_profile_create(GPUMaterial *material, float *radii) +{ + material->sss_radii = radii; + material->sss_dirty = true; + + /* Update / Create UBO */ + if (material->sss_profile == NULL) { + material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + } +} + +static void GPU_material_sss_profile_update(GPUMaterial *material) +{ + GPUSssKernelData kd; + + compute_sss_kernel(&kd, material->sss_radii); + + /* Update / Create UBO */ + GPU_uniformbuffer_update(material->sss_profile, &kd); + + material->sss_dirty = false; +} +#undef SSS_EXPONENT +#undef SSS_SAMPLES + +struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material) +{ + if (material->sss_dirty) { + GPU_material_sss_profile_update(material); + } + return material->sss_profile; +} + void GPU_material_vertex_attributes(GPUMaterial *material, GPUVertexAttribs *attribs) { *attribs = material->attribs; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index e829c4a0c0e..8956f97c8c9 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2672,11 +2672,9 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result) #ifdef EEVEE_ENGINE vec3 L = eevee_surface_diffuse_lit(N, vec3(1.0), 1.0); vec3 vN = normalize(mat3(ViewMatrix) * N); + result = CLOSURE_DEFAULT; result.radiance = L * color.rgb; - result.opacity = 1.0; - result.ssr_data = vec4(0.0); result.ssr_normal = normal_encode(vN, viewCameraVec); - result.ssr_id = -1; #else /* ambient light */ vec3 L = vec3(0.2); @@ -2701,8 +2699,8 @@ void node_bsdf_glossy(vec4 color, float roughness, vec3 N, float ssr_id, out Clo roughness = sqrt(roughness); vec3 L = eevee_surface_glossy_lit(N, vec3(1.0), roughness, 1.0, int(ssr_id), ssr_spec); vec3 vN = normalize(mat3(ViewMatrix) * N); + result = CLOSURE_DEFAULT; result.radiance = L * color.rgb; - result.opacity = 1.0; result.ssr_data = vec4(ssr_spec * color.rgb, roughness); result.ssr_normal = normal_encode(vN, viewCameraVec); result.ssr_id = int(ssr_id); @@ -2743,8 +2741,8 @@ void node_bsdf_glass(vec4 color, float roughness, float ior, vec3 N, float ssr_i roughness = sqrt(roughness); vec3 L = eevee_surface_glass(N, (refractionDepth > 0.0) ? color.rgb : vec3(1.0), roughness, ior, int(ssr_id), ssr_spec); vec3 vN = normalize(mat3(ViewMatrix) * N); + result = CLOSURE_DEFAULT; result.radiance = L * color.rgb; - result.opacity = 1.0; result.ssr_data = vec4(ssr_spec * color.rgb, roughness); result.ssr_normal = normal_encode(vN, viewCameraVec); result.ssr_id = int(ssr_id); @@ -2868,8 +2866,8 @@ void node_bsdf_principled_simple(vec4 base_color, float subsurface, vec3 subsurf vec3 L = eevee_surface_lit(N, diffuse, f0, roughness, 1.0, int(ssr_id), ssr_spec); vec3 vN = normalize(mat3(ViewMatrix) * N); + result = CLOSURE_DEFAULT; result.radiance = L; - result.opacity = 1.0; result.ssr_data = vec4(ssr_spec, roughness); result.ssr_normal = normal_encode(vN, viewCameraVec); result.ssr_id = int(ssr_id); @@ -2925,8 +2923,8 @@ void node_bsdf_principled_clearcoat(vec4 base_color, float subsurface, vec3 subs vec3 L = eevee_surface_clearcoat_lit(N, diffuse, f0, roughness, CN, clearcoat, clearcoat_roughness, 1.0, int(ssr_id), ssr_spec); L = mix(L, L_trans, transmission); vec3 vN = normalize(mat3(ViewMatrix) * N); + result = CLOSURE_DEFAULT; result.radiance = L; - result.opacity = 1.0; result.ssr_data = vec4(ssr_spec, roughness); result.ssr_normal = normal_encode(vN, viewCameraVec); result.ssr_id = int(ssr_id); @@ -2947,6 +2945,7 @@ void node_bsdf_translucent(vec4 color, vec3 N, out Closure result) void node_bsdf_transparent(vec4 color, out Closure result) { /* this isn't right */ + result = CLOSURE_DEFAULT; result.radiance = vec3(0.0); result.opacity = 0.0; #ifdef EEVEE_ENGINE @@ -2965,8 +2964,7 @@ void node_subsurface_scattering( { #if defined(EEVEE_ENGINE) && defined(USE_SSS) vec3 vN = normalize(mat3(ViewMatrix) * N); - result.radiance = vec3(0.0); - result.opacity = 1.0; + result = CLOSURE_DEFAULT; result.ssr_data = vec4(0.0); result.ssr_normal = normal_encode(vN, viewCameraVec); result.ssr_id = -1; @@ -2983,8 +2981,8 @@ void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Cl color.rgb *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); /* Simulate 2 absorption event. */ roughness = sqrt(roughness); vec3 L = eevee_surface_refraction(N, vec3(1.0), roughness, ior); + result = CLOSURE_DEFAULT; result.radiance = L * color.rgb; - result.opacity = 1.0; result.ssr_id = REFRACT_CLOSURE_FLAG; #else node_bsdf_diffuse(color, 0.0, N, result); @@ -3013,11 +3011,10 @@ void node_emission(vec4 color, float strength, vec3 N, out Closure result) #ifndef VOLUMETRICS color *= strength; #ifdef EEVEE_ENGINE + result = CLOSURE_DEFAULT; result.radiance = color.rgb; result.opacity = color.a; - result.ssr_data = vec4(0.0); result.ssr_normal = normal_encode(N, viewCameraVec); - result.ssr_id = -1; #else result = Closure(color.rgb, color.a); #endif @@ -3046,6 +3043,7 @@ void node_background(vec4 color, float strength, out Closure result) #ifndef VOLUMETRICS color *= strength; #ifdef EEVEE_ENGINE + result = CLOSURE_DEFAULT; result.radiance = color.rgb; result.opacity = color.a; #else @@ -4181,6 +4179,7 @@ void node_eevee_specular( vec3 L = eevee_surface_lit(normal, diffuse.rgb, specular.rgb, roughness, occlusion, int(ssr_id), ssr_spec); vec3 vN = normalize(mat3(ViewMatrix) * normal); + result = CLOSURE_DEFAULT; result.radiance = L + emissive.rgb; result.opacity = 1.0 - transp; result.ssr_data = vec4(ssr_spec, roughness); diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 220326fb01d..34b9f9f83bf 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -513,6 +513,7 @@ enum { enum { MA_BL_HIDE_BACKSIDE = (1 << 0), MA_BL_SS_REFRACTION = (1 << 1), + MA_BL_SS_SUBSURFACE = (1 << 2), }; /* blend_shadow */ diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index b92f06c97a5..ec2ff5f1741 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -367,6 +367,7 @@ RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(volumetric_light_clamp) RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_shadows) RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(volumetric_shadow_samples) RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_colored_transmittance) +RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(sss_enable) RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_refraction) RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_enable) RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_halfres) @@ -1195,6 +1196,14 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update"); + /* Screen Space Subsurface Scattering */ + prop = RNA_def_property(srna, "sss_enable", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_sss_enable_get", + "rna_LayerEngineSettings_Eevee_sss_enable_set"); + RNA_def_property_ui_text(prop, "Subsurface Scattering", "Enable screen space subsurface scattering"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update"); + /* Screen Space Reflection */ prop = RNA_def_property(srna, "ssr_enable", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_enable_get", diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 7f23cd78779..a78ad4262ce 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -1877,6 +1877,11 @@ void RNA_def_material(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Screen Space Refraction", "Use raytraced screen space refractions"); RNA_def_property_update(prop, 0, "rna_Material_draw_update"); + prop = RNA_def_property(srna, "use_screen_subsurface", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "blend_flag", MA_BL_SS_SUBSURFACE); + RNA_def_property_ui_text(prop, "Screen Space Subsurface Scattering", "Use post process subsurface scattering"); + RNA_def_property_update(prop, 0, "rna_Material_draw_update"); + prop = RNA_def_property(srna, "refraction_depth", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "refract_depth"); RNA_def_property_range(prop, 0.0f, FLT_MAX); diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c index 58f345c2590..4321c47a092 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c @@ -54,6 +54,13 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, bNode *node, if (!in[5].link) GPU_link(mat, "world_normals_get", &in[5].link); + if (node->sss_id == 0) { + bNodeSocket *socket = BLI_findlink(&node->original->inputs, 2); + bNodeSocketValueRGBA *socket_data = socket->default_value; + /* For some reason it seems that the socket value is in ARGB format. */ + GPU_material_sss_profile_create(mat, &socket_data->value[1]); + } + return GPU_stack_link(mat, node, "node_subsurface_scattering", in, out, GPU_uniform(&node->sss_id)); }