From d0d2d156770dc8726df4cf7587042a6b772d7032 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 7 Mar 2023 20:49:27 +0100 Subject: [PATCH 01/21] Copy eeevee-rewrite sss implementation --- source/blender/draw/CMakeLists.txt | 3 + .../draw/engines/eevee_next/eevee_instance.cc | 1 + .../draw/engines/eevee_next/eevee_instance.hh | 3 + .../engines/eevee_next/eevee_shader_shared.hh | 1 + .../engines/eevee_next/eevee_subsurface.cc | 195 ++++++++++++++++++ .../engines/eevee_next/eevee_subsurface.hh | 69 +++++++ .../shaders/eevee_subsurface_eval_frag.glsl | 132 ++++++++++++ .../shaders/infos/eevee_material_info.hh | 19 ++ 8 files changed, 423 insertions(+) create mode 100644 source/blender/draw/engines/eevee_next/eevee_subsurface.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_subsurface.hh create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c6f8dcf335f..fea78229609 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -153,6 +153,7 @@ set(SRC engines/eevee_next/eevee_shader.cc engines/eevee_next/eevee_shadow.cc engines/eevee_next/eevee_sync.cc + engines/eevee_next/eevee_subsurface.cc engines/eevee_next/eevee_velocity.cc engines/eevee_next/eevee_view.cc engines/eevee_next/eevee_world.cc @@ -284,6 +285,7 @@ set(SRC engines/eevee_next/eevee_shader.hh engines/eevee_next/eevee_shadow.hh engines/eevee_next/eevee_sync.hh + engines/eevee_next/eevee_subsurface.hh engines/eevee_next/eevee_velocity.hh engines/eevee_next/eevee_view.hh engines/eevee_next/eevee_world.hh @@ -489,6 +491,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl + engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl engines/eevee_next/shaders/eevee_surf_depth_frag.glsl engines/eevee_next/shaders/eevee_surf_forward_frag.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 95bcf92d32c..530dc444ce3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -204,6 +204,7 @@ void Instance::end_sync() shadows.end_sync(); /** \note: Needs to be before lights. */ lights.end_sync(); sampling.end_sync(); + subsurface.end_sync(); film.end_sync(); cryptomatte.end_sync(); pipelines.end_sync(); diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 8e7855961e4..859660bd26d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -30,6 +30,7 @@ #include "eevee_sampling.hh" #include "eevee_shader.hh" #include "eevee_shadow.hh" +#include "eevee_subsurface.hh" #include "eevee_sync.hh" #include "eevee_view.hh" #include "eevee_world.hh" @@ -48,6 +49,7 @@ class Instance { ShaderModule &shaders; SyncModule sync; MaterialModule materials; + SubsurfaceModule subsurface; PipelineModule pipelines; ShadowModule shadows; LightModule lights; @@ -94,6 +96,7 @@ class Instance { : shaders(*ShaderModule::module_get()), sync(*this), materials(*this), + subsurface(*this), pipelines(*this), shadows(*this), lights(*this), diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 91a23253401..0e8bc360a51 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -965,6 +965,7 @@ using ShadowPageCacheBuf = draw::StorageArrayBuffer; using ShadowTileMapClipBuf = draw::StorageArrayBuffer; using ShadowTileDataBuf = draw::StorageArrayBuffer; +using SubsurfaceDataBuf = draw::UniformBuffer; using VelocityGeometryBuf = draw::StorageArrayBuffer; using VelocityIndexBuf = draw::StorageArrayBuffer; using VelocityObjectBuf = draw::StorageArrayBuffer; diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc new file mode 100644 index 00000000000..d30bc6a95fb --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + */ + +#include "BLI_vector.hh" + +#include "eevee_instance.hh" +#include "eevee_subsurface.hh" + +#include + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Subsurface + * + * \{ */ + +/* TODO(fclem) Only enable this module if there is any SSS object in the scene. */ +void SubsurfaceModule::end_sync() +{ + data_.jitter_threshold = inst_.scene->eevee.sss_jitter_threshold; + if (data_.sample_len != inst_.scene->eevee.sss_samples) { + /* Convert sample count from old implementation which was using a separable filter. */ + /* TODO(fclem) better remapping. */ + // data_.sample_len = square_f(1 + 2 * inst_.scene->eevee.sss_samples); + data_.sample_len = 55; + } + + if (transmittance_tx == nullptr) { + precompute_transmittance_profile(); + } + + precompute_samples_location(); + + data_.push_update(); +} + +void SubsurfaceModule::precompute_samples_location() +{ + /* Precompute sample position with white albedo. */ + float d = burley_setup(1.0f, 1.0f); + + float rand_u = inst_.sampling.rng_get(SAMPLING_SSS_U); + float rand_v = inst_.sampling.rng_get(SAMPLING_SSS_V); + + double golden_angle = M_PI * (3.0 - sqrt(5.0)); + for (auto i : IndexRange(data_.sample_len)) { + float theta = golden_angle * i + M_PI * 2.0f * rand_u; + /* Scale using rand_v in order to keep first sample always at center. */ + float x = (1.0f + (rand_v / data_.sample_len)) * (i / (float)data_.sample_len); + float r = burley_sample(d, x); + data_.samples[i].x = cosf(theta) * r; + data_.samples[i].y = sinf(theta) * r; + data_.samples[i].z = 1.0f / burley_pdf(d, r); + } +} + +void SubsurfaceModule::precompute_transmittance_profile() +{ + Vector profile(SSS_TRANSMIT_LUT_SIZE); + + /* Precompute sample position with white albedo. */ + float radius = 1.0f; + float d = burley_setup(radius, 1.0f); + + /* For each distance d we compute the radiance incoming from an hypothetical parallel plane. */ + for (auto i : IndexRange(SSS_TRANSMIT_LUT_SIZE)) { + /* Distance from the lit surface plane. + * Compute to a larger maximum distance to have a smoother falloff for all channels. */ + float lut_radius = SSS_TRANSMIT_LUT_RADIUS * radius; + float distance = lut_radius * (i + 1e-5f) / profile.size(); + /* Compute radius of the footprint on the hypothetical plane. */ + float r_fp = sqrtf(square_f(lut_radius) - square_f(distance)); + + profile[i] = 0.0f; + float area_accum = 0.0f; + for (auto j : IndexRange(SSS_TRANSMIT_LUT_STEP_RES)) { + /* Compute distance to the "shading" point through the medium. */ + float r = (r_fp * (j + 0.5f)) / SSS_TRANSMIT_LUT_STEP_RES; + float r_prev = (r_fp * (j + 0.0f)) / SSS_TRANSMIT_LUT_STEP_RES; + float r_next = (r_fp * (j + 1.0f)) / SSS_TRANSMIT_LUT_STEP_RES; + r = hypotf(r, distance); + float R = burley_eval(d, r); + /* Since the profile and configuration are radially symmetrical we + * can just evaluate it once and weight it accordingly */ + float disk_area = square_f(r_next) - square_f(r_prev); + + profile[i] += R * disk_area; + area_accum += disk_area; + } + /* Normalize over the disk. */ + profile[i] /= area_accum; + } + /* Make a smooth gradient from 1 to 0. */ + float range = profile.first() - profile.last(); + float offset = profile.last(); + for (float &value : profile) { + value = (value - offset) / range; + } + profile.first() = 1; + profile.last() = 0; + + transmittance_tx = GPU_texture_create_1d("SSSTransmittanceProfile", + profile.size(), + 1, + GPU_R16F, + GPU_TEXTURE_USAGE_SHADER_READ, + profile.data()); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Christensen-Burley SSS model + * + * Based on: "Approximate Reflectance Profiles for Efficient Subsurface Scattering" + * by Per Christensen + * https://graphics.pixar.com/library/ApproxBSSRDF/approxbssrdfslides.pdf + * \{ */ + +float SubsurfaceModule::burley_setup(float radius, float albedo) +{ + float A = albedo; + /* Diffuse surface transmission, equation (6). */ + float s = 1.9f - A + 3.5f * square_f(A - 0.8f); + /* Mean free path length adapted to fit ancient Cubic and Gaussian models. */ + float l = 0.25 * M_1_PI * radius; + + return l / s; +} + +float SubsurfaceModule::burley_sample(float d, float x_rand) +{ + x_rand *= SSS_BURLEY_TRUNCATE_CDF; + + const float tolerance = 1e-6; + const int max_iteration_count = 10; + /* Do initial guess based on manual curve fitting, this allows us to reduce + * number of iterations to maximum 4 across the [0..1] range. We keep maximum + * number of iteration higher just to be sure we didn't miss root in some + * corner case. + */ + float r; + if (x_rand <= 0.9) { + r = exp(x_rand * x_rand * 2.4) - 1.0; + } + else { + /* TODO(sergey): Some nicer curve fit is possible here. */ + r = 15.0; + } + /* Solve against scaled radius. */ + for (int i = 0; i < max_iteration_count; i++) { + float exp_r_3 = exp(-r / 3.0); + float exp_r = exp_r_3 * exp_r_3 * exp_r_3; + float f = 1.0 - 0.25 * exp_r - 0.75 * exp_r_3 - x_rand; + float f_ = 0.25 * exp_r + 0.25 * exp_r_3; + + if (abs(f) < tolerance || f_ == 0.0) { + break; + } + + r = r - f / f_; + if (r < 0.0) { + r = 0.0; + } + } + + return r * d; +} + +float SubsurfaceModule::burley_eval(float d, float r) +{ + if (r >= SSS_BURLEY_TRUNCATE * d) { + return 0.0; + } + /* Slide 33. */ + float exp_r_3_d = expf(-r / (3.0f * d)); + float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; + return (exp_r_d + exp_r_3_d) / (8.0f * (float)M_PI * d); +} + +float SubsurfaceModule::burley_pdf(float d, float r) +{ + return burley_eval(d, r) / SSS_BURLEY_TRUNCATE_CDF; +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh new file mode 100644 index 00000000000..7b0deb064dd --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + */ + +#pragma once + +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Subsurface + * + * \{ */ + +class Instance; + +struct SubsurfaceModule { + private: + Instance &inst_; + /** Contains samples locations. */ + SubsurfaceDataBuf data_; + /** Contains translucence profile for a single color channel. */ + GPUTexture *transmittance_tx = nullptr; + + public: + SubsurfaceModule(Instance &inst) : inst_(inst) + { + /* Force first update. */ + data_.sample_len = -1; + }; + + ~SubsurfaceModule() + { + GPU_TEXTURE_FREE_SAFE(transmittance_tx); + }; + + void end_sync(); + + const GPUUniformBuf *ubo_get(void) const + { + return data_; + } + + GPUTexture **transmittance_ref_get(void) + { + return &transmittance_tx; + } + + private: + void precompute_samples_location(); + void precompute_transmittance_profile(); + + /** Christensen-Burley implementation. */ + static float burley_setup(float radius, float albedo); + static float burley_sample(float d, float x_rand); + static float burley_eval(float d, float r); + static float burley_pdf(float d, float r); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl new file mode 100644 index 00000000000..54907553d26 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl @@ -0,0 +1,132 @@ + +/** + * Postprocess diffuse radiance output from the diffuse evaluation pass to mimic subsurface + * transmission. + * + * This implementation follows the technique described in the siggraph presentation: + * "Efficient screen space subsurface scattering Siggraph 2018" + * by Evgenii Golubev + * + * But, instead of having all the precomputed weights for all three color primaries, + * we precompute a weight profile texture to be able to support per pixel AND per channel radius. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) + +vec3 burley_setup(vec3 radius, vec3 albedo) +{ + /* Scale albedo because we can have HDR value caused by BSDF sampling. */ + vec3 A = albedo / max(1e-6, max_v3(albedo)); + /* Diffuse surface transmission, equation (6). */ + vec3 s = 1.9 - A + 3.5 * sqr(A - 0.8); + /* Mean free path length adapted to fit ancient Cubic and Gaussian models. */ + vec3 l = 0.25 * M_1_PI * radius; + + return l / s; +} + +vec3 burley_eval(vec3 d, float r) +{ + /* Slide 33. */ + vec3 exp_r_3_d = exp(-r / (3.0 * d)); + vec3 exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; + /** NOTE: + * - Surface albedo is applied at the end. + * - This is normalized diffuse model, so the equation is multiplied + * by 2*pi, which also matches cdf(). + */ + return (exp_r_d + exp_r_3_d) / (4.0 * d); +} + +void main(void) +{ + vec2 center_uv = uvcoordsvar.xy; + ivec2 texel = ivec2(gl_FragCoord.xy); + + float gbuffer_depth = texelFetch(hiz_tx, texel, 0).r; + vec3 vP = get_view_space_from_depth(center_uv, gbuffer_depth); + vec4 tra_col_in = texelFetch(transmit_color_tx, texel, 0); + vec4 tra_nor_in = texelFetch(transmit_normal_tx, texel, 0); + vec4 tra_dat_in = texelFetch(transmit_data_tx, texel, 0); + + ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); + + if (diffuse.sss_id == 0u) { + /* Normal diffuse is already in combined pass. */ + /* Refraction also go into this case. */ + out_combined = vec4(0.0); + return; + } + + float max_radius = max_v3(diffuse.sss_radius); + + float homcoord = ProjectionMatrix[2][3] * vP.z + ProjectionMatrix[3][3]; + vec2 sample_scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * + (0.5 * max_radius / homcoord); + + float pixel_footprint = sample_scale.x * drw_view.viewport_size.x; + if (pixel_footprint <= 1.0) { + /* Early out. */ + out_combined = vec4(texture(radiance_tx, center_uv).rgb * diffuse.color, 0.0); + return; + } + + diffuse.sss_radius = max(vec3(1e-4), diffuse.sss_radius / max_radius) * max_radius; + vec3 d = burley_setup(diffuse.sss_radius, diffuse.color); + + /* Do not rotate too much to avoid too much cache misses. */ + float golden_angle = M_PI * (3.0 - sqrt(5.0)); + float theta = interlieved_gradient_noise(gl_FragCoord.xy, 0, 0.0) * golden_angle; + float cos_theta = cos(theta); + float sin_theta = sqrt(1.0 - sqr(cos_theta)); + mat2 rot = mat2(cos_theta, sin_theta, -sin_theta, cos_theta); + + mat2 scale = mat2(sample_scale.x, 0.0, 0.0, sample_scale.y); + mat2 sample_space = scale * rot; + + vec3 accum_weight = vec3(0.0); + vec3 accum = vec3(0.0); + + /* TODO/OPTI(fclem) Make separate sample set for lower radius. */ + + for (int i = 0; i < sss_buf.sample_len; i++) { + vec2 sample_uv = center_uv + sample_space * sss_buf.samples[i].xy; + float pdf_inv = sss_buf.samples[i].z; + + float sample_depth = textureLod(hiz_tx, sample_uv * hiz_buf.uv_scale, 0.0).r; + vec3 sample_vP = get_view_space_from_depth(sample_uv, sample_depth); + + vec4 sample_data = texture(radiance_tx, sample_uv); + vec3 sample_radiance = sample_data.rgb; + uint sample_sss_id = uint(sample_data.a); + + if (sample_sss_id != diffuse.sss_id) { + continue; + } + + /* Discard out of bounds samples. */ + if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) { + continue; + } + + /* Slide 34. */ + float r = distance(sample_vP, vP); + vec3 weight = burley_eval(d, r) * pdf_inv; + + accum += sample_radiance * weight; + accum_weight += weight; + } + /* Normalize the sum (slide 34). */ + accum /= accum_weight; + /* Apply surface color on final radiance. */ + accum *= diffuse.color; + + /* Debug, detect NaNs. */ + if (any(isnan(accum))) { + accum = vec3(1.0, 0.0, 1.0); + } + + out_combined = vec4(accum, 0.0); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index f16113a5efc..b4f1125fabf 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -180,6 +180,25 @@ GPU_SHADER_CREATE_INFO(eevee_surf_shadow) .fragment_source("eevee_surf_shadow_frag.glsl") .additional_info("eevee_camera", "eevee_utility_texture", "eevee_sampling_data"); +GPU_SHADER_CREATE_INFO(eevee_transmittance_data) + .define("SSS_TRANSMITTANCE") + .sampler(0, ImageType::FLOAT_1D, "sss_transmittance_tx"); + +GPU_SHADER_CREATE_INFO(eevee_subsurface_eval) + .do_static_compilation(true) + .additional_info("eevee_shared") + .uniform_buf(2, "SubsurfaceData", "sss_buf") + .uniform_buf(1, "HiZData", "hiz_buf") + .sampler(0, ImageType::FLOAT_2D, "hiz_tx") + .sampler(1, ImageType::FLOAT_2D, "radiance_tx") + .sampler(2, ImageType::FLOAT_2D, "transmit_color_tx") + .sampler(3, ImageType::FLOAT_2D, "transmit_normal_tx") + .sampler(4, ImageType::FLOAT_2D, "transmit_data_tx") + .fragment_out(0, Type::VEC4, "out_combined") + .fragment_source("eevee_subsurface_eval_frag.glsl") + /* TODO(fclem) Output to diffuse pass without feedback loop. */ + .additional_info("draw_fullscreen", "draw_view"); + #undef image_out #undef image_array_out -- 2.30.2 From 154156a44d5b7a4f2c22e00a7358950fe75a3cfe Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Fri, 21 Apr 2023 19:48:35 +0200 Subject: [PATCH 02/21] Initial implementation --- .../draw/engines/eevee_next/eevee_pipeline.cc | 29 +++++++++- .../draw/engines/eevee_next/eevee_pipeline.hh | 1 + .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 2 + .../engines/eevee_next/eevee_subsurface.cc | 1 - .../engines/eevee_next/eevee_subsurface.hh | 5 ++ .../shaders/eevee_deferred_light_frag.glsl | 57 ++++++++----------- .../shaders/eevee_subsurface_eval_frag.glsl | 15 +++-- .../shaders/infos/eevee_material_info.hh | 11 ++-- 9 files changed, 76 insertions(+), 47 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 2b80070b5ef..eceaff5fe0c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -369,10 +369,10 @@ void DeferredLayer::end_sync() DRWState state_write_color = state | DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; if (closure_bits_ & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)) { - const bool is_last_eval_pass = true; + const bool is_last_eval_pass = !(closure_bits_ & CLOSURE_SSS); eval_light_ps_.init(); - eval_light_ps_.state_set(is_last_eval_pass ? state_write_color : state); + eval_light_ps_.state_set(state_write_color); eval_light_ps_.state_stencil(0x00u, 0x01u, 0xFFu); eval_light_ps_.shader_set(inst_.shaders.static_shader_get(DEFERRED_LIGHT)); eval_light_ps_.bind_image("out_diffuse_light_img", &diffuse_light_tx_); @@ -391,6 +391,26 @@ void DeferredLayer::end_sync() eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); } + + if (closure_bits_ & CLOSURE_SSS) { + inst_.subsurface.end_sync(); + + subsurface_ps_.init(); + subsurface_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | + DRW_STATE_BLEND_ADD_FULL); + subsurface_ps_.state_stencil(0x00u, 0x01u, 0xFFu); + /* TODO(Miguel Pozo): */ + /*subsurface_ps_.state_stencil(0x0, 0xFF, CLOSURE_SSS);*/ + subsurface_ps_.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_EVAL)); + inst_.subsurface.bind_resources(&subsurface_ps_); + inst_.hiz_buffer.bind_resources(&subsurface_ps_); + subsurface_ps_.bind_texture("radiance_tx", &diffuse_light_tx_); + subsurface_ps_.bind_texture("gbuffer_closure_tx", &inst_.gbuffer.closure_tx); + subsurface_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx); + + subsurface_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); + subsurface_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); + } } PassMain::Sub *DeferredLayer::prepass_add(::Material *blender_mat, @@ -421,7 +441,6 @@ void DeferredLayer::render(View &view, Framebuffer &combined_fb, int2 extent) { - GPU_framebuffer_bind(prepass_fb); inst_.manager->submit(prepass_ps_, view); @@ -441,6 +460,10 @@ void DeferredLayer::render(View &view, inst_.manager->submit(eval_light_ps_, view); + if (closure_bits_ & CLOSURE_SSS) { + inst_.manager->submit(subsurface_ps_, view); + } + diffuse_light_tx_.release(); specular_light_tx_.release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index c09f5fe99cf..c76be9da5f8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -132,6 +132,7 @@ class DeferredLayer { PassMain::Sub *gbuffer_double_sided_ps_ = nullptr; PassSimple eval_light_ps_ = {"EvalLights"}; + PassSimple subsurface_ps_ = {"Subsurface"}; /* Closures bits from the materials in this pass. */ eClosureBits closure_bits_; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 26d9642a525..30eafff73c9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -172,6 +172,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_shadow_tag_usage_opaque"; case SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT: return "eevee_shadow_tag_usage_transparent"; + case SUBSURFACE_EVAL: + return "eevee_subsurface_eval"; /* To avoid compiler warning about missing case. */ case MAX_SHADER_TYPE: return ""; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index d987105e3fc..fdceef4055f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -80,6 +80,8 @@ enum eShaderType { SHADOW_TILEMAP_TAG_USAGE_OPAQUE, SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT, + SUBSURFACE_EVAL, + MAX_SHADER_TYPE, }; diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc index d30bc6a95fb..1c19155ae9a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -21,7 +21,6 @@ namespace blender::eevee { * * \{ */ -/* TODO(fclem) Only enable this module if there is any SSS object in the scene. */ void SubsurfaceModule::end_sync() { data_.jitter_threshold = inst_.scene->eevee.sss_jitter_threshold; diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh index 7b0deb064dd..ad7f7dddcdc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh @@ -43,6 +43,11 @@ struct SubsurfaceModule { void end_sync(); + template void bind_resources(draw::detail::PassBase *pass) + { + pass->bind_ubo("sss_buf", data_); + } + const GPUUniformBuf *ubo_get(void) const { return data_; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index 80dcaaf67cb..ddf27e28fdc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -42,11 +42,10 @@ void main() * produces a complete diffuse light buffer that will be correctly convolved by the SSSS. * The refraction pixels will just set the diffuse radiance to 0. */ } - else if (false /* TODO */) { + else if (textureSize(gbuffer_closure_tx, 0).z >= 3) { vec4 gbuffer_2_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 2), 0); - diffuse_data.sss_radius = gbuffer_sss_radii_unpack(gbuffer_2_packed.xyz); diffuse_data.sss_id = gbuffer_object_id_unorm16_unpack(gbuffer_2_packed.w); - thickness = gbuffer_thickness_pack(gbuffer_1_packed.z); + thickness = gbuffer_thickness_unpack(gbuffer_1_packed.z); } vec3 diffuse_light = vec3(0.0); @@ -55,30 +54,7 @@ void main() light_eval( diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light); - if (is_last_eval_pass) { - /* Apply color and output lighting to render-passes. */ - vec4 color_0_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 0), 0); - vec4 color_1_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 1), 0); - - reflection_data.color = gbuffer_color_unpack(color_0_packed); - diffuse_data.color = gbuffer_color_unpack(color_1_packed); - - if (is_refraction) { - diffuse_data.color = vec3(0.0); - } - - reflection_light *= reflection_data.color; - diffuse_light *= diffuse_data.color; - /* Add radiance to light pass. */ - imageStore( - rp_light_img, ivec3(texel, RENDER_PASS_LAYER_DIFFUSE_LIGHT), vec4(diffuse_light, 1.0)); - imageStore( - rp_light_img, ivec3(texel, RENDER_PASS_LAYER_SPECULAR_LIGHT), vec4(reflection_light, 1.0)); - /* Add radiance to combined pass. */ - out_radiance = vec4(diffuse_light + reflection_light, 0.0); - out_transmittance = vec4(1.0); - } - else { + if (!is_last_eval_pass) { /* Store lighting for next deferred pass. */ /* Output diffuse light along with object ID for sub-surface screen space processing. */ @@ -88,10 +64,27 @@ void main() imageStore(out_diffuse_light_img, texel, diffuse_radiance); imageStore(out_specular_light_img, texel, vec4(reflection_light, 0.0)); - - /* Final radiance will be amended by the last pass. - * This should do nothing as color write should be disabled in this case. */ - out_radiance = vec4(0.0); - out_transmittance = vec4(0.0); } + + /* Apply color and output lighting to render-passes. */ + vec4 color_0_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 0), 0); + vec4 color_1_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 1), 0); + + reflection_data.color = gbuffer_color_unpack(color_0_packed); + diffuse_data.color = gbuffer_color_unpack(color_1_packed); + + if (is_refraction) { + diffuse_data.color = vec3(0.0); + } + + reflection_light *= reflection_data.color; + diffuse_light *= diffuse_data.color; + /* Add radiance to light pass. */ + imageStore( + rp_light_img, ivec3(texel, RENDER_PASS_LAYER_DIFFUSE_LIGHT), vec4(diffuse_light, 1.0)); + imageStore( + rp_light_img, ivec3(texel, RENDER_PASS_LAYER_SPECULAR_LIGHT), vec4(reflection_light, 1.0)); + /* Add radiance to combined pass. */ + out_radiance = vec4(diffuse_light + reflection_light, 0.0); + out_transmittance = vec4(1.0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl index 54907553d26..6a3cd6a24a4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl @@ -11,6 +11,8 @@ * we precompute a weight profile texture to be able to support per pixel AND per channel radius. **/ +#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) @@ -47,11 +49,14 @@ void main(void) float gbuffer_depth = texelFetch(hiz_tx, texel, 0).r; vec3 vP = get_view_space_from_depth(center_uv, gbuffer_depth); - vec4 tra_col_in = texelFetch(transmit_color_tx, texel, 0); - vec4 tra_nor_in = texelFetch(transmit_normal_tx, texel, 0); - vec4 tra_dat_in = texelFetch(transmit_data_tx, texel, 0); - ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); + vec4 color_1_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 1), 0); + vec4 gbuffer_2_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 2), 0); + + ClosureDiffuse diffuse; + diffuse.sss_radius = gbuffer_sss_radii_unpack(gbuffer_2_packed.xyz); + diffuse.sss_id = gbuffer_object_id_unorm16_unpack(gbuffer_2_packed.w); + diffuse.color = gbuffer_color_unpack(color_1_packed); if (diffuse.sss_id == 0u) { /* Normal diffuse is already in combined pass. */ @@ -66,7 +71,7 @@ void main(void) vec2 sample_scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * (0.5 * max_radius / homcoord); - float pixel_footprint = sample_scale.x * drw_view.viewport_size.x; + float pixel_footprint = sample_scale.x * textureSize(hiz_tx, 0).x; if (pixel_footprint <= 1.0) { /* Early out. */ out_combined = vec4(texture(radiance_tx, center_uv).rgb * diffuse.color, 0.0); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index b4f1125fabf..695c28628ac 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -187,13 +187,12 @@ GPU_SHADER_CREATE_INFO(eevee_transmittance_data) GPU_SHADER_CREATE_INFO(eevee_subsurface_eval) .do_static_compilation(true) .additional_info("eevee_shared") - .uniform_buf(2, "SubsurfaceData", "sss_buf") - .uniform_buf(1, "HiZData", "hiz_buf") + .uniform_buf(1, "SubsurfaceData", "sss_buf") + .uniform_buf(2, "HiZData", "hiz_buf") .sampler(0, ImageType::FLOAT_2D, "hiz_tx") - .sampler(1, ImageType::FLOAT_2D, "radiance_tx") - .sampler(2, ImageType::FLOAT_2D, "transmit_color_tx") - .sampler(3, ImageType::FLOAT_2D, "transmit_normal_tx") - .sampler(4, ImageType::FLOAT_2D, "transmit_data_tx") + .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") + .sampler(2, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") + .sampler(3, ImageType::FLOAT_2D, "radiance_tx") .fragment_out(0, Type::VEC4, "out_combined") .fragment_source("eevee_subsurface_eval_frag.glsl") /* TODO(fclem) Output to diffuse pass without feedback loop. */ -- 2.30.2 From c13ce1dc06d819279acc40ba98aebf11886b7384 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Fri, 21 Apr 2023 19:48:57 +0200 Subject: [PATCH 03/21] Fix sss_id --- .../engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl index d84d0a030e2..461bf5fcebb 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl @@ -123,7 +123,7 @@ void main() /* SubSurface Scattering. */ vec4 closure; closure.xyz = gbuffer_sss_radii_pack(g_diffuse_data.sss_radius); - closure.w = gbuffer_object_id_unorm16_pack(g_diffuse_data.sss_id); + closure.w = gbuffer_object_id_unorm16_pack(g_diffuse_data.sss_id > 0 ? uint(resource_id) : 0); imageStore(out_gbuff_closure_img, ivec3(out_texel, 2), closure); } -- 2.30.2 From e6ee38d218d30fe3fe8ba2d060f186a0d1bbf74a Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Mon, 24 Apr 2023 20:06:12 +0200 Subject: [PATCH 04/21] SSS transmittance --- source/blender/draw/engines/eevee_next/eevee_defines.hh | 2 ++ source/blender/draw/engines/eevee_next/eevee_pipeline.cc | 6 ++++++ source/blender/draw/engines/eevee_next/eevee_shader.cc | 4 ++++ .../eevee_next/shaders/eevee_deferred_light_frag.glsl | 1 + .../engines/eevee_next/shaders/eevee_light_eval_lib.glsl | 2 +- .../engines/eevee_next/shaders/eevee_surf_forward_frag.glsl | 4 +++- .../engines/eevee_next/shaders/infos/eevee_deferred_info.hh | 1 + .../engines/eevee_next/shaders/infos/eevee_material_info.hh | 2 +- 8 files changed, 19 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index fd7fd7b55b6..101403513c8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -95,6 +95,8 @@ /* Only during shadow rendering. */ #define SHADOW_RENDER_MAP_SLOT 13 #define RBUFS_UTILITY_TEX_SLOT 14 +/* TODO(Miguel Pozo): Set the correct slot. */ +#define SSS_TRANSMITTANCE_TEX_SLOT 28 /* Images. */ #define RBUFS_NORMAL_SLOT 0 diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index eceaff5fe0c..297a1f0050a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -156,6 +156,9 @@ void ForwardPipeline::sync() opaque_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info); /* Textures. */ opaque_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + opaque_ps_.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, + inst_.subsurface.transmittance_ref_get()); + /* Uniform Buffer. */ opaque_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); @@ -182,6 +185,7 @@ void ForwardPipeline::sync() /* Textures. */ sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + sub.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, inst_.subsurface.transmittance_ref_get()); /* Uniform Buffer. */ sub.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); @@ -382,6 +386,8 @@ void DeferredLayer::end_sync() eval_light_ps_.push_constant("is_last_eval_pass", is_last_eval_pass); eval_light_ps_.bind_image(RBUFS_LIGHT_SLOT, &inst_.render_buffers.light_tx); eval_light_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + eval_light_ps_.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, + inst_.subsurface.transmittance_ref_get()); inst_.lights.bind_resources(&eval_light_ps_); inst_.shadows.bind_resources(&eval_light_ps_); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 30eafff73c9..5aaf090c7b6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -267,6 +267,10 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu info.additional_info("eevee_cryptomatte_out"); } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE) && pipeline_type == MAT_PIPE_FORWARD) { + info.additional_info("eevee_transmittance_data"); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC)) { switch (geometry_type) { case MAT_GEOM_MESH: diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index ddf27e28fdc..f626cbe8dbc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -44,6 +44,7 @@ void main() } else if (textureSize(gbuffer_closure_tx, 0).z >= 3) { vec4 gbuffer_2_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 2), 0); + diffuse_data.sss_radius = gbuffer_sss_radii_unpack(gbuffer_2_packed.xyz); diffuse_data.sss_id = gbuffer_object_id_unorm16_unpack(gbuffer_2_packed.w); thickness = gbuffer_thickness_unpack(gbuffer_1_packed.z); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl index 4a57a850d34..a9a840da242 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl @@ -46,7 +46,7 @@ void light_eval_ex(ClosureDiffuse diffuse, #ifdef SSS_TRANSMITTANCE /* Transmittance evaluation first to use initial visibility without shadow. */ if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) { - float delta = max(thickness, samp.occluder_delta + samp.bias); + float delta = max(thickness, -(samp.occluder_delta + samp.bias)); vec3 intensity = visibility * light.transmit_power * light_translucent(sss_transmittance_tx, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl index dad1faad0b4..7487ccad4e2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -58,6 +58,8 @@ void main() g_holdout = saturate(g_holdout); + float thickness = nodetree_thickness(); + vec3 diffuse_light = vec3(0.0); vec3 reflection_light = vec3(0.0); vec3 refraction_light = vec3(0.0); @@ -70,7 +72,7 @@ void main() g_data.Ng, cameraVec(g_data.P), vP_z, - 0.01 /* TODO(fclem) thickness. */, + thickness, diffuse_light, reflection_light); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh index f5bc967e5b6..eea38136e0e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh @@ -39,6 +39,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light) "eevee_light_data", "eevee_shadow_data", "eevee_deferred_base", + "eevee_transmittance_data", "eevee_hiz_data", "draw_view", "draw_fullscreen") diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 695c28628ac..d1a5f3358b2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -182,7 +182,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_shadow) GPU_SHADER_CREATE_INFO(eevee_transmittance_data) .define("SSS_TRANSMITTANCE") - .sampler(0, ImageType::FLOAT_1D, "sss_transmittance_tx"); + .sampler(SSS_TRANSMITTANCE_TEX_SLOT, ImageType::FLOAT_1D, "sss_transmittance_tx"); GPU_SHADER_CREATE_INFO(eevee_subsurface_eval) .do_static_compilation(true) -- 2.30.2 From ac61b698dd11b6535f0dc570e08a0f92c4be8190 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 25 Apr 2023 18:14:23 +0200 Subject: [PATCH 05/21] Fix subsurface_eval energy --- .../eevee_next/shaders/eevee_subsurface_eval_frag.glsl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl index 6a3cd6a24a4..c60aa8685d9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl @@ -74,7 +74,7 @@ void main(void) float pixel_footprint = sample_scale.x * textureSize(hiz_tx, 0).x; if (pixel_footprint <= 1.0) { /* Early out. */ - out_combined = vec4(texture(radiance_tx, center_uv).rgb * diffuse.color, 0.0); + out_combined = vec4(0); return; } @@ -125,6 +125,11 @@ void main(void) } /* Normalize the sum (slide 34). */ accum /= accum_weight; + + /* This pass uses additive blending. + * Subtract the surface diffuse radiance so it's not added twice. */ + accum -= texelFetch(radiance_tx, texel, 0).rgb; + /* Apply surface color on final radiance. */ accum *= diffuse.color; -- 2.30.2 From 8dd009fabb320728b4efb8fc5b075939563c4d8f Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 26 Apr 2023 18:31:28 +0200 Subject: [PATCH 06/21] Fix gbuffer_normal_pack --- .../draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl index 2147c68de83..2182cb26d5d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl @@ -10,7 +10,10 @@ vec2 gbuffer_normal_pack(vec3 N) { N /= length_manhattan(N); - N.xy = (N.z >= 0.0) ? N.xy : ((1.0 - abs(N.yx)) * sign(N.xy)); + vec2 _sign = sign(N.xy); + _sign.x = _sign.x == 0.0 ? 1.0 : _sign.x; + _sign.y = _sign.y == 0.0 ? 1.0 : _sign.y; + N.xy = (N.z >= 0.0) ? N.xy : ((1.0 - abs(N.yx)) * _sign); N.xy = N.xy * 0.5 + 0.5; return N.xy; } -- 2.30.2 From 7db9f0087f0d4addb8ee3a4e42c121d2b5459aa7 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 26 Apr 2023 18:48:30 +0200 Subject: [PATCH 07/21] Cleanup --- .../draw/engines/eevee_next/eevee_pipeline.cc | 16 ++++++---------- .../engines/eevee_next/eevee_subsurface.cc | 10 +++------- .../engines/eevee_next/eevee_subsurface.hh | 18 +++++++++++------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 297a1f0050a..c6dd324eb3f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -156,8 +156,7 @@ void ForwardPipeline::sync() opaque_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info); /* Textures. */ opaque_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); - opaque_ps_.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, - inst_.subsurface.transmittance_ref_get()); + opaque_ps_.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, inst_.subsurface.transmittance_tx_get()); /* Uniform Buffer. */ opaque_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); @@ -185,7 +184,7 @@ void ForwardPipeline::sync() /* Textures. */ sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); - sub.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, inst_.subsurface.transmittance_ref_get()); + sub.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, inst_.subsurface.transmittance_tx_get()); /* Uniform Buffer. */ sub.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); @@ -366,17 +365,14 @@ void DeferredLayer::begin_sync() void DeferredLayer::end_sync() { - /* Use stencil test to reject pixel not written by this layer. */ - /* WORKAROUND: Stencil write is only here to avoid rasterizer discard. */ - DRWState state = DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_EQUAL; - /* Allow output to combined pass for the last pass. */ - DRWState state_write_color = state | DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; if (closure_bits_ & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)) { const bool is_last_eval_pass = !(closure_bits_ & CLOSURE_SSS); eval_light_ps_.init(); - eval_light_ps_.state_set(state_write_color); + /* Use stencil test to reject pixel not written by this layer. */ + eval_light_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | + DRW_STATE_BLEND_CUSTOM); eval_light_ps_.state_stencil(0x00u, 0x01u, 0xFFu); eval_light_ps_.shader_set(inst_.shaders.static_shader_get(DEFERRED_LIGHT)); eval_light_ps_.bind_image("out_diffuse_light_img", &diffuse_light_tx_); @@ -387,7 +383,7 @@ void DeferredLayer::end_sync() eval_light_ps_.bind_image(RBUFS_LIGHT_SLOT, &inst_.render_buffers.light_tx); eval_light_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); eval_light_ps_.bind_texture(SSS_TRANSMITTANCE_TEX_SLOT, - inst_.subsurface.transmittance_ref_get()); + inst_.subsurface.transmittance_tx_get()); inst_.lights.bind_resources(&eval_light_ps_); inst_.shadows.bind_resources(&eval_light_ps_); diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc index 1c19155ae9a..e4b863c21bb 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -31,7 +31,7 @@ void SubsurfaceModule::end_sync() data_.sample_len = 55; } - if (transmittance_tx == nullptr) { + if (!transmittance_tx.is_valid()) { precompute_transmittance_profile(); } @@ -105,12 +105,8 @@ void SubsurfaceModule::precompute_transmittance_profile() profile.first() = 1; profile.last() = 0; - transmittance_tx = GPU_texture_create_1d("SSSTransmittanceProfile", - profile.size(), - 1, - GPU_R16F, - GPU_TEXTURE_USAGE_SHADER_READ, - profile.data()); + transmittance_tx.ensure_1d( + GPU_R16F, profile.size(), GPU_TEXTURE_USAGE_SHADER_READ, profile.data()); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh index ad7f7dddcdc..09e62383b0a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh @@ -5,6 +5,15 @@ /** \file * \ingroup eevee * + * Postprocess diffuse radiance output from the diffuse evaluation pass to mimic subsurface + * transmission. + * + * This implementation follows the technique described in the siggraph presentation: + * "Efficient screen space subsurface scattering Siggraph 2018" + * by Evgenii Golubev + * + * But, instead of having all the precomputed weights for all three color primaries, + * we precompute a weight profile texture to be able to support per pixel AND per channel radius. */ #pragma once @@ -27,7 +36,7 @@ struct SubsurfaceModule { /** Contains samples locations. */ SubsurfaceDataBuf data_; /** Contains translucence profile for a single color channel. */ - GPUTexture *transmittance_tx = nullptr; + Texture transmittance_tx; public: SubsurfaceModule(Instance &inst) : inst_(inst) @@ -48,12 +57,7 @@ struct SubsurfaceModule { pass->bind_ubo("sss_buf", data_); } - const GPUUniformBuf *ubo_get(void) const - { - return data_; - } - - GPUTexture **transmittance_ref_get(void) + GPUTexture **transmittance_tx_get(void) { return &transmittance_tx; } -- 2.30.2 From 8127efbaa40d8f808e642420f087ae7069d557b3 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 26 Apr 2023 19:21:40 +0200 Subject: [PATCH 08/21] Correct transmittance tex slot --- .../blender/draw/engines/eevee_next/eevee_defines.hh | 4 ++-- .../engines/eevee_next/shaders/infos/eevee_hiz_info.hh | 2 +- .../eevee_next/shaders/infos/eevee_material_info.hh | 10 ++++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 101403513c8..b70fe9eaacf 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -95,8 +95,8 @@ /* Only during shadow rendering. */ #define SHADOW_RENDER_MAP_SLOT 13 #define RBUFS_UTILITY_TEX_SLOT 14 -/* TODO(Miguel Pozo): Set the correct slot. */ -#define SSS_TRANSMITTANCE_TEX_SLOT 28 +#define HIZ_TEX_SLOT 15 +#define SSS_TRANSMITTANCE_TEX_SLOT 16 /* Images. */ #define RBUFS_NORMAL_SLOT 0 diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh index 5e32631a8f8..ba25c2b83ca 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh @@ -4,7 +4,7 @@ #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(eevee_hiz_data) - .sampler(15, ImageType::FLOAT_2D, "hiz_tx") + .sampler(HIZ_TEX_SLOT, ImageType::FLOAT_2D, "hiz_tx") .uniform_buf(5, "HiZData", "hiz_buf"); GPU_SHADER_CREATE_INFO(eevee_hiz_update) diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index d1a5f3358b2..7ae20d86745 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -188,15 +188,13 @@ GPU_SHADER_CREATE_INFO(eevee_subsurface_eval) .do_static_compilation(true) .additional_info("eevee_shared") .uniform_buf(1, "SubsurfaceData", "sss_buf") - .uniform_buf(2, "HiZData", "hiz_buf") - .sampler(0, ImageType::FLOAT_2D, "hiz_tx") - .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") - .sampler(2, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") - .sampler(3, ImageType::FLOAT_2D, "radiance_tx") + .sampler(0, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") + .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") + .sampler(2, ImageType::FLOAT_2D, "radiance_tx") .fragment_out(0, Type::VEC4, "out_combined") .fragment_source("eevee_subsurface_eval_frag.glsl") /* TODO(fclem) Output to diffuse pass without feedback loop. */ - .additional_info("draw_fullscreen", "draw_view"); + .additional_info("draw_fullscreen", "draw_view", "eevee_hiz_data"); #undef image_out #undef image_array_out -- 2.30.2 From 5cef5870dd805162a4a9a00872a13b54ebe07677 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 26 Apr 2023 20:16:23 +0200 Subject: [PATCH 09/21] Closure stencil --- .../draw/engines/eevee_next/eevee_pipeline.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index c6dd324eb3f..a20afba3082 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -322,7 +322,7 @@ void DeferredLayer::begin_sync() { gbuffer_ps_.init(); gbuffer_ps_.clear_stencil(0x00u); - gbuffer_ps_.state_stencil(0x01u, 0x01u, 0x01u); + gbuffer_ps_.state_stencil(0xFFu, 0xFFu, 0xFFu); { /* Common resources. */ @@ -371,9 +371,9 @@ void DeferredLayer::end_sync() eval_light_ps_.init(); /* Use stencil test to reject pixel not written by this layer. */ - eval_light_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | + eval_light_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_CUSTOM); - eval_light_ps_.state_stencil(0x00u, 0x01u, 0xFFu); + eval_light_ps_.state_stencil(0x00u, 0x00u, (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)); eval_light_ps_.shader_set(inst_.shaders.static_shader_get(DEFERRED_LIGHT)); eval_light_ps_.bind_image("out_diffuse_light_img", &diffuse_light_tx_); eval_light_ps_.bind_image("out_specular_light_img", &specular_light_tx_); @@ -400,9 +400,7 @@ void DeferredLayer::end_sync() subsurface_ps_.init(); subsurface_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | DRW_STATE_BLEND_ADD_FULL); - subsurface_ps_.state_stencil(0x00u, 0x01u, 0xFFu); - /* TODO(Miguel Pozo): */ - /*subsurface_ps_.state_stencil(0x0, 0xFF, CLOSURE_SSS);*/ + subsurface_ps_.state_stencil(0x00u, 0xFFu, CLOSURE_SSS); subsurface_ps_.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_EVAL)); inst_.subsurface.bind_resources(&subsurface_ps_); inst_.hiz_buffer.bind_resources(&subsurface_ps_); @@ -430,12 +428,15 @@ PassMain::Sub *DeferredLayer::prepass_add(::Material *blender_mat, PassMain::Sub *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat) { - closure_bits_ |= shader_closure_bits_from_flag(gpumat); + eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat); + closure_bits_ |= closure_bits; PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? gbuffer_single_sided_ps_ : gbuffer_double_sided_ps_; - return &pass->sub(GPU_material_get_name(gpumat)); + pass = &pass->sub(GPU_material_get_name(gpumat)); + pass->state_stencil(closure_bits, 0xFFu, 0xFFu); + return pass; } void DeferredLayer::render(View &view, -- 2.30.2 From 7dcfd4cd62fe83dca765344a2b49948b116a1d57 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 26 Apr 2023 20:19:59 +0200 Subject: [PATCH 10/21] Fix texture negative ref count --- source/blender/draw/engines/eevee_next/eevee_subsurface.hh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh index 09e62383b0a..b5a05114735 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh @@ -45,10 +45,7 @@ struct SubsurfaceModule { data_.sample_len = -1; }; - ~SubsurfaceModule() - { - GPU_TEXTURE_FREE_SAFE(transmittance_tx); - }; + ~SubsurfaceModule(){}; void end_sync(); -- 2.30.2 From cfae033fcbbeefc5b55c1ac89d6ebe5874523072 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Thu, 27 Apr 2023 17:54:54 +0200 Subject: [PATCH 11/21] Fix gbuffer_ior_unpack --- .../draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl index 2182cb26d5d..32edcd62a7c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl @@ -35,7 +35,7 @@ float gbuffer_ior_pack(float ior) float gbuffer_ior_unpack(float ior_packed) { - return (ior_packed > 0.5) ? (-1.0 / (ior_packed * 2.0 + 2.0)) : (2.0 * ior_packed); + return (ior_packed > 0.5) ? (0.5 / (1.0 - ior_packed)) : (2.0 * ior_packed); } float gbuffer_thickness_pack(float thickness) -- 2.30.2 From 5194b648c3d9e951a270720d2b35f2df2d50d7bf Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Thu, 27 Apr 2023 18:15:53 +0200 Subject: [PATCH 12/21] Code style --- source/blender/draw/engines/eevee_next/eevee_subsurface.cc | 4 ++-- source/blender/draw/engines/eevee_next/eevee_subsurface.hh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc index e4b863c21bb..bc63f458329 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -31,7 +31,7 @@ void SubsurfaceModule::end_sync() data_.sample_len = 55; } - if (!transmittance_tx.is_valid()) { + if (!transmittance_tx_.is_valid()) { precompute_transmittance_profile(); } @@ -105,7 +105,7 @@ void SubsurfaceModule::precompute_transmittance_profile() profile.first() = 1; profile.last() = 0; - transmittance_tx.ensure_1d( + transmittance_tx_.ensure_1d( GPU_R16F, profile.size(), GPU_TEXTURE_USAGE_SHADER_READ, profile.data()); } diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh index b5a05114735..abfb346032d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh @@ -36,7 +36,7 @@ struct SubsurfaceModule { /** Contains samples locations. */ SubsurfaceDataBuf data_; /** Contains translucence profile for a single color channel. */ - Texture transmittance_tx; + Texture transmittance_tx_; public: SubsurfaceModule(Instance &inst) : inst_(inst) @@ -56,7 +56,7 @@ struct SubsurfaceModule { GPUTexture **transmittance_tx_get(void) { - return &transmittance_tx; + return &transmittance_tx_; } private: -- 2.30.2 From e0d6c31af9ffd6f6b91084a93c90dd96f9a5f462 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Fri, 12 May 2023 15:18:31 +0200 Subject: [PATCH 13/21] Move shader create info to its own file --- .../shaders/infos/eevee_material_info.hh | 16 --------------- .../shaders/infos/eevee_subsurface_info.hh | 20 +++++++++++++++++++ source/blender/gpu/CMakeLists.txt | 1 + 3 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 7ae20d86745..f16113a5efc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -180,22 +180,6 @@ GPU_SHADER_CREATE_INFO(eevee_surf_shadow) .fragment_source("eevee_surf_shadow_frag.glsl") .additional_info("eevee_camera", "eevee_utility_texture", "eevee_sampling_data"); -GPU_SHADER_CREATE_INFO(eevee_transmittance_data) - .define("SSS_TRANSMITTANCE") - .sampler(SSS_TRANSMITTANCE_TEX_SLOT, ImageType::FLOAT_1D, "sss_transmittance_tx"); - -GPU_SHADER_CREATE_INFO(eevee_subsurface_eval) - .do_static_compilation(true) - .additional_info("eevee_shared") - .uniform_buf(1, "SubsurfaceData", "sss_buf") - .sampler(0, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") - .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") - .sampler(2, ImageType::FLOAT_2D, "radiance_tx") - .fragment_out(0, Type::VEC4, "out_combined") - .fragment_source("eevee_subsurface_eval_frag.glsl") - /* TODO(fclem) Output to diffuse pass without feedback loop. */ - .additional_info("draw_fullscreen", "draw_view", "eevee_hiz_data"); - #undef image_out #undef image_array_out diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh new file mode 100644 index 00000000000..6a69ff56dca --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "eevee_defines.hh" +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(eevee_transmittance_data) + .define("SSS_TRANSMITTANCE") + .sampler(SSS_TRANSMITTANCE_TEX_SLOT, ImageType::FLOAT_1D, "sss_transmittance_tx"); + +GPU_SHADER_CREATE_INFO(eevee_subsurface_eval) + .do_static_compilation(true) + .additional_info("eevee_shared") + .uniform_buf(1, "SubsurfaceData", "sss_buf") + .sampler(0, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") + .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") + .sampler(2, ImageType::FLOAT_2D, "radiance_tx") + .fragment_out(0, Type::VEC4, "out_combined") + .fragment_source("eevee_subsurface_eval_frag.glsl") + /* TODO(fclem) Output to diffuse pass without feedback loop. */ + .additional_info("draw_fullscreen", "draw_view", "eevee_hiz_data"); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 30f7c3b0cfe..ea16f485212 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -648,6 +648,7 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh -- 2.30.2 From 1d28e81235723b8d8beb35b261e835b81d3a4e28 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Fri, 12 May 2023 15:59:49 +0200 Subject: [PATCH 14/21] Move pass implementation to SubsurfaceModule --- .../draw/engines/eevee_next/eevee_pipeline.cc | 21 +------------------ .../draw/engines/eevee_next/eevee_pipeline.hh | 1 - .../engines/eevee_next/eevee_subsurface.cc | 21 +++++++++++++++++++ .../engines/eevee_next/eevee_subsurface.hh | 6 ++++++ 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index a20afba3082..2f03ec53aad 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -365,7 +365,6 @@ void DeferredLayer::begin_sync() void DeferredLayer::end_sync() { - if (closure_bits_ & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)) { const bool is_last_eval_pass = !(closure_bits_ & CLOSURE_SSS); @@ -393,24 +392,6 @@ void DeferredLayer::end_sync() eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); } - - if (closure_bits_ & CLOSURE_SSS) { - inst_.subsurface.end_sync(); - - subsurface_ps_.init(); - subsurface_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | - DRW_STATE_BLEND_ADD_FULL); - subsurface_ps_.state_stencil(0x00u, 0xFFu, CLOSURE_SSS); - subsurface_ps_.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_EVAL)); - inst_.subsurface.bind_resources(&subsurface_ps_); - inst_.hiz_buffer.bind_resources(&subsurface_ps_); - subsurface_ps_.bind_texture("radiance_tx", &diffuse_light_tx_); - subsurface_ps_.bind_texture("gbuffer_closure_tx", &inst_.gbuffer.closure_tx); - subsurface_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx); - - subsurface_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); - subsurface_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); - } } PassMain::Sub *DeferredLayer::prepass_add(::Material *blender_mat, @@ -464,7 +445,7 @@ void DeferredLayer::render(View &view, inst_.manager->submit(eval_light_ps_, view); if (closure_bits_ & CLOSURE_SSS) { - inst_.manager->submit(subsurface_ps_, view); + inst_.subsurface.render(view, combined_fb, diffuse_light_tx_); } diffuse_light_tx_.release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index c76be9da5f8..c09f5fe99cf 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -132,7 +132,6 @@ class DeferredLayer { PassMain::Sub *gbuffer_double_sided_ps_ = nullptr; PassSimple eval_light_ps_ = {"EvalLights"}; - PassSimple subsurface_ps_ = {"Subsurface"}; /* Closures bits from the materials in this pass. */ eClosureBits closure_bits_; diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc index bc63f458329..fb540d87225 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -38,6 +38,27 @@ void SubsurfaceModule::end_sync() precompute_samples_location(); data_.push_update(); + + subsurface_ps_.init(); + subsurface_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | + DRW_STATE_BLEND_ADD_FULL); + subsurface_ps_.state_stencil(0x00u, 0xFFu, CLOSURE_SSS); + subsurface_ps_.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_EVAL)); + inst_.subsurface.bind_resources(&subsurface_ps_); + inst_.hiz_buffer.bind_resources(&subsurface_ps_); + subsurface_ps_.bind_texture("radiance_tx", &diffuse_light_tx_); + subsurface_ps_.bind_texture("gbuffer_closure_tx", &inst_.gbuffer.closure_tx); + subsurface_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx); + + subsurface_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); + subsurface_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); +} + +void SubsurfaceModule::render(View &view, Framebuffer &fb, Texture &diffuse_light_tx) +{ + fb.bind(); + diffuse_light_tx_ = *&diffuse_light_tx; + inst_.manager->submit(subsurface_ps_, view); } void SubsurfaceModule::precompute_samples_location() diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh index abfb346032d..433aa60c616 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.hh +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.hh @@ -37,6 +37,10 @@ struct SubsurfaceModule { SubsurfaceDataBuf data_; /** Contains translucence profile for a single color channel. */ Texture transmittance_tx_; + /** Scene diffuse irradiance. Pointer binded at sync time, set at render time. */ + GPUTexture *diffuse_light_tx_; + /** Subsurface eval pass. Runs after the deferred pass. */ + PassSimple subsurface_ps_ = {"Subsurface"}; public: SubsurfaceModule(Instance &inst) : inst_(inst) @@ -49,6 +53,8 @@ struct SubsurfaceModule { void end_sync(); + void render(View &view, Framebuffer &fb, Texture &diffuse_light_tx); + template void bind_resources(draw::detail::PassBase *pass) { pass->bind_ubo("sss_buf", data_); -- 2.30.2 From 1f47c1b12223f513d4716e71e4981c433339154c Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Thu, 1 Jun 2023 17:54:30 +0200 Subject: [PATCH 15/21] Diffuse Light Pass --- source/blender/draw/engines/eevee_next/eevee_subsurface.cc | 2 ++ .../eevee_next/shaders/eevee_subsurface_eval_frag.glsl | 6 +++++- .../eevee_next/shaders/infos/eevee_subsurface_info.hh | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc index fb540d87225..d66510549bf 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -49,6 +49,8 @@ void SubsurfaceModule::end_sync() subsurface_ps_.bind_texture("radiance_tx", &diffuse_light_tx_); subsurface_ps_.bind_texture("gbuffer_closure_tx", &inst_.gbuffer.closure_tx); subsurface_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx); + subsurface_ps_.bind_ubo(RBUFS_BUF_SLOT, &inst_.render_buffers.data); + subsurface_ps_.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx); subsurface_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); subsurface_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl index c60aa8685d9..e716ac68cfb 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl @@ -74,7 +74,7 @@ void main(void) float pixel_footprint = sample_scale.x * textureSize(hiz_tx, 0).x; if (pixel_footprint <= 1.0) { /* Early out. */ - out_combined = vec4(0); + out_combined = vec4(0.0); return; } @@ -126,6 +126,10 @@ void main(void) /* Normalize the sum (slide 34). */ accum /= accum_weight; + if (rp_buf.diffuse_light_id >= 0) { + imageStore(rp_color_img, ivec3(texel, rp_buf.diffuse_light_id), vec4(accum, 1.0)); + } + /* This pass uses additive blending. * Subtract the surface diffuse radiance so it's not added twice. */ accum -= texelFetch(radiance_tx, texel, 0).rgb; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh index 6a69ff56dca..c9d36a85744 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_subsurface_info.hh @@ -9,11 +9,12 @@ GPU_SHADER_CREATE_INFO(eevee_transmittance_data) GPU_SHADER_CREATE_INFO(eevee_subsurface_eval) .do_static_compilation(true) - .additional_info("eevee_shared") + .additional_info("eevee_shared", "eevee_render_pass_out") .uniform_buf(1, "SubsurfaceData", "sss_buf") .sampler(0, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") .sampler(2, ImageType::FLOAT_2D, "radiance_tx") + .early_fragment_test(true) .fragment_out(0, Type::VEC4, "out_combined") .fragment_source("eevee_subsurface_eval_frag.glsl") /* TODO(fclem) Output to diffuse pass without feedback loop. */ -- 2.30.2 From 8d6a0f4e4df73271dd42f0724e277050723e2867 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 14 Jun 2023 17:40:38 +0200 Subject: [PATCH 16/21] Remap texture slots --- .../blender/draw/engines/eevee_next/eevee_defines.hh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index b5c8a720ca7..56486ed69a0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -90,14 +90,14 @@ /* Resource bindings. */ /* Texture. */ -#define SHADOW_TILEMAPS_TEX_SLOT 12 +#define SHADOW_TILEMAPS_TEX_SLOT 11 /* Only during surface shading. */ -#define SHADOW_ATLAS_TEX_SLOT 13 +#define SHADOW_ATLAS_TEX_SLOT 12 /* Only during shadow rendering. */ -#define SHADOW_RENDER_MAP_SLOT 13 -#define RBUFS_UTILITY_TEX_SLOT 14 -#define HIZ_TEX_SLOT 15 -#define SSS_TRANSMITTANCE_TEX_SLOT 16 +#define SHADOW_RENDER_MAP_SLOT 12 +#define RBUFS_UTILITY_TEX_SLOT 13 +#define HIZ_TEX_SLOT 14 +#define SSS_TRANSMITTANCE_TEX_SLOT 15 /* Images. */ #define RBUFS_COLOR_SLOT 0 -- 2.30.2 From d8ce569c21437e38df50f950c1e7d4be3603f040 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 14 Jun 2023 17:41:26 +0200 Subject: [PATCH 17/21] Bind rp_value_tx --- source/blender/draw/engines/eevee_next/eevee_subsurface.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc index d66510549bf..89a03dbdf40 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -51,6 +51,8 @@ void SubsurfaceModule::end_sync() subsurface_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx); subsurface_ps_.bind_ubo(RBUFS_BUF_SLOT, &inst_.render_buffers.data); subsurface_ps_.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx); + /** NOTE: Not used in the shader, but we bind it to avoid debug warnings. */ + subsurface_ps_.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx); subsurface_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH); subsurface_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); -- 2.30.2 From 07dce2224b726025d252c402231275abff1fa9cf Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 14 Jun 2023 17:42:05 +0200 Subject: [PATCH 18/21] Workaround for transmittance_profile values. --- source/blender/draw/engines/eevee_next/eevee_subsurface.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc index 89a03dbdf40..58e606c3b8e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_subsurface.cc +++ b/source/blender/draw/engines/eevee_next/eevee_subsurface.cc @@ -121,11 +121,18 @@ void SubsurfaceModule::precompute_transmittance_profile() /* Normalize over the disk. */ profile[i] /= area_accum; } + + /** NOTE: There's something very wrong here. + * This should be a small remap, + * but current profile range goes from 0.0399098 to 0.0026898. */ + /* Make a smooth gradient from 1 to 0. */ float range = profile.first() - profile.last(); float offset = profile.last(); for (float &value : profile) { value = (value - offset) / range; + /** HACK: Remap the curve to better fit Cycles values. */ + value = std::pow(value, 1.6f); } profile.first() = 1; profile.last() = 0; -- 2.30.2 From 11c6138bc425497221981fde602f05a0b866638f Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 14 Jun 2023 17:50:16 +0200 Subject: [PATCH 19/21] Change default thickness --- source/blender/draw/engines/eevee_next/eevee_shader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 105250b9fa2..d9f9e9b72db 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -400,7 +400,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu frag_gen << "float nodetree_thickness()\n"; frag_gen << "{\n"; /* TODO(fclem): Better default. */ - frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.1;\n"); + frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.0;\n"); frag_gen << "}\n\n"; info.fragment_source_generated = frag_gen.str(); -- 2.30.2 From d34f22d0f4210d2647680644332f08ed87d54b92 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 14 Jun 2023 18:54:38 +0200 Subject: [PATCH 20/21] Add texture slot workaround --- source/blender/draw/engines/eevee_next/eevee_shader.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index d9f9e9b72db..518c96eed2a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -242,6 +242,8 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu case SHADOW_TILEMAPS_TEX_SLOT: resource.slot = GPU_max_textures_frag() - 3; break; + case SSS_TRANSMITTANCE_TEX_SLOT: + resource.slot = GPU_max_textures_frag() - 4; } } } -- 2.30.2 From fbe7c3c770ef689789dd349c8c2c6095f0343f70 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Thu, 15 Jun 2023 15:44:36 +0200 Subject: [PATCH 21/21] Revert "Change default thickness" This reverts commit 11c6138bc425497221981fde602f05a0b866638f. --- source/blender/draw/engines/eevee_next/eevee_shader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 6c5cc3f65fa..622b330cb03 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -391,7 +391,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu frag_gen << "float nodetree_thickness()\n"; frag_gen << "{\n"; /* TODO(fclem): Better default. */ - frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.0;\n"); + frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.1;\n"); frag_gen << "}\n\n"; info.fragment_source_generated = frag_gen.str(); -- 2.30.2