Eevee: SSS: Add Quality settings.

Samples : pretty self explanatory.
Jitter Threshold : Reduce cache misses and improve performance (greatly) by lowering this value. This settings let user decide how many samples should be jittered (rotated) to reduce banding artifacts.
This commit is contained in:
Clément Foucault 2017-11-14 02:17:34 +01:00
parent f8b1430566
commit 289f9f42ff
10 changed files with 75 additions and 53 deletions

View File

@ -778,6 +778,8 @@ class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel):
props = scene.layer_properties['BLENDER_EEVEE']
col = layout.column()
col.prop(props, "sss_samples")
col.prop(props, "sss_jitter_threshold")
class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):

View File

@ -344,6 +344,8 @@ class RENDERLAYER_PT_eevee_subsurface_scattering(RenderLayerButtonsPanel, Panel)
layer_props = layer.engine_overrides['BLENDER_EEVEE']
col = layout.column()
col.template_override_property(layer_props, scene_props, "sss_samples")
col.template_override_property(layer_props, scene_props, "sss_jitter_threshold")
class RENDERLAYER_PT_eevee_screen_space_reflections(RenderLayerButtonsPanel, Panel):

View File

@ -323,6 +323,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_int(props, "sss_samples", 7);
BKE_collection_engine_property_add_float(props, "sss_jitter_threshold", 0.3f);
BKE_collection_engine_property_add_bool(props, "ssr_enable", false);
BKE_collection_engine_property_add_bool(props, "ssr_refraction", false);

View File

@ -1006,7 +1006,8 @@ static void material_opaque(
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);
struct GPUUniformBuffer *sss_profile = GPU_material_sss_profile_get(*gpumat,
stl->effects->sss_sample_count);
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);

View File

