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:
parent
89e9f6ea79
commit
f8b1430566
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue