Eevee: Render: Add Subsurface Pass support.

This commit is contained in:
Clément Foucault 2018-01-31 21:17:27 +01:00
parent a57063a432
commit 253b412ace
7 changed files with 181 additions and 13 deletions

View File

@ -469,6 +469,12 @@ class VIEWLAYER_PT_eevee_layer_passes(ViewLayerButtonsPanel, Panel):
col.prop(view_layer, "use_pass_z")
col.prop(view_layer, "use_pass_normal")
col = split.column()
col.label(text="Subsurface:")
row = col.row(align=True)
row.prop(view_layer, "use_pass_subsurface_direct", text="Direct", toggle=True)
row.prop(view_layer, "use_pass_subsurface_color", text="Color", toggle=True)
classes = (
VIEWLAYER_UL_viewlayers,

View File

@ -1056,9 +1056,16 @@ static void material_opaque(
DRW_shgroup_uniform_texture(*shgrp, "sssTexProfile", sss_tex_profile);
}
DRW_shgroup_stencil_mask(*shgrp, e_data.sss_count + 1);
EEVEE_subsurface_add_pass(sldata, vedata, e_data.sss_count + 1, sss_profile);
e_data.sss_count++;
/* Limit of 8 bit stencil buffer. ID 255 is refraction. */
if (e_data.sss_count < 254) {
DRW_shgroup_stencil_mask(*shgrp, e_data.sss_count + 1);
EEVEE_subsurface_add_pass(sldata, vedata, e_data.sss_count + 1, sss_profile);
e_data.sss_count++;
}
else {
/* TODO : display message. */
printf("Error: Too many different Subsurface shader in the scene.\n");
}
}
}
}

View File

