Blender V4.3
subsurface.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
8
9#include "kernel/bvh/bvh.h"
10
15
21
23
24#ifdef __SUBSURFACE__
25
26ccl_device_inline bool subsurface_entry_bounce(KernelGlobals kg,
29 ccl_private RNGState *rng_state,
31{
32 float2 rand_bsdf = path_state_rng_2D(kg, rng_state, PRNG_SUBSURFACE_BSDF);
33
35 /* CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID has a 50% chance to sample a diffuse entry bounce.
36 * Also, for the refractive entry, it uses a fixed roughness of 1.0. */
37 if (rand_bsdf.x < 0.5f) {
38 rand_bsdf.x *= 2.0f;
39 float pdf;
40 sample_cos_hemisphere(-bssrdf->N, rand_bsdf, wo, &pdf);
41 return true;
42 }
43 rand_bsdf.x = 2.0f * (rand_bsdf.x - 0.5f);
44 }
45
46 const float cos_NI = dot(bssrdf->N, sd->wi);
47 if (cos_NI <= 0.0f) {
48 return false;
49 }
50
51 float3 X, Y, Z = bssrdf->N;
52 make_orthonormals(Z, &X, &Y);
53
54 const float alpha = bssrdf->alpha;
55 const float neta = 1.0f / bssrdf->ior;
56
57 /* Sample microfacet normal by transforming to/from local coordinates. */
58 const float3 local_I = make_float3(dot(X, sd->wi), dot(Y, sd->wi), cos_NI);
59 const float3 local_H = microfacet_ggx_sample_vndf(local_I, alpha, alpha, rand_bsdf);
60 const float3 H = X * local_H.x + Y * local_H.y + Z * local_H.z;
61
62 const float cos_HI = dot(H, sd->wi);
63 const float arg = 1.0f - (sqr(neta) * (1.0f - sqr(cos_HI)));
64 /* We clamp subsurface IOR to be above 1, so there should never be TIR. */
65 kernel_assert(arg >= 0.0f);
66
67 const float dnp = max(sqrtf(arg), 1e-7f);
68 const float nK = (neta * cos_HI) - dnp;
69 *wo = -(neta * sd->wi) + (nK * H);
70 return true;
71 /* NOTE: For a proper refractive GGX interface, we should be computing lambdaI and lambdaO
72 * and multiplying the throughput by BSDF/pdf, which for VNDF sampling works out to
73 * `(1 + lambdaI) / (1 + lambdaI + lambdaO)`.
74 * However, this causes darkening due to the single-scattering approximation, which we'd
75 * then have to correct with a lookup table.
76 * Since we only really care about the directional distribution here, it's much easier to
77 * just skip all that instead. */
78}
79
80ccl_device int subsurface_bounce(KernelGlobals kg,
83 ccl_private const ShaderClosure *sc)
84{
85 /* We should never have two consecutive BSSRDF bounces, the second one should
86 * be converted to a diffuse BSDF to avoid this. */
88
89 /* Setup path state for intersect_subsurface kernel. */
90 ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc;
91
92 /* Setup ray into surface. */
93 INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
94 INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
98
99 /* Advance random number offset for bounce. */
100 INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM;
101
102 /* Compute weight, optionally including Fresnel from entry point. */
104 INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight;
105
106 uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA);
107 if (sc->type == CLOSURE_BSSRDF_BURLEY_ID) {
108 path_flag |= PATH_RAY_SUBSURFACE_DISK;
109 INTEGRATOR_STATE_WRITE(state, subsurface, N) = sd->Ng;
110 }
111 else {
113
114 /* Sample entry bounce into the material. */
115 RNGState rng_state;
116 path_state_rng_load(state, &rng_state);
117 float3 wo;
118 if (!subsurface_entry_bounce(kg, bssrdf, sd, &rng_state, &wo) || dot(sd->Ng, wo) >= 0.0f) {
119 /* Sampling failed, give up on this bounce. */
120 return LABEL_NONE;
121 }
122 INTEGRATOR_STATE_WRITE(state, ray, D) = wo;
123 INTEGRATOR_STATE_WRITE(state, subsurface, N) = sd->N;
124 }
125
126 if (sd->flag & SD_BACKFACING) {
128 }
129
130 INTEGRATOR_STATE_WRITE(state, path, flag) = path_flag;
131
132 if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
133 if (INTEGRATOR_STATE(state, path, bounce) == 0) {
134 INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_spectrum();
135 INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_spectrum();
136 }
137 }
138
139 /* Pass BSSRDF parameters. */
140 INTEGRATOR_STATE_WRITE(state, subsurface, albedo) = bssrdf->albedo;
141 INTEGRATOR_STATE_WRITE(state, subsurface, radius) = bssrdf->radius;
142 INTEGRATOR_STATE_WRITE(state, subsurface, anisotropy) = bssrdf->anisotropy;
143
144 /* Path guiding. */
145 guiding_record_bssrdf_weight(kg, state, weight, bssrdf->albedo);
146
148}
149
150ccl_device void subsurface_shader_data_setup(KernelGlobals kg,
153 const uint32_t path_flag)
154{
155 /* Get bump mapped normal from shader evaluation at exit point. */
156 float3 N = sd->N;
157 if (sd->flag & SD_HAS_BSSRDF_BUMP) {
158 N = surface_shader_bssrdf_normal(sd);
159 }
160
161 /* Setup diffuse BSDF at the exit point. This replaces shader_eval_surface. */
162 sd->flag &= ~SD_CLOSURE_FLAGS;
163 sd->num_closure = 0;
164 sd->num_closure_left = kernel_data.max_closures;
165
166 const Spectrum weight = one_spectrum();
167
169 sd, sizeof(DiffuseBsdf), weight);
170
171 if (bsdf) {
172 bsdf->N = N;
173 sd->flag |= bsdf_diffuse_setup(bsdf);
174 }
175}
176
177ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState state)
178{
179 RNGState rng_state;
180 path_state_rng_load(state, &rng_state);
181
184
186 if (!subsurface_random_walk(kg, state, rng_state, ray, ss_isect)) {
187 return false;
188 }
189 }
190 else {
191 if (!subsurface_disk(kg, state, rng_state, ray, ss_isect)) {
192 return false;
193 }
194 }
195
196# ifdef __VOLUME__
197 /* Update volume stack if needed. */
198 if (kernel_data.integrator.use_volumes) {
199 const int object = ss_isect.hits[0].object;
200 const int object_flag = kernel_data_fetch(object_flag, object);
201
202 if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) {
204
206 }
207 }
208# endif /* __VOLUME__ */
209
210 /* Pretend ray is coming from the outside towards the exit point. This ensures
211 * correct front/back facing normals.
212 * TODO: find a more elegant solution? */
213 ray.P += ray.D * ray.tmax * 2.0f;
214 ray.D = -ray.D;
215
216 integrator_state_write_isect(state, &ss_isect.hits[0]);
218
219 /* Advance random number offset for bounce. */
220 INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM;
221
222 const int shader = intersection_get_shader(kg, &ss_isect.hits[0]);
223 const int shader_flags = kernel_data_fetch(shaders, shader).flags;
224 const int object_flags = intersection_get_object_flags(kg, &ss_isect.hits[0]);
225 const bool use_caustics = kernel_data.integrator.use_caustics &&
226 (object_flags & SD_OBJECT_CAUSTICS);
227 const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE);
228
229 if (use_caustics) {
231 state,
234 shader);
235 }
236 else if (use_raytrace_kernel) {
238 state,
241 shader);
242 }
243 else {
245 state,
248 shader);
249 }
250
251 return true;
252}
253
254#endif /* __SUBSURFACE__ */
255
#define X
#define Y
ccl_device_inline ccl_private ShaderClosure * bsdf_alloc(ccl_private ShaderData *sd, int size, Spectrum weight)
Definition alloc.h:52
ccl_device int bsdf_diffuse_setup(ccl_private DiffuseBsdf *bsdf)
ccl_device_forceinline float3 microfacet_ggx_sample_vndf(const float3 wi, const float alpha_x, const float alpha_y, const float2 rand)
additional_info("compositor_sum_squared_difference_float_shared") .push_constant(Type output_img float dot(value.rgb, luminance_coefficients)") .define("LOAD(value)"
ccl_device_forceinline int intersection_get_object_flags(KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect)
ccl_device_forceinline int intersection_get_shader(KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect)
#define kernel_assert(cond)
#define kernel_data
const KernelGlobalsCPU *ccl_restrict KernelGlobals
#define kernel_data_fetch(name, index)
#define ccl_optional_struct_init
#define ccl_device
#define ccl_private
#define ccl_device_inline
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define sqrtf(x)
ccl_device_forceinline float differential_make_compact(const float dD)
ccl_device_forceinline float differential_zero_compact()
CCL_NAMESPACE_BEGIN ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, IntegratorState state, const float3 from_P, const float3 to_P)
ccl_device_forceinline void guiding_record_bssrdf_weight(KernelGlobals kg, IntegratorState state, const Spectrum weight, const Spectrum albedo)
@ CLOSURE_BSSRDF_BURLEY_ID
@ CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID
@ SD_BACKFACING
@ SD_HAS_BSSRDF_BUMP
@ SD_HAS_RAYTRACE
@ PRNG_BOUNCE_NUM
@ PRNG_SUBSURFACE_BSDF
@ PATH_RAY_SUBSURFACE_BACKFACING
@ PATH_RAY_SUBSURFACE_DISK
@ PATH_RAY_DIFFUSE_ANCESTOR
@ PATH_RAY_SUBSURFACE_RANDOM_WALK
ShaderData
@ SD_OBJECT_CAUSTICS
@ SD_OBJECT_INTERSECTS_VOLUME
#define KERNEL_FEATURE_LIGHT_PASSES
@ LABEL_NONE
@ LABEL_SUBSURFACE_SCATTER
ShaderClosure
@ DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE
@ DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE
@ DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE
@ DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE
static ulong state[N]
#define N
#define H(x, y, z)
ccl_device_inline float2 path_state_rng_2D(KernelGlobals kg, ccl_private const RNGState *rng_state, const int dimension)
Definition path_state.h:347
ccl_device_inline void path_state_rng_load(ConstIntegratorState state, ccl_private RNGState *rng_state)
Definition path_state.h:315
ccl_device_inline void sample_cos_hemisphere(const float3 N, float2 rand_in, ccl_private float3 *wo, ccl_private float *pdf)
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
ccl_device_forceinline void integrator_path_next_sorted(KernelGlobals kg, IntegratorState state, const DeviceKernel current_kernel, const DeviceKernel next_kernel, const uint32_t key)
Definition state_flow.h:186
ccl_device_forceinline void integrator_state_write_isect(IntegratorState state, ccl_private const Intersection *ccl_restrict isect)
Definition state_util.h:128
CCL_NAMESPACE_BEGIN ccl_device_forceinline void integrator_state_write_ray(IntegratorState state, ccl_private const Ray *ccl_restrict ray)
Definition state_util.h:15
#define FLT_MAX
Definition stdcycles.h:14
closure color bssrdf(string method, normal N, vector radius, color albedo) BUILTIN
unsigned int uint32_t
Definition stdint.h:80
Definition bssrdf.h:9
struct Intersection hits[LOCAL_MAX_HITS]
float x
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27
ccl_device_inline Spectrum surface_shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd, ccl_private const ShaderClosure *ccl_restrict bssrdf_sc)
float max
#define one_spectrum
#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