Code refactor: better distinguish scatter and absorption for volume integration.
This commit is contained in:
@@ -39,7 +39,7 @@ ccl_device int volume_henyey_greenstein_setup(ShaderClosure *sc)
|
||||
/* clamp anisotropy to avoid delta function */
|
||||
sc->data0 = signf(sc->data0) * min(fabsf(sc->data0), 1.0f - 1e-3f);
|
||||
|
||||
return SD_BSDF|SD_BSDF_HAS_EVAL|SD_VOLUME;
|
||||
return SD_BSDF|SD_BSDF_HAS_EVAL|SD_SCATTER;
|
||||
}
|
||||
|
||||
ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, const float3 I, float3 omega_in, float *pdf)
|
||||
@@ -98,7 +98,7 @@ ccl_device int volume_absorption_setup(ShaderClosure *sc)
|
||||
{
|
||||
sc->type = CLOSURE_VOLUME_ABSORPTION_ID;
|
||||
|
||||
return SD_VOLUME;
|
||||
return SD_ABSORPTION;
|
||||
}
|
||||
|
||||
/* VOLUME CLOSURE */
|
||||
|
@@ -92,11 +92,10 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ra
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* volume attenuation */
|
||||
/* volume attenuation, emission, scatter */
|
||||
if(state.volume_stack[0].shader != SHADER_NO_ID) {
|
||||
Ray segment_ray = ray;
|
||||
segment_ray.t = (hit)? isect.t: FLT_MAX;
|
||||
kernel_volume_integrate(kg, &state, &segment_ray, L, &throughput);
|
||||
ray.t = (hit)? isect.t: FLT_MAX;
|
||||
kernel_volume_integrate(kg, &state, &ray, L, &throughput);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -514,11 +513,10 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* volume attenuation */
|
||||
/* volume attenuation, emission, scatter */
|
||||
if(state.volume_stack[0].shader != SHADER_NO_ID) {
|
||||
Ray segment_ray = ray;
|
||||
segment_ray.t = (hit)? isect.t: FLT_MAX;
|
||||
kernel_volume_integrate(kg, &state, &segment_ray, &L, &throughput);
|
||||
ray.t = (hit)? isect.t: FLT_MAX;
|
||||
kernel_volume_integrate(kg, &state, &ray, &L, &throughput);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1018,11 +1016,10 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* volume attenuation */
|
||||
/* volume attenuation, emission, scatter */
|
||||
if(state.volume_stack[0].shader != SHADER_NO_ID) {
|
||||
Ray segment_ray = ray;
|
||||
segment_ray.t = (hit)? isect.t: FLT_MAX;
|
||||
kernel_volume_integrate(kg, &state, &segment_ray, &L, &throughput);
|
||||
ray.t = (hit)? isect.t: FLT_MAX;
|
||||
kernel_volume_integrate(kg, &state, &ray, &L, &throughput);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -500,25 +500,26 @@ enum ShaderDataFlag {
|
||||
SD_BSDF_GLOSSY = 16, /* have glossy bsdf */
|
||||
SD_BSSRDF = 32, /* have bssrdf */
|
||||
SD_HOLDOUT = 64, /* have holdout closure? */
|
||||
SD_VOLUME = 128, /* have volume closure? */
|
||||
SD_AO = 256, /* have ao closure? */
|
||||
SD_ABSORPTION = 128, /* have volume absorption closure? */
|
||||
SD_SCATTER = 256, /* have volume scattering closure? */
|
||||
SD_AO = 512, /* have ao closure? */
|
||||
|
||||
SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY|SD_BSSRDF|SD_HOLDOUT|SD_VOLUME|SD_AO),
|
||||
SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY|SD_BSSRDF|SD_HOLDOUT|SD_ABSORPTION|SD_SCATTER|SD_AO),
|
||||
|
||||
/* shader flags */
|
||||
SD_USE_MIS = 512, /* direct light sample */
|
||||
SD_HAS_TRANSPARENT_SHADOW = 1024, /* has transparent shadow */
|
||||
SD_HAS_VOLUME = 2048, /* has volume shader */
|
||||
SD_HAS_ONLY_VOLUME = 4096, /* has only volume shader, no surface */
|
||||
SD_HOMOGENEOUS_VOLUME = 8192, /* has homogeneous volume */
|
||||
SD_HAS_BSSRDF_BUMP = 16384, /* bssrdf normal uses bump */
|
||||
SD_USE_MIS = 1024, /* direct light sample */
|
||||
SD_HAS_TRANSPARENT_SHADOW = 2048, /* has transparent shadow */
|
||||
SD_HAS_VOLUME = 4096, /* has volume shader */
|
||||
SD_HAS_ONLY_VOLUME = 8192, /* has only volume shader, no surface */
|
||||
SD_HOMOGENEOUS_VOLUME = 16384, /* has homogeneous volume */
|
||||
SD_HAS_BSSRDF_BUMP = 32768, /* bssrdf normal uses bump */
|
||||
|
||||
SD_SHADER_FLAGS = (SD_USE_MIS|SD_HAS_TRANSPARENT_SHADOW|SD_HAS_VOLUME|SD_HAS_ONLY_VOLUME|SD_HOMOGENEOUS_VOLUME|SD_HAS_BSSRDF_BUMP),
|
||||
|
||||
/* object flags */
|
||||
SD_HOLDOUT_MASK = 32768, /* holdout for camera rays */
|
||||
SD_OBJECT_MOTION = 65536, /* has object motion blur */
|
||||
SD_TRANSFORM_APPLIED = 131072, /* vertices have transform applied */
|
||||
SD_HOLDOUT_MASK = 65536, /* holdout for camera rays */
|
||||
SD_OBJECT_MOTION = 131072, /* has object motion blur */
|
||||
SD_TRANSFORM_APPLIED = 262144, /* vertices have transform applied */
|
||||
|
||||
SD_OBJECT_FLAGS = (SD_HOLDOUT_MASK|SD_OBJECT_MOTION|SD_TRANSFORM_APPLIED)
|
||||
};
|
||||
|
@@ -16,16 +16,23 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
typedef enum VolumeIntegrateResult {
|
||||
VOLUME_PATH_TERMINATED = 0,
|
||||
VOLUME_PATH_SCATTERED = 1,
|
||||
VOLUME_PATH_ATTENUATED = 2,
|
||||
VOLUME_PATH_MISSED = 3
|
||||
} VolumeIntegrateResult;
|
||||
|
||||
/* Volume shader properties
|
||||
*
|
||||
* extinction coefficient = absorption coefficient + scattering coefficient
|
||||
* sigma_t = sigma_a + sigma_s */
|
||||
|
||||
typedef struct VolumeShaderSample {
|
||||
typedef struct VolumeShaderCoefficients {
|
||||
float3 sigma_a;
|
||||
float3 sigma_s;
|
||||
float3 emission;
|
||||
} VolumeShaderSample;
|
||||
} VolumeShaderCoefficients;
|
||||
|
||||
/* evaluate shader to get extinction coefficient at P */
|
||||
ccl_device bool volume_shader_extinction_sample(KernelGlobals *kg, ShaderData *sd, VolumeStack *stack, int path_flag, ShaderContext ctx, float3 P, float3 *extinction)
|
||||
@@ -33,7 +40,7 @@ ccl_device bool volume_shader_extinction_sample(KernelGlobals *kg, ShaderData *s
|
||||
sd->P = P;
|
||||
shader_eval_volume(kg, sd, stack, 0.0f, path_flag, ctx);
|
||||
|
||||
if(!(sd->flag & SD_VOLUME))
|
||||
if(!(sd->flag & (SD_ABSORPTION|SD_SCATTER)))
|
||||
return false;
|
||||
|
||||
float3 sigma_t = make_float3(0.0f, 0.0f, 0.0f);
|
||||
@@ -50,12 +57,12 @@ ccl_device bool volume_shader_extinction_sample(KernelGlobals *kg, ShaderData *s
|
||||
}
|
||||
|
||||
/* evaluate shader to get absorption, scattering and emission at P */
|
||||
ccl_device bool volume_shader_sample(KernelGlobals *kg, ShaderData *sd, VolumeStack *stack, int path_flag, ShaderContext ctx, float3 P, VolumeShaderSample *sample)
|
||||
ccl_device bool volume_shader_sample(KernelGlobals *kg, ShaderData *sd, VolumeStack *stack, int path_flag, ShaderContext ctx, float3 P, VolumeShaderCoefficients *sample)
|
||||
{
|
||||
sd->P = P;
|
||||
shader_eval_volume(kg, sd, stack, 0.0f, path_flag, ctx);
|
||||
|
||||
if(!(sd->flag & (SD_VOLUME|SD_EMISSION)))
|
||||
if(!(sd->flag & (SD_ABSORPTION|SD_SCATTER|SD_EMISSION)))
|
||||
return false;
|
||||
|
||||
sample->sigma_a = make_float3(0.0f, 0.0f, 0.0f);
|
||||
@@ -83,12 +90,12 @@ ccl_device float3 volume_color_attenuation(float3 sigma, float t)
|
||||
|
||||
/* Volumetric Shadows */
|
||||
|
||||
/* get the volume attenuation over line segment defined by segment_ray, with the
|
||||
/* get the volume attenuation over line segment defined by ray, with the
|
||||
* assumption that there are no surfaces blocking light between the endpoints */
|
||||
ccl_device void kernel_volume_get_shadow_attenuation(KernelGlobals *kg, PathState *state, Ray *segment_ray, float3 *throughput)
|
||||
ccl_device void kernel_volume_get_shadow_attenuation(KernelGlobals *kg, PathState *state, Ray *ray, float3 *throughput)
|
||||
{
|
||||
ShaderData sd;
|
||||
shader_setup_from_volume(kg, &sd, segment_ray, state->bounce);
|
||||
shader_setup_from_volume(kg, &sd, ray, state->bounce);
|
||||
|
||||
ShaderContext ctx = SHADER_CONTEXT_SHADOW;
|
||||
int path_flag = PATH_RAY_SHADOW;
|
||||
@@ -96,54 +103,58 @@ ccl_device void kernel_volume_get_shadow_attenuation(KernelGlobals *kg, PathStat
|
||||
|
||||
/* homogenous volume: assume shader evaluation at the starts gives
|
||||
* the extinction coefficient for the entire line segment */
|
||||
if(!volume_shader_extinction_sample(kg, &sd, state->volume_stack, path_flag, ctx, segment_ray->P, &sigma_t))
|
||||
if(!volume_shader_extinction_sample(kg, &sd, state->volume_stack, path_flag, ctx, ray->P, &sigma_t))
|
||||
return;
|
||||
|
||||
*throughput *= volume_color_attenuation(sigma_t, segment_ray->t);
|
||||
*throughput *= volume_color_attenuation(sigma_t, ray->t);
|
||||
}
|
||||
|
||||
/* Volumetric Path */
|
||||
|
||||
/* get the volume attenuation and emission over line segment defined by
|
||||
* segment_ray, with the assumption that there are no surfaces blocking light
|
||||
* ray, with the assumption that there are no surfaces blocking light
|
||||
* between the endpoints */
|
||||
ccl_device void kernel_volume_integrate(KernelGlobals *kg, PathState *state, Ray *segment_ray, PathRadiance *L, float3 *throughput)
|
||||
ccl_device VolumeIntegrateResult kernel_volume_integrate(KernelGlobals *kg, PathState *state, Ray *ray, PathRadiance *L, float3 *throughput)
|
||||
{
|
||||
ShaderData sd;
|
||||
shader_setup_from_volume(kg, &sd, segment_ray, state->bounce);
|
||||
shader_setup_from_volume(kg, &sd, ray, state->bounce);
|
||||
|
||||
ShaderContext ctx = SHADER_CONTEXT_VOLUME;
|
||||
int path_flag = PATH_RAY_SHADOW;
|
||||
VolumeShaderSample sample;
|
||||
VolumeShaderCoefficients coeff;
|
||||
|
||||
/* homogenous volume: assume shader evaluation at the starts gives
|
||||
* the extinction coefficient for the entire line segment */
|
||||
if(!volume_shader_sample(kg, &sd, state->volume_stack, path_flag, ctx, segment_ray->P, &sample))
|
||||
return;
|
||||
if(!volume_shader_sample(kg, &sd, state->volume_stack, path_flag, ctx, ray->P, &coeff))
|
||||
return VOLUME_PATH_MISSED;
|
||||
|
||||
/* todo: in principle the SD_EMISSION, SD_ABSORPTION and SD_SCATTER flags
|
||||
* should ensure that one of the components is > 0 and so no division by
|
||||
* zero occurs, however this needs to be double-checked and tested */
|
||||
|
||||
int closure_flag = sd.flag;
|
||||
float t = segment_ray->t;
|
||||
float t = ray->t;
|
||||
|
||||
/* compute attenuation from absorption (+ scattering for now) */
|
||||
float3 sigma_t, attenuation;
|
||||
float3 attenuation;
|
||||
|
||||
if(closure_flag & SD_VOLUME) {
|
||||
sigma_t = sample.sigma_a + sample.sigma_s;
|
||||
attenuation = volume_color_attenuation(sigma_t, t);
|
||||
}
|
||||
if(closure_flag & SD_ABSORPTION)
|
||||
attenuation = volume_color_attenuation(coeff.sigma_a, t);
|
||||
|
||||
/* integrate emission attenuated by absorption
|
||||
* integral E * exp(-sigma_t * t) from 0 to t = E * (1 - exp(-sigma_t * t))/sigma_t
|
||||
* this goes to E * t as sigma_t goes to zero
|
||||
* integral E * exp(-sigma_a * t) from 0 to t = E * (1 - exp(-sigma_a * t))/sigma_a
|
||||
* this goes to E * t as sigma_a goes to zero
|
||||
*
|
||||
* todo: we should use an epsilon to avoid precision issues near zero sigma_t */
|
||||
* todo: we should use an epsilon to avoid precision issues near zero sigma_a */
|
||||
if(closure_flag & SD_EMISSION) {
|
||||
float3 emission = sample.emission;
|
||||
float3 emission = coeff.emission;
|
||||
|
||||
if(closure_flag & SD_VOLUME) {
|
||||
emission.x *= (sigma_t.x > 0.0f)? (1.0f - attenuation.x)/sigma_t.x: t;
|
||||
emission.y *= (sigma_t.y > 0.0f)? (1.0f - attenuation.y)/sigma_t.y: t;
|
||||
emission.z *= (sigma_t.z > 0.0f)? (1.0f - attenuation.z)/sigma_t.z: t;
|
||||
if(closure_flag & SD_ABSORPTION) {
|
||||
float3 sigma_a = coeff.sigma_a;
|
||||
|
||||
emission.x *= (sigma_a.x > 0.0f)? (1.0f - attenuation.x)/sigma_a.x: t;
|
||||
emission.y *= (sigma_a.y > 0.0f)? (1.0f - attenuation.y)/sigma_a.y: t;
|
||||
emission.z *= (sigma_a.z > 0.0f)? (1.0f - attenuation.z)/sigma_a.z: t;
|
||||
}
|
||||
else
|
||||
emission *= t;
|
||||
@@ -152,8 +163,10 @@ ccl_device void kernel_volume_integrate(KernelGlobals *kg, PathState *state, Ray
|
||||
}
|
||||
|
||||
/* modify throughput */
|
||||
if(closure_flag & SD_VOLUME)
|
||||
if(closure_flag & SD_ABSORPTION)
|
||||
*throughput *= attenuation;
|
||||
|
||||
return VOLUME_PATH_ATTENUATED;
|
||||
}
|
||||
|
||||
/* Volume Stack */
|
||||
|
Reference in New Issue
Block a user