@ -178,6 +178,7 @@ typedef struct EEVEE_PassList {
struct DRWPass *ssr_resolve;
struct DRWPass *sss_blur_ps;
struct DRWPass *sss_resolve_ps;
struct DRWPass *sss_accum_ps;
struct DRWPass *color_downsample_ps;
struct DRWPass *color_downsample_cube_ps;
struct DRWPass *taa_resolve;
@ -220,6 +221,7 @@ typedef struct EEVEE_FramebufferList {
struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP - 1];
struct GPUFrameBuffer *sss_blur_fb;
struct GPUFrameBuffer *sss_clear_fb;
struct GPUFrameBuffer *sss_accum_fb;
struct GPUFrameBuffer *dof_down_fb;
struct GPUFrameBuffer *dof_scatter_far_fb;
struct GPUFrameBuffer *dof_scatter_near_fb;
@ -249,6 +251,8 @@ typedef struct EEVEE_TextureList {
struct GPUTexture *bloom_blit; /* R16_G16_B16 */
struct GPUTexture *bloom_downsample[MAX_BLOOM_STEP]; /* R16_G16_B16 */
struct GPUTexture *bloom_upsample[MAX_BLOOM_STEP - 1]; /* R16_G16_B16 */
struct GPUTexture *sss_dir_accum;
struct GPUTexture *sss_col_accum;
struct GPUTexture *ssr_normal_input;
struct GPUTexture *ssr_specrough_input;
struct GPUTexture *ssr_hit_output;
@ -820,10 +824,12 @@ void EEVEE_screen_raytrace_free(void);
/* eevee_subsurface.c */
int EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_subsurface_add_pass(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, unsigned int sss_id, struct GPUUniformBuffer *sss_profile);
void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_subsurface_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_subsurface_free(void);
/* eevee_motion_blur.c */

View File

@ -160,6 +160,51 @@ static void eevee_render_result_combined(
DRW_framebuffer_read_data(rr->xof, rr->yof, rr->rectx, rr->recty, 4, 0, rp->rect);
}
static void eevee_render_result_subsurface(
RenderResult *rr, const char *viewname,
EEVEE_Data *vedata, EEVEE_ViewLayerData *UNUSED(sldata))
{
const DRWContextState *draw_ctx = DRW_context_state_get();
ViewLayer *view_layer = draw_ctx->view_layer;
if ((view_layer->passflag & SCE_PASS_SUBSURFACE_COLOR) != 0) {
RenderLayer *rl = rr->layers.first;
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_SUBSURFACE_COLOR, viewname);
IDProperty *props = BKE_view_layer_engine_evaluated_get(view_layer, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_EEVEE);
float render_samples = (float)BKE_collection_engine_property_value_get_int(props, "taa_render_samples");
DRW_framebuffer_bind(vedata->fbl->sss_accum_fb);
DRW_framebuffer_read_data(rr->xof, rr->yof, rr->rectx, rr->recty, 3, 1, rp->rect);
/* This is the accumulated color. Divide by the number of samples. */
for (int i = 0; i < rr->rectx * rr->recty * 3; i++) {
rp->rect[i] /= render_samples;
}
}
if ((view_layer->passflag & SCE_PASS_SUBSURFACE_DIRECT) != 0) {
RenderLayer *rl = rr->layers.first;
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_SUBSURFACE_DIRECT, viewname);
IDProperty *props = BKE_view_layer_engine_evaluated_get(view_layer, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_EEVEE);
float render_samples = (float)BKE_collection_engine_property_value_get_int(props, "taa_render_samples");
DRW_framebuffer_bind(vedata->fbl->sss_accum_fb);
DRW_framebuffer_read_data(rr->xof, rr->yof, rr->rectx, rr->recty, 3, 0, rp->rect);
/* This is the accumulated color. Divide by the number of samples. */
for (int i = 0; i < rr->rectx * rr->recty * 3; i++) {
rp->rect[i] /= render_samples;
}
}
if ((view_layer->passflag & SCE_PASS_SUBSURFACE_INDIRECT) != 0) {
/* Do nothing as all the lighting is in the direct pass.
* TODO : Separate Direct from indirect lighting. */
}
}
static void eevee_render_result_normal(
RenderResult *rr, const char *viewname,
EEVEE_Data *vedata, EEVEE_ViewLayerData *UNUSED(sldata))
@ -253,6 +298,13 @@ void EEVEE_render_draw(EEVEE_Data *vedata, struct RenderEngine *engine, struct D
EEVEE_lights_cache_finish(sldata);
EEVEE_lightprobes_cache_finish(sldata, vedata);
if ((view_layer->passflag & (SCE_PASS_SUBSURFACE_COLOR |
SCE_PASS_SUBSURFACE_DIRECT |
SCE_PASS_SUBSURFACE_INDIRECT)) != 0)
{
EEVEE_subsurface_output_init(sldata, vedata);
}
/* Init render result. */
const char *viewname = NULL;
const float *render_size = DRW_viewport_size_get();
@ -304,13 +356,12 @@ void EEVEE_render_draw(EEVEE_Data *vedata, struct RenderEngine *engine, struct D
/* 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);
EEVEE_refraction_compute(sldata, vedata);
/* Opaque refraction */
DRW_draw_pass(psl->refract_depth_pass);
DRW_draw_pass(psl->refract_depth_pass_cull);
DRW_draw_pass(psl->refract_pass);
EEVEE_subsurface_output_accumulate(sldata, vedata);
/* Result NORMAL */
eevee_render_result_normal(rr, viewname, vedata, sldata);
/* Volumetrics Resolve Opaque */
@ -324,8 +375,8 @@ void EEVEE_render_draw(EEVEE_Data *vedata, struct RenderEngine *engine, struct D
EEVEE_draw_effects(sldata, vedata);
}
/* Result Combined */
eevee_render_result_combined(rr, viewname, vedata, sldata);
eevee_render_result_subsurface(rr, viewname, vedata, sldata);
RE_engine_end_result(engine, rr, false, false, false);
}

View File

@ -33,7 +33,7 @@
#include "GPU_texture.h"
static struct {
struct GPUShader *sss_sh[3];
struct GPUShader *sss_sh[4];
} e_data = {NULL}; /* Engine data */
extern char datatoc_common_uniforms_lib_glsl[];
@ -49,6 +49,9 @@ static void eevee_create_shader_subsurface(void)
e_data.sss_sh[1] = DRW_shader_create_fullscreen(frag_str, "#define SECOND_PASS\n");
e_data.sss_sh[2] = DRW_shader_create_fullscreen(frag_str, "#define SECOND_PASS\n"
"#define USE_SEP_ALBEDO\n");
e_data.sss_sh[3] = DRW_shader_create_fullscreen(frag_str, "#define SECOND_PASS\n"
"#define USE_SEP_ALBEDO\n"
"#define RESULT_ACCUM\n");
MEM_freeN(frag_str);
}
@ -71,6 +74,11 @@ int EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
effects->sss_separate_albedo = BKE_collection_engine_property_value_get_bool(props, "sss_separate_albedo");
common_data->sss_jitter_threshold = BKE_collection_engine_property_value_get_float(props, "sss_jitter_threshold");
/* Force separate albedo for final render */
if (DRW_state_is_image_render()) {
effects->sss_separate_albedo = true;
}
/* Shaders */
if (!e_data.sss_sh[0]) {
eevee_create_shader_subsurface();
@ -109,6 +117,47 @@ int EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
return 0;
}
static void set_shgrp_stencil(void *UNUSED(userData), DRWShadingGroup *shgrp)
{
DRW_shgroup_stencil_mask(shgrp, 255);
}
void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *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();
ViewLayer *view_layer = draw_ctx->view_layer;
IDProperty *props = BKE_view_layer_engine_evaluated_get(view_layer, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_EEVEE);
if (BKE_collection_engine_property_value_get_bool(props, "sss_enable")) {
float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
DRWFboTexture tex_data[2] = {{&txl->sss_dir_accum, DRW_TEX_RGBA_16, 0},
{&txl->sss_col_accum, DRW_TEX_RGBA_16, 0}};
DRW_framebuffer_init(&fbl->sss_accum_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1],
tex_data, 2);
/* Clear texture. */
DRW_framebuffer_bind(fbl->sss_accum_fb);
DRW_framebuffer_clear(true, false, false, clear, 0.0f);
/* Make the opaque refraction pass mask the sss. */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES |
DRW_STATE_WIRE | DRW_STATE_WRITE_STENCIL;
DRW_pass_state_set(vedata->psl->refract_pass, state);
DRW_pass_foreach_shgroup(vedata->psl->refract_pass, &set_shgrp_stencil, NULL);
}
else {
/* Cleanup to release memory */
DRW_TEXTURE_FREE_SAFE(txl->sss_dir_accum);
DRW_TEXTURE_FREE_SAFE(txl->sss_col_accum);
DRW_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb);
}
}
void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
@ -121,7 +170,9 @@ void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
*/
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);
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE | DRW_STATE_STENCIL_EQUAL;
psl->sss_resolve_ps = DRW_pass_create("Blur Vert", state);
psl->sss_accum_ps = DRW_pass_create("Resolve Accum", state);
}
}
@ -157,6 +208,18 @@ void EEVEE_subsurface_add_pass(
if (effects->sss_separate_albedo) {
DRW_shgroup_uniform_buffer(grp, "sssAlbedo", &txl->sss_albedo);
}
if (DRW_state_is_image_render()) {
grp = DRW_shgroup_create(e_data.sss_sh[3], psl->sss_accum_ps);
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_buffer(grp, "sssAlbedo", &txl->sss_albedo);
DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_add(grp, quad, NULL);
}
}
void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
@ -271,9 +334,35 @@ void EEVEE_subsurface_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *v
}
}
void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *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) && (fbl->sss_accum_fb != NULL)) {
/* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */
DRW_framebuffer_blit(fbl->main, fbl->sss_blur_fb, false, true);
/* Only do vertical pass + Resolve */
DRW_framebuffer_texture_detach(txl->sss_stencil);
DRW_framebuffer_texture_attach(fbl->sss_accum_fb, txl->sss_stencil, 0, 0);
DRW_framebuffer_bind(fbl->sss_accum_fb);
DRW_draw_pass(psl->sss_accum_ps);
/* Restore */
DRW_framebuffer_texture_detach(txl->sss_stencil);
DRW_framebuffer_texture_attach(fbl->sss_blur_fb, txl->sss_stencil, 0, 0);
DRW_framebuffer_bind(fbl->main);
}
}
void EEVEE_subsurface_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.sss_sh[0]);
DRW_SHADER_FREE_SAFE(e_data.sss_sh[1]);
DRW_SHADER_FREE_SAFE(e_data.sss_sh[2]);
DRW_SHADER_FREE_SAFE(e_data.sss_sh[3]);
}

View File

@ -18,7 +18,10 @@ uniform sampler2DArray utilTex;
#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
#endif /* UTIL_TEX */
out vec4 FragColor;
layout(location = 0) out vec4 FragColor;
#ifdef RESULT_ACCUM
layout(location = 1) out vec4 sssColor;
#endif
uniform mat4 ProjectionMatrix;
@ -84,10 +87,15 @@ void main(void)
#ifdef FIRST_PASS
FragColor = vec4(accum, sss_data.a);
#else /* SECOND_PASS */
#ifdef USE_SEP_ALBEDO
FragColor = vec4(accum * texture(sssAlbedo, uvs).rgb, 1.0);
#else
# ifdef USE_SEP_ALBEDO
# ifdef RESULT_ACCUM
FragColor = vec4(accum, 1.0);
#endif
sssColor = texture(sssAlbedo, uvs);
# else
FragColor = vec4(accum * texture(sssAlbedo, uvs).rgb, 1.0);
# endif
# else
FragColor = vec4(accum, 1.0);
# endif
#endif
}

View File

@ -1105,6 +1105,7 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state)
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, unsigned int mask)
{
BLI_assert(mask <= 255);
shgroup->stencil_mask = mask;
}