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
This commit is contained in:
Clément Foucault 2017-11-14 00:49:54 +01:00
parent 89e9f6ea79
commit f8b1430566
19 changed files with 684 additions and 45 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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)

View File

@ -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. */

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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]);
}

View File

@ -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 */

View File

@ -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
}

View File

@ -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(

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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",

View File

@ -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);

View File

@ -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));
}