Blender V4.3
subsurface_disk.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6
8
9#ifdef __SUBSURFACE__
10
11/* BSSRDF using disk based importance sampling.
12 *
13 * BSSRDF Importance Sampling, SIGGRAPH 2013
14 * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf
15 */
16
17ccl_device_inline Spectrum subsurface_disk_eval(const Spectrum radius, float disk_r, float r)
18{
19 const Spectrum eval = bssrdf_eval(radius, r);
20 const float pdf = bssrdf_pdf(radius, disk_r);
21 return (pdf > 0.0f) ? eval / pdf : zero_spectrum();
22}
23
24/* Subsurface scattering step, from a point on the surface to other
25 * nearby points on the same object. */
26ccl_device_inline bool subsurface_disk(KernelGlobals kg,
28 RNGState rng_state,
29 ccl_private Ray &ray,
31
32{
33 float2 rand_disk = path_state_rng_2D(kg, &rng_state, PRNG_SUBSURFACE_DISK);
34
35 /* Read shading point info from integrator state. */
36 const float3 P = INTEGRATOR_STATE(state, ray, P);
37 const float ray_dP = INTEGRATOR_STATE(state, ray, dP);
38 const float time = INTEGRATOR_STATE(state, ray, time);
39 const float3 Ng = INTEGRATOR_STATE(state, subsurface, N);
40 const int object = INTEGRATOR_STATE(state, isect, object);
41 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
42
43 /* Read subsurface scattering parameters. */
44 const Spectrum radius = INTEGRATOR_STATE(state, subsurface, radius);
45
46 /* Pick random axis in local frame and point on disk. */
47 float3 disk_N, disk_T, disk_B;
48 float pick_pdf_N, pick_pdf_T, pick_pdf_B;
49
50 disk_N = Ng;
51 make_orthonormals(disk_N, &disk_T, &disk_B);
52
53 if (rand_disk.y < 0.5f) {
54 pick_pdf_N = 0.5f;
55 pick_pdf_T = 0.25f;
56 pick_pdf_B = 0.25f;
57 rand_disk.y *= 2.0f;
58 }
59 else if (rand_disk.y < 0.75f) {
60 float3 tmp = disk_N;
61 disk_N = disk_T;
62 disk_T = tmp;
63 pick_pdf_N = 0.25f;
64 pick_pdf_T = 0.5f;
65 pick_pdf_B = 0.25f;
66 rand_disk.y = (rand_disk.y - 0.5f) * 4.0f;
67 }
68 else {
69 float3 tmp = disk_N;
70 disk_N = disk_B;
71 disk_B = tmp;
72 pick_pdf_N = 0.25f;
73 pick_pdf_T = 0.25f;
74 pick_pdf_B = 0.5f;
75 rand_disk.y = (rand_disk.y - 0.75f) * 4.0f;
76 }
77
78 /* Sample point on disk. */
79 float phi = M_2PI_F * rand_disk.y;
80 float disk_height, disk_r;
81
82 bssrdf_sample(radius, rand_disk.x, &disk_r, &disk_height);
83
84 float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B;
85
86 /* Create ray. */
87 ray.P = P + disk_N * disk_height + disk_P;
88 ray.D = -disk_N;
89 ray.tmin = 0.0f;
90 ray.tmax = 2.0f * disk_height;
91 ray.dP = ray_dP;
93 ray.time = time;
94 ray.self.object = OBJECT_NONE;
95 ray.self.prim = PRIM_NONE;
96 ray.self.light_object = OBJECT_NONE;
97 ray.self.light_prim = PRIM_NONE;
98 ray.self.light = LAMP_NONE;
99
100 /* Intersect with the same object. if multiple intersections are found it
101 * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */
102 uint lcg_state = lcg_state_init(
103 rng_state.rng_pixel, rng_state.rng_offset, rng_state.sample, 0x68bc21eb);
104 const int max_hits = BSSRDF_MAX_HITS;
105
106 scene_intersect_local(kg, &ray, &ss_isect, object, &lcg_state, max_hits);
107 const int num_eval_hits = min(ss_isect.num_hits, max_hits);
108 if (num_eval_hits == 0) {
109 return false;
110 }
111
112 /* Sort for consistent renders between CPU and GPU, independent of the BVH
113 * traversal algorithm. */
114 sort_intersections_and_normals(ss_isect.hits, ss_isect.Ng, num_eval_hits);
115
116 Spectrum weights[BSSRDF_MAX_HITS]; /* TODO: zero? */
117 float sum_weights = 0.0f;
118
119 for (int hit = 0; hit < num_eval_hits; hit++) {
120 /* Get geometric normal. */
121 const int object = ss_isect.hits[hit].object;
122 const int object_flag = kernel_data_fetch(object_flag, object);
123 float3 hit_Ng = ss_isect.Ng[hit];
124 if (path_flag & PATH_RAY_SUBSURFACE_BACKFACING) {
125 hit_Ng = -hit_Ng;
126 }
127 if (object_negative_scale_applied(object_flag)) {
128 hit_Ng = -hit_Ng;
129 }
130
131 if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
132 /* Transform normal to world space. */
133 Transform itfm;
134 object_fetch_transform_motion_test(kg, object, time, &itfm);
135 hit_Ng = normalize(transform_direction_transposed(&itfm, hit_Ng));
136 }
137
138 /* Quickly retrieve P and Ng without setting up ShaderData. */
139 const float3 hit_P = ray.P + ray.D * ss_isect.hits[hit].t;
140
141 /* Probability densities for local frame axes. */
142 const float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng));
143 const float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng));
144 const float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng));
145
146 /* Multiple importance sample between 3 axes, power heuristic
147 * found to be slightly better than balance heuristic. pdf_N
148 * in the MIS weight and denominator cancelled out. */
149 float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B));
150 if (ss_isect.num_hits > max_hits) {
151 w *= ss_isect.num_hits / (float)max_hits;
152 }
153
154 /* Real distance to sampled point. */
155 const float r = len(hit_P - P);
156
157 /* Evaluate profiles. */
158 const Spectrum weight = subsurface_disk_eval(radius, disk_r, r) * w;
159
160 /* Store result. */
161 ss_isect.Ng[hit] = hit_Ng;
162 weights[hit] = weight;
163 sum_weights += average(fabs(weight));
164 }
165
166 if (sum_weights == 0.0f) {
167 return false;
168 }
169
170 /* Use importance resampling, sampling one of the hits proportional to weight. */
171 const float rand_resample = path_state_rng_1D(kg, &rng_state, PRNG_SUBSURFACE_DISK_RESAMPLE);
172 const float r = rand_resample * sum_weights;
173 float partial_sum = 0.0f;
174
175 for (int hit = 0; hit < num_eval_hits; hit++) {
176 const Spectrum weight = weights[hit];
177 const float sample_weight = average(fabs(weight));
178 float next_sum = partial_sum + sample_weight;
179
180 if (r < next_sum) {
181 /* Return exit point. */
182 const Spectrum resampled_weight = weight * sum_weights / sample_weight;
183 INTEGRATOR_STATE_WRITE(state, path, throughput) *= resampled_weight;
184 ss_isect.hits[0] = ss_isect.hits[hit];
185 ss_isect.Ng[0] = ss_isect.Ng[hit];
186
187 ray.P = ray.P + ray.D * ss_isect.hits[hit].t;
188 ray.D = ss_isect.Ng[hit];
189 ray.tmin = 0.0f;
190 ray.tmax = 1.0f;
191
193 kg, state, 1.0f, Ng, -Ng, resampled_weight, INTEGRATOR_STATE(state, subsurface, albedo));
194 return true;
195 }
196
197 partial_sum = next_sum;
198 }
199
200 return false;
201}
202
203#endif /* __SUBSURFACE__ */
204
unsigned int uint
ccl_device_forceinline float bssrdf_pdf(const Spectrum radius, float r)
Definition bssrdf.h:257
ccl_device void bssrdf_sample(const Spectrum radius, float xi, ccl_private float *r, ccl_private float *h)
Definition bssrdf.h:217
ccl_device_forceinline Spectrum bssrdf_eval(const Spectrum radius, float r)
Definition bssrdf.h:248
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
additional_info("compositor_sum_squared_difference_float_shared") .push_constant(Type output_img float dot(value.rgb, luminance_coefficients)") .define("LOAD(value)"
ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection *hits, ccl_private float3 *Ng, uint num_hits)
double time
const KernelGlobalsCPU *ccl_restrict KernelGlobals
#define kernel_data_fetch(name, index)
#define sinf(x)
#define cosf(x)
#define ccl_private
#define ccl_device_inline
#define CCL_NAMESPACE_END
#define fabsf(x)
ccl_device_forceinline float differential_zero_compact()
int len
draw_view in_light_buf[] float
ccl_device_inline bool object_negative_scale_applied(const int object_flag)
ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals kg, int object, float time, ccl_private Transform *itfm)
ccl_device_forceinline void guiding_record_bssrdf_bounce(KernelGlobals kg, IntegratorState state, const float pdf, const float3 N, const float3 wo, const Spectrum weight, const Spectrum albedo)
@ PRNG_SUBSURFACE_DISK_RESAMPLE
@ PRNG_SUBSURFACE_DISK
#define PRIM_NONE
@ PATH_RAY_SUBSURFACE_BACKFACING
#define OBJECT_NONE
@ SD_OBJECT_TRANSFORM_APPLIED
#define LAMP_NONE
#define BSSRDF_MAX_HITS
ccl_device_inline uint lcg_state_init(const uint rng_hash, const uint rng_offset, const uint sample, const uint scramble)
Definition lcg.h:36
ccl_device_inline float average(const float2 a)
ccl_device_inline float2 fabs(const float2 a)
static ulong state[N]
#define N
ccl_device_inline float path_state_rng_1D(KernelGlobals kg, ccl_private const RNGState *rng_state, const int dimension)
Definition path_state.h:339
ccl_device_inline float2 path_state_rng_2D(KernelGlobals kg, ccl_private const RNGState *rng_state, const int dimension)
Definition path_state.h:347
#define M_2PI_F
Definition sky_float3.h:23
#define min(a, b)
Definition sort.c:32
IntegratorStateCPU *ccl_restrict IntegratorState
Definition state.h:228
#define INTEGRATOR_STATE_WRITE(state, nested_struct, member)
Definition state.h:236
#define INTEGRATOR_STATE(state, nested_struct, member)
Definition state.h:235
unsigned int uint32_t
Definition stdint.h:80
uint rng_pixel
Definition path_state.h:310
uint rng_offset
Definition path_state.h:311
float x
float y
ccl_device_inline float3 transform_direction_transposed(ccl_private const Transform *t, const float3 a)
Definition transform.h:123
#define zero_spectrum
SPECTRUM_DATA_TYPE Spectrum
ccl_device_inline float sqr(float a)
Definition util/math.h:782
ccl_device_inline void make_orthonormals(const float3 N, ccl_private float3 *a, ccl_private float3 *b)
Definition util/math.h:593
uint8_t flag
Definition wm_window.cc:138