@ -429,6 +429,10 @@ typedef struct EEVEE_EffectsInfo {
int enabled_effects;
bool swap_double_buffer;
/* SSSS */
int sss_sample_count;
float sss_jitter_threshold;
/* Volumetrics */
bool use_volumetrics;
int volume_current_sample;

View File

@ -19,7 +19,7 @@
*
*/
/* Screen space reflections and refractions techniques.
/* Screen space subsurface scattering technique.
*/
/** \file eevee_subsurface.c
@ -33,16 +33,7 @@
#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 */
@ -56,6 +47,8 @@ static void eevee_create_shader_subsurface(void)
int EEVEE_subsurface_init(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
const float *viewport_size = DRW_viewport_size_get();
@ -65,6 +58,8 @@ int EEVEE_subsurface_init(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *veda
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")) {
effects->sss_sample_count = 1 + BKE_collection_engine_property_value_get_int(props, "sss_samples") * 2;
effects->sss_jitter_threshold = BKE_collection_engine_property_value_get_float(props, "sss_jitter_threshold");
/* Shaders */
if (!e_data.sss_sh[0]) {
@ -119,6 +114,8 @@ void EEVEE_subsurface_add_pass(EEVEE_Data *vedata, unsigned int sss_id, struct G
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
EEVEE_TextureList *txl = vedata->txl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get();
DRWShadingGroup *grp = DRW_shgroup_create(e_data.sss_sh[0], psl->sss_blur_ps);
@ -127,6 +124,8 @@ void EEVEE_subsurface_add_pass(EEVEE_Data *vedata, unsigned int sss_id, struct G
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_uniform_int(grp, "sampleCount", &effects->sss_sample_count, 1);
DRW_shgroup_uniform_float(grp, "jitterThreshold", &effects->sss_jitter_threshold, 1);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_add(grp, quad, NULL);
@ -136,6 +135,8 @@ void EEVEE_subsurface_add_pass(EEVEE_Data *vedata, unsigned int sss_id, struct G
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_uniform_int(grp, "sampleCount", &effects->sss_sample_count, 1);
DRW_shgroup_uniform_float(grp, "jitterThreshold", &effects->sss_jitter_threshold, 1);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_add(grp, quad, NULL);
}

View File

@ -1,12 +1,14 @@
/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
#define SSS_SAMPLES 25
#define MAX_SSS_SAMPLES 65
layout(std140) uniform sssProfile {
vec4 kernel[SSS_SAMPLES];
vec4 kernel[MAX_SSS_SAMPLES];
vec4 radii_max_radius;
};
uniform int sampleCount;
uniform float jitterThreshold;
uniform sampler2D depthBuffer;
uniform sampler2D sssData;
uniform sampler2DArray utilTex;
@ -27,16 +29,6 @@ float get_view_z_from_depth(float depth)
}
}
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 */
@ -67,9 +59,8 @@ void main(void)
/* 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);
for (int i = 1; i < sampleCount && i < MAX_SSS_SAMPLES; i++) {
vec2 sample_uv = uvs + kernel[i].a * finalStep * ((abs(kernel[i].a) > jitterThreshold) ? 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);

View File

@ -236,7 +236,7 @@ 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);
struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct);
/* High level functions to create and use GPU materials */
GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo);

View File

@ -144,6 +144,7 @@ struct GPUMaterial {
GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */
GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */
float *sss_radii; /* UBO containing SSS profile. */
int sss_samples;
bool sss_dirty;
};
@ -485,7 +486,7 @@ void GPU_material_uniform_buffer_tag_dirty(ListBase *gpumaterials)
/* Eevee Subsurface scattering. */
/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
#define SSS_SAMPLES 25
#define SSS_SAMPLES 65
#define SSS_EXPONENT 2.0f /* Importance sampling exponent */
typedef struct GPUSssKernelData {
@ -493,10 +494,10 @@ typedef struct GPUSssKernelData {
float radii_n[3], max_radius;
} GPUSssKernelData;
static void sss_calculate_offsets(GPUSssKernelData *kd)
static void sss_calculate_offsets(GPUSssKernelData *kd, int count)
{
float step = 2.0f / (float)(SSS_SAMPLES - 1);
for (int i = 0; i < SSS_SAMPLES; i++) {
float step = 2.0f / (float)(count - 1);
for (int i = 0; i < count; 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));
@ -543,7 +544,7 @@ static float gaussian_integral(float x0, float x1) {
return gaussian_primitive(x0) - gaussian_primitive(x1);
}
static void compute_sss_kernel(GPUSssKernelData *kd, float *radii)
static void compute_sss_kernel(GPUSssKernelData *kd, float *radii, int sample_ct)
{
/* Normalize size */
copy_v3_v3(kd->radii_n, radii);
@ -551,7 +552,7 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii)
mul_v3_fl(kd->radii_n, 1.0f / kd->max_radius);
/* Compute samples locations on the 1d kernel */
sss_calculate_offsets(kd);
sss_calculate_offsets(kd, sample_ct);
#if 0 /* Maybe used for other distributions */
/* Calculate areas (using importance-sampling) */
@ -563,7 +564,7 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii)
float sum[3] = {0.0f, 0.0f, 0.0f};
/* Compute interpolated weights */
for (int i = 0; i < SSS_SAMPLES; i++) {
for (int i = 0; i < sample_ct; i++) {
float x0, x1;
if (i == 0) {
@ -573,8 +574,8 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii)
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;
if (i == sample_ct - 1) {
x1 = kd->kernel[sample_ct - 1][3] + abs(kd->kernel[sample_ct - 2][3] - kd->kernel[sample_ct - 1][3]) / 2.0f;
}
else {
x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f;
@ -590,7 +591,7 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii)
}
/* Normalize */
for (int i = 0; i < SSS_SAMPLES; i++) {
for (int i = 0; i < sample_ct; i++) {
kd->kernel[i][0] /= sum[0];
kd->kernel[i][1] /= sum[1];
kd->kernel[i][2] /= sum[2];
@ -598,8 +599,8 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii)
/* 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(tmpv, kd->kernel[sample_ct / 2]);
for (int i = sample_ct / 2; i > 0; i--) {
copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]);
}
copy_v4_v4(kd->kernel[0], tmpv);
@ -616,24 +617,24 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float *radii)
}
}
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)
struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct)
{
if (material->sss_dirty) {
GPU_material_sss_profile_update(material);
if (material->sss_radii == NULL)
return NULL;
if (material->sss_dirty || (material->sss_samples != sample_ct)) {
GPUSssKernelData kd;
compute_sss_kernel(&kd, material->sss_radii, sample_ct);
/* Update / Create UBO */
GPU_uniformbuffer_update(material->sss_profile, &kd);
material->sss_samples = sample_ct;
material->sss_dirty = false;
}
return material->sss_profile;
}

View File

@ -368,6 +368,8 @@ 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_INT(sss_samples)
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(sss_jitter_threshold)
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)
@ -1204,6 +1206,22 @@ 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");
prop = RNA_def_property(srna, "sss_samples", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs(prop, "rna_LayerEngineSettings_Eevee_sss_samples_get",
"rna_LayerEngineSettings_Eevee_sss_samples_set", NULL);
RNA_def_property_ui_text(prop, "Samples", "Number of samples to compute the scattering effect");
RNA_def_property_range(prop, 1, 32);
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
prop = RNA_def_property(srna, "sss_jitter_threshold", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Eevee_sss_jitter_threshold_get",
"rna_LayerEngineSettings_Eevee_sss_jitter_threshold_set", NULL);
RNA_def_property_ui_text(prop, "Jitter Threshold", "Rotate samples that are below this threshold");
RNA_def_property_range(prop, 0.0f, 1.0f);
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",