Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2017-11-14 02:49:54 +0300
committerClément Foucault <foucault.clem@gmail.com>2017-11-14 02:49:54 +0300
commitf8b14305668ff7b1f3ba6f886b9e1881c764b201 (patch)
tree5f773bf0b3a723e7a3b895e0f41bcaecd93f75ad /source
parent89e9f6ea79078f846d78b6effda2ae8a8a32de84 (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')
-rw-r--r--source/blender/draw/CMakeLists.txt2
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c48
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c8
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c35
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h21
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c235
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl12
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl94
-rw-r--r--source/blender/gpu/GPU_material.h3
-rw-r--r--source/blender/gpu/intern/gpu_material.c169
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl23
-rw-r--r--source/blender/makesdna/DNA_material_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_layer.c9
-rw-r--r--source/blender/makesrna/intern/rna_material.c5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c7
15 files changed, 628 insertions, 44 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
+}
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,7 +476,166 @@ 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)
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));
}