Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2013-12-29 02:00:51 +0400
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2013-12-29 02:20:53 +0400
commitfe222643b4a53fae38bc5ead86551b99abbdc583 (patch)
tree382d10ebc69d9ac0003ec6451d9e56e88408b358 /intern/cycles
parent077fe03eaf29da93df94d9f3a38c3eb0180364cd (diff)
Cycles Volume Render: add volume emission support.
This is done using the existing Emission node and closure (we may add a volume emission node, not clear yet if it will be needed). Volume emission only supports indirect light sampling which means it's not very efficient to make small or far away bright light sources. Using direct light sampling and MIS would be tricky and probably won't be added anytime soon. Other renderers don't support this either as far as I know, lamps and ray visibility tricks may be used instead.
Diffstat (limited to 'intern/cycles')
-rw-r--r--intern/cycles/kernel/closure/volume.h2
-rw-r--r--intern/cycles/kernel/kernel_path.h6
-rw-r--r--intern/cycles/kernel/kernel_shader.h7
-rw-r--r--intern/cycles/kernel/kernel_shadow.h6
-rw-r--r--intern/cycles/kernel/kernel_volume.h137
5 files changed, 103 insertions, 55 deletions
diff --git a/intern/cycles/kernel/closure/volume.h b/intern/cycles/kernel/closure/volume.h
index dae24fb03fd..4cf918f32b8 100644
--- a/intern/cycles/kernel/closure/volume.h
+++ b/intern/cycles/kernel/closure/volume.h
@@ -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;
+ return SD_BSDF|SD_BSDF_HAS_EVAL|SD_VOLUME;
}
ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, const float3 I, float3 omega_in, float *pdf)
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index b7039704c44..7aea673528a 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -96,7 +96,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ra
if(state.volume_stack[0].shader != SHADER_NO_ID) {
Ray segment_ray = ray;
segment_ray.t = (hit)? isect.t: FLT_MAX;
- throughput *= kernel_volume_get_shadow_attenuation(kg, &state, &segment_ray);
+ kernel_volume_integrate(kg, &state, &segment_ray, L, &throughput);
}
#endif
@@ -518,7 +518,7 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
if(state.volume_stack[0].shader != SHADER_NO_ID) {
Ray segment_ray = ray;
segment_ray.t = (hit)? isect.t: FLT_MAX;
- throughput *= kernel_volume_get_shadow_attenuation(kg, &state, &segment_ray);
+ kernel_volume_integrate(kg, &state, &segment_ray, &L, &throughput);
}
#endif
@@ -1022,7 +1022,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
if(state.volume_stack[0].shader != SHADER_NO_ID) {
Ray segment_ray = ray;
segment_ray.t = (hit)? isect.t: FLT_MAX;
- throughput *= kernel_volume_get_shadow_attenuation(kg, &state, &segment_ray);
+ kernel_volume_integrate(kg, &state, &segment_ray, &L, &throughput);
}
#endif
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 685143b29c3..7b9a4ab12b1 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -412,15 +412,15 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals *kg, ShaderDat
/* ShaderData setup from point inside volume */
-ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *sd, const Ray *ray, int volume_shader, int bounce)
+ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *sd, const Ray *ray, int bounce)
{
/* vectors */
sd->P = ray->P;
sd->N = -ray->D;
sd->Ng = -ray->D;
sd->I = -ray->D;
- sd->shader = volume_shader;
- sd->flag = kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*2);
+ sd->shader = SHADER_NO_ID;
+ sd->flag = 0;
#ifdef __OBJECT_MOTION__
sd->time = ray->time;
#endif
@@ -992,6 +992,7 @@ ccl_device void shader_eval_volume(KernelGlobals *kg, ShaderData *sd,
#else
sd->closure.type = NBUILTIN_CLOSURES;
#endif
+ sd->flag = 0;
for(int i = 0; stack[i].shader != SHADER_NO_ID; i++) {
/* setup shaderdata from stack. it's mostly setup already in
diff --git a/intern/cycles/kernel/kernel_shadow.h b/intern/cycles/kernel/kernel_shadow.h
index 80c9da8eab8..4bf063ee185 100644
--- a/intern/cycles/kernel/kernel_shadow.h
+++ b/intern/cycles/kernel/kernel_shadow.h
@@ -74,7 +74,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *
#ifdef __VOLUME__
/* attenuation for last line segment towards light */
if(ps.volume_stack[0].shader != SHADER_NO_ID)
- throughput *= kernel_volume_get_shadow_attenuation(kg, &ps, ray);
+ kernel_volume_get_shadow_attenuation(kg, &ps, ray, &throughput);
#endif
*shadow *= throughput;
@@ -89,7 +89,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *
if(ps.volume_stack[0].shader != SHADER_NO_ID) {
Ray segment_ray = *ray;
segment_ray.t = isect.t;
- throughput *= kernel_volume_get_shadow_attenuation(kg, &ps, &segment_ray);
+ kernel_volume_get_shadow_attenuation(kg, &ps, &segment_ray, &throughput);
}
#endif
@@ -120,7 +120,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *
#ifdef __VOLUME__
else if(!result && state->volume_stack[0].shader != SHADER_NO_ID) {
/* apply attenuation from current volume shader */
- *shadow *= kernel_volume_get_shadow_attenuation(kg, state, ray);
+ kernel_volume_get_shadow_attenuation(kg, state, ray, shadow);
}
#endif
#endif
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index fa201e1ff46..6b198ed9173 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -21,8 +21,21 @@ CCL_NAMESPACE_BEGIN
* extinction coefficient = absorption coefficient + scattering coefficient
* sigma_t = sigma_a + sigma_s */
-ccl_device float3 volume_shader_get_extinction_coefficient(ShaderData *sd)
+typedef struct VolumeShaderSample {
+ float3 sigma_a;
+ float3 sigma_s;
+ float3 emission;
+} VolumeShaderSample;
+
+/* 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)
{
+ sd->P = P;
+ shader_eval_volume(kg, sd, stack, 0.0f, path_flag, ctx);
+
+ if(!(sd->flag & SD_VOLUME))
+ return false;
+
float3 sigma_t = make_float3(0.0f, 0.0f, 0.0f);
for(int i = 0; i < sd->num_closure; i++) {
@@ -32,45 +45,35 @@ ccl_device float3 volume_shader_get_extinction_coefficient(ShaderData *sd)
sigma_t += sc->weight;
}
- return sigma_t;
+ *extinction = sigma_t;
+ return true;
}
-ccl_device float3 volume_shader_get_scattering_coefficient(ShaderData *sd)
+/* 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)
{
- float3 sigma_s = make_float3(0.0f, 0.0f, 0.0f);
-
- for(int i = 0; i < sd->num_closure; i++) {
- const ShaderClosure *sc = &sd->closure[i];
-
- if(CLOSURE_IS_VOLUME(sc->type) && sc->type != CLOSURE_VOLUME_ABSORPTION_ID)
- sigma_s += sc->weight;
- }
+ sd->P = P;
+ shader_eval_volume(kg, sd, stack, 0.0f, path_flag, ctx);
- return sigma_s;
-}
+ if(!(sd->flag & (SD_VOLUME|SD_EMISSION)))
+ return false;
-ccl_device float3 volume_shader_get_absorption_coefficient(ShaderData *sd)
-{
- float3 sigma_a = make_float3(0.0f, 0.0f, 0.0f);
+ sample->sigma_a = make_float3(0.0f, 0.0f, 0.0f);
+ sample->sigma_s = make_float3(0.0f, 0.0f, 0.0f);
+ sample->emission = make_float3(0.0f, 0.0f, 0.0f);
for(int i = 0; i < sd->num_closure; i++) {
const ShaderClosure *sc = &sd->closure[i];
if(sc->type == CLOSURE_VOLUME_ABSORPTION_ID)
- sigma_a += sc->weight;
+ sample->sigma_a += sc->weight;
+ else if(sc->type == CLOSURE_EMISSION_ID)
+ sample->emission += sc->weight;
+ else if(CLOSURE_IS_VOLUME(sc->type))
+ sample->sigma_s += sc->weight;
}
- return sigma_a;
-}
-
-/* evaluate shader to get extinction coefficient at P */
-ccl_device float3 volume_extinction_sample(KernelGlobals *kg, ShaderData *sd, VolumeStack *stack, int path_flag, ShaderContext ctx, float3 P)
-{
- sd->P = P;
-
- shader_eval_volume(kg, sd, stack, 0.0f, path_flag, ctx);
-
- return volume_shader_get_extinction_coefficient(sd);
+ return true;
}
ccl_device float3 volume_color_attenuation(float3 sigma, float t)
@@ -81,32 +84,76 @@ 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
- * assumption that there are surfaces blocking light between the endpoints */
-ccl_device float3 kernel_volume_get_shadow_attenuation(KernelGlobals *kg, PathState *state, Ray *segment_ray)
+ * 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)
{
ShaderData sd;
- shader_setup_from_volume(kg, &sd, segment_ray, state->volume_stack[0].shader, state->bounce);
-
- /* do we have a volume shader? */
- if(!(sd.flag & SD_HAS_VOLUME))
- return make_float3(1.0f, 1.0f, 1.0f);
+ shader_setup_from_volume(kg, &sd, segment_ray, state->bounce);
- /* single shader evaluation at the start */
ShaderContext ctx = SHADER_CONTEXT_SHADOW;
int path_flag = PATH_RAY_SHADOW;
- float3 attenuation;
+ float3 sigma_t;
+
+ /* 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))
+ return;
+
+ *throughput *= volume_color_attenuation(sigma_t, segment_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
+ * between the endpoints */
+ccl_device void kernel_volume_integrate(KernelGlobals *kg, PathState *state, Ray *segment_ray, PathRadiance *L, float3 *throughput)
+{
+ ShaderData sd;
+ shader_setup_from_volume(kg, &sd, segment_ray, state->bounce);
+
+ ShaderContext ctx = SHADER_CONTEXT_VOLUME;
+ int path_flag = PATH_RAY_SHADOW;
+ VolumeShaderSample sample;
+
+ /* 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;
+
+ int closure_flag = sd.flag;
+ float t = segment_ray->t;
- //if(sd.flag & SD_HOMOGENEOUS_VOLUME) {
- /* homogenous volume: assume shader evaluation at the starts gives
- * the extinction coefficient for the entire line segment */
+ /* compute attenuation from absorption (+ scattering for now) */
+ float3 sigma_t, attenuation;
- /* todo: could this use sigma_t_cache? */
- float3 sigma_t = volume_extinction_sample(kg, &sd, state->volume_stack, path_flag, ctx, segment_ray->P);
+ if(closure_flag & SD_VOLUME) {
+ sigma_t = sample.sigma_a + sample.sigma_s;
+ attenuation = volume_color_attenuation(sigma_t, t);
+ }
- attenuation = volume_color_attenuation(sigma_t, segment_ray->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
+ *
+ * todo: we should use an epsilon to avoid precision issues near zero sigma_t */
+ if(closure_flag & SD_EMISSION) {
+ float3 emission = sample.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;
+ }
+ else
+ emission *= t;
+
+ path_radiance_accum_emission(L, *throughput, emission, state->bounce);
+ }
- return attenuation;
+ /* modify throughput */
+ if(closure_flag & SD_VOLUME)
+ *throughput *= attenuation;
}
/* Volume Stack */