Blender V5.0
eevee_subsurface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "eevee_instance.hh"
12#include "eevee_subsurface.hh"
13
14namespace blender::eevee {
15
16/* -------------------------------------------------------------------- */
19
21{
22 data_.sample_len = 16;
23
24 if (!(inst_.pipelines.deferred.closure_bits_get() & CLOSURE_SSS)) {
25 return;
26 }
27
28 {
29 PassSimple &pass = setup_ps_;
30 pass.init();
32 pass.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_SETUP));
33 pass.bind_resources(inst_.gbuffer);
34 pass.bind_texture("depth_tx", &inst_.render_buffers.depth_tx);
35 pass.bind_image("direct_light_img", &direct_light_tx_);
36 pass.bind_image("indirect_light_img", &indirect_light_tx_);
37 pass.bind_image("object_id_img", &object_id_tx_);
38 pass.bind_image("radiance_img", &radiance_tx_);
39 pass.bind_ssbo("convolve_tile_buf", &convolve_tile_buf_);
40 pass.bind_ssbo("convolve_dispatch_buf", &convolve_dispatch_buf_);
42 pass.dispatch(&setup_dispatch_size_);
43 }
44 {
45 /* Clamping to border color allows to always load ID 0 for out of view samples and discard
46 * their influence. Also disable filtering to avoid light bleeding between different objects
47 * and loading invalid interpolated IDs. */
53
54 PassSimple &pass = convolve_ps_;
55 pass.init();
57 pass.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_CONVOLVE));
58 pass.bind_resources(inst_.uniform_data);
59 pass.bind_resources(inst_.gbuffer);
60 pass.bind_texture("radiance_tx", &radiance_tx_, sampler);
61 pass.bind_texture("depth_tx", &inst_.render_buffers.depth_tx, sampler);
62 pass.bind_texture("object_id_tx", &object_id_tx_, sampler);
63 pass.bind_image("out_direct_light_img", &direct_light_tx_);
64 pass.bind_image("out_indirect_light_img", &indirect_light_tx_);
65 pass.bind_ssbo("tiles_coord_buf", &convolve_tile_buf_);
67 pass.dispatch(convolve_dispatch_buf_);
68 }
69}
70
71void SubsurfaceModule::render(gpu::Texture *direct_diffuse_light_tx,
72 gpu::Texture *indirect_diffuse_light_tx,
73 eClosureBits active_closures,
74 View &view)
75{
76 if (!(active_closures & CLOSURE_SSS)) {
77 return;
78 }
79
80 precompute_samples_location();
81
82 int2 render_extent = inst_.film.render_extent_get();
83 setup_dispatch_size_ = int3(math::divide_ceil(render_extent, int2(SUBSURFACE_GROUP_SIZE)), 1);
84
85 const int convolve_tile_count = setup_dispatch_size_.x * setup_dispatch_size_.y;
86 convolve_tile_buf_.resize(ceil_to_multiple_u(convolve_tile_count, 512));
87
88 direct_light_tx_ = direct_diffuse_light_tx;
89 indirect_light_tx_ = indirect_diffuse_light_tx;
90
92 object_id_tx_.acquire(render_extent, gpu::TextureFormat::SUBSURFACE_OBJECT_ID_FORMAT, usage);
93 radiance_tx_.acquire(render_extent, gpu::TextureFormat::SUBSURFACE_RADIANCE_FORMAT, usage);
94
95 convolve_dispatch_buf_.clear_to_zero();
96
97 inst_.manager->submit(setup_ps_, view);
98 inst_.manager->submit(convolve_ps_, view);
99
100 object_id_tx_.release();
101 radiance_tx_.release();
102}
103
104void SubsurfaceModule::precompute_samples_location()
105{
106 /* Precompute sample position with white albedo. */
107 float d = burley_setup(float3(1.0f), float3(1.0f)).x;
108
109 float rand_u = inst_.sampling.rng_get(SAMPLING_SSS_U);
110 float rand_v = inst_.sampling.rng_get(SAMPLING_SSS_V);
111
112 /* Find minimum radius that we can represent because we are only sampling the largest radius. */
113 data_.min_radius = 1.0f;
114
115 double golden_angle = M_PI * (3.0 - sqrt(5.0));
116 for (auto i : IndexRange(data_.sample_len)) {
117 float theta = golden_angle * i + M_PI * 2.0f * rand_u;
118 float x = (rand_v + i) / data_.sample_len;
119 float r = SubsurfaceModule::burley_sample(d, x);
120 data_.min_radius = min_ff(data_.min_radius, r);
121 data_.samples[i].x = cosf(theta) * r;
122 data_.samples[i].y = sinf(theta) * r;
123 data_.samples[i].z = 1.0f / burley_pdf(d, r);
124 }
125 /* Avoid float imprecision. */
126 data_.min_radius = max_ff(data_.min_radius, 1e-4f);
127
129}
130
132
133/* -------------------------------------------------------------------- */
140
141float SubsurfaceModule::burley_sample(float d, float x_rand)
142{
143 x_rand *= SSS_BURLEY_TRUNCATE_CDF;
144
145 const float tolerance = 1e-6;
146 const int max_iteration_count = 10;
147 /* Do initial guess based on manual curve fitting, this allows us to reduce
148 * number of iterations to maximum 4 across the [0..1] range. We keep maximum
149 * number of iteration higher just to be sure we didn't miss root in some
150 * corner case.
151 */
152 float r;
153 if (x_rand <= 0.9) {
154 r = exp(x_rand * x_rand * 2.4) - 1.0;
155 }
156 else {
157 /* TODO(sergey): Some nicer curve fit is possible here. */
158 r = 15.0;
159 }
160 /* Solve against scaled radius. */
161 for (int i = 0; i < max_iteration_count; i++) {
162 float exp_r_3 = exp(-r / 3.0);
163 float exp_r = exp_r_3 * exp_r_3 * exp_r_3;
164 float f = 1.0 - 0.25 * exp_r - 0.75 * exp_r_3 - x_rand;
165 float f_ = 0.25 * exp_r + 0.25 * exp_r_3;
166
167 if (abs(f) < tolerance || f_ == 0.0) {
168 break;
169 }
170
171 r = r - f / f_;
172 r = std::max<double>(r, 0.0);
173 }
174
175 return r * d;
176}
177
178float SubsurfaceModule::burley_eval(float d, float r)
179{
180 if (r >= SSS_BURLEY_TRUNCATE * d) {
181 return 0.0;
182 }
183 /* Slide 33. */
184 float exp_r_3_d = expf(-r / (3.0f * d));
185 float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
186 return (exp_r_d + exp_r_3_d) / (8.0f * float(M_PI) * d);
187}
188
189float SubsurfaceModule::burley_pdf(float d, float r)
190{
191 return burley_eval(d, r) / SSS_BURLEY_TRUNCATE_CDF;
192}
193
195
196} // namespace blender::eevee
MINLINE uint ceil_to_multiple_u(uint a, uint b)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
#define M_PI
static AppView * view
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_TEXTURE_FETCH
Definition GPU_state.hh:37
@ GPU_BARRIER_SHADER_IMAGE_ACCESS
Definition GPU_state.hh:35
@ GPU_SAMPLER_CUSTOM_COMPARE
@ GPU_SAMPLER_STATE_TYPE_PARAMETERS
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_SHADER_WRITE
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
@ GPU_SAMPLER_FILTERING_DEFAULT
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
void bind_resources(U &resources)
Definition draw_pass.hh:449
void shader_set(gpu::Shader *shader)
void bind_texture(const char *name, gpu::Texture *texture, GPUSamplerState state=sampler_auto)
void bind_image(const char *name, gpu::Texture *image)
void dispatch(int group_len)
void state_set(DRWState state, int clip_plane_count=0)
void barrier(GPUBarrier type)
void bind_ssbo(const char *name, gpu::StorageBuf *buffer)
UniformDataModule uniform_data
float rng_get(eSamplingDimension dimension) const
nullptr float
#define expf(x)
@ DRW_STATE_NO_DRAW
Definition draw_state.hh:27
#define SUBSURFACE_GROUP_SIZE
#define SSS_BURLEY_TRUNCATE
#define SSS_BURLEY_TRUNCATE_CDF
#define exp
#define abs
#define sqrt
detail::Pass< command::DrawCommandBuf > PassSimple
static float3 burley_setup(float3 radius, float3 albedo)
VecBase< T, Size > divide_ceil(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< int32_t, 2 > int2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
#define sinf
#define cosf
void render(gpu::Texture *direct_diffuse_light_tx, gpu::Texture *indirect_diffuse_light_tx, eClosureBits active_closures, View &view)
i
Definition text_draw.cc:230