Cycles Volume Render: support for rendering of homogeneous volume with absorption.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Sat, 28 Dec 2013 15:56:19 +0000 (16:56 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Sat, 28 Dec 2013 15:57:10 +0000 (16:57 +0100)
This is the simplest possible volume rendering case, constant density inside
the volume and no scattering or emission. My plan is to tweak, verify and commit
more volume rendering effects one by one, doing it all at once makes it
difficult to verify correctness and track down bugs.

Documentation is here:
http://wiki.blender.org/index.php/Doc:2.6/Manual/Render/Cycles/Materials/Volume

Currently this hooks into path tracing in 3 ways, which should get us pretty
far until we add more advanced light sampling. These 3 hooks are repeated in
the path tracing, branched path tracing and transparent shadow code:

* Determine active volume shader at start of the path
* Change active volume shader on transmission through a surface
* Light attenuation over line segments between camera, surfaces and background

This is work by "storm", Stuart Broadfoot, Thomas Dinges and myself.

15 files changed:
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_shader.cpp
intern/cycles/kernel/CMakeLists.txt
intern/cycles/kernel/kernel_path.h
intern/cycles/kernel/kernel_path_state.h
intern/cycles/kernel/kernel_shader.h
intern/cycles/kernel/kernel_shadow.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/kernel_volume.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_closure.h
intern/cycles/render/scene.cpp
release/scripts/startup/nodeitems_builtins.py
source/blender/blenkernel/intern/node.c
source/blender/editors/space_node/drawnode.c

index f5c052e7f0cd87d3f2a53f713b7476d22aad986c..e446e4711e4ff7e4186d31e437e14d88f4606bf0 100644 (file)
@@ -577,6 +577,12 @@ class CyclesWorldSettings(bpy.types.PropertyGroup):
                 min=1, max=10000,
                 default=4,
                 )
+        cls.homogeneous_volume = BoolProperty(
+                name="Homogeneous Volume",
+                description="When using volume rendering, assume volume has the same density everywhere, "
+                            "for faster rendering",
+                default=False,
+                )
 
     @classmethod
     def unregister(cls):
index 83483cbcae982ce7e1ffc604f87b2c227b79e299..02d1dfb1542740b998e601f967967cd740726c99 100644 (file)
@@ -780,9 +780,8 @@ class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel):
 
     @classmethod
     def poll(cls, context):
-        # world = context.world
-        # world and world.node_tree and CyclesButtonsPanel.poll(context)
-        return False
+        world = context.world
+        return world and world.node_tree and CyclesButtonsPanel.poll(context)
 
     def draw(self, context):
         layout = self.layout
@@ -790,6 +789,8 @@ class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel):
         world = context.world
         panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume')
 
+        layout.prop(world.cycles, "homogeneous_volume")
+
 
 class CyclesWorld_PT_ambient_occlusion(CyclesButtonsPanel, Panel):
     bl_label = "Ambient Occlusion"
@@ -926,9 +927,8 @@ class CyclesMaterial_PT_volume(CyclesButtonsPanel, Panel):
 
     @classmethod
     def poll(cls, context):
-        # mat = context.material
-        # mat and mat.node_tree and CyclesButtonsPanel.poll(context)
-        return False
+        mat = context.material
+        return mat and mat.node_tree and CyclesButtonsPanel.poll(context)
 
     def draw(self, context):
         layout = self.layout
index 0c3a32af20db2305e2af08f2ed484d18da4ee745..6ec72c33466b285e816f3fc4b4b328e5e7b4f8fb 100644 (file)
@@ -956,6 +956,9 @@ void BlenderSync::sync_world(bool update_all)
                        out = graph->output();
 
                        graph->connect(closure->output("Background"), out->input("Surface"));
+
+                       PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
+                       shader->homogeneous_volume = get_boolean(cworld, "homogeneous_volume");
                }
 
                if(b_world) {
index 63637991c2917339ecd79d32800ffc7379d28d3f..860909818b33a42b5f29fbf4c269f3268bbf4816 100644 (file)
@@ -51,6 +51,7 @@ set(SRC_HEADERS
        kernel_textures.h
        kernel_triangle.h
        kernel_types.h
+       kernel_volume.h
 )
 
 set(SRC_CLOSURE_HEADERS
index 903efc9d6f5c788caf6b11c14aab52043a8c17e1..d318a85fcb70fb86a56bc67ba96f6b895a4f9e30 100644 (file)
 #include "kernel_subsurface.h"
 #endif
 
+#ifdef __VOLUME__
+#include "kernel_volume.h"
+#endif
+
 #include "kernel_shadow.h"
 
 CCL_NAMESPACE_BEGIN
@@ -87,6 +91,15 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ra
                }
 #endif
 
+#ifdef __VOLUME__
+               /* volume attenuation */
+               if(state.volume_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, state.volume_shader);
+               }
+#endif
+
                if(!hit) {
 #ifdef __BACKGROUND__
                        /* sample background shader */
@@ -235,47 +248,73 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ra
 #endif
 
                /* no BSDF? we can stop here */
-               if(!(sd.flag & SD_BSDF))
-                       break;
-
-               /* sample BSDF */
-               float bsdf_pdf;
-               BsdfEval bsdf_eval;
-               float3 bsdf_omega_in;
-               differential3 bsdf_domega_in;
-               float bsdf_u, bsdf_v;
-               path_rng_2D(kg, rng, sample, num_total_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-               int label;
+               if(sd.flag & SD_BSDF) {
+                       /* sample BSDF */
+                       float bsdf_pdf;
+                       BsdfEval bsdf_eval;
+                       float3 bsdf_omega_in;
+                       differential3 bsdf_domega_in;
+                       float bsdf_u, bsdf_v;
+                       path_rng_2D(kg, rng, sample, num_total_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
+                       int label;
 
-               label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
-                       &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+                       label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
+                               &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
 
-               if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
-                       break;
+                       if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
+                               break;
 
-               /* modify throughput */
-               path_radiance_bsdf_bounce(L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
+                       /* modify throughput */
+                       path_radiance_bsdf_bounce(L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
 
-               /* set labels */
-               if(!(label & LABEL_TRANSPARENT)) {
-                       ray_pdf = bsdf_pdf;
+                       /* set labels */
+                       if(!(label & LABEL_TRANSPARENT)) {
+                               ray_pdf = bsdf_pdf;
 #ifdef __LAMP_MIS__
-                       ray_t = 0.0f;
+                               ray_t = 0.0f;
+#endif
+                               min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
+                       }
+
+                       /* update path state */
+                       path_state_next(kg, &state, label);
+
+                       /* setup ray */
+                       ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
+                       ray.D = bsdf_omega_in;
+                       ray.t = FLT_MAX;
+#ifdef __RAY_DIFFERENTIALS__
+                       ray.dP = sd.dP;
+                       ray.dD = bsdf_domega_in;
+#endif
+
+#ifdef __VOLUME__
+                       /* enter/exit volume */
+                       if(label & LABEL_TRANSMIT)
+                               kernel_volume_enter_exit(kg, &sd, &state.volume_shader);
 #endif
-                       min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
                }
+#ifdef __VOLUME__
+               else if(sd.flag & SD_HAS_ONLY_VOLUME) {
+                       /* no surface shader but have a volume shader? act transparent */
 
-               /* update path state */
-               path_state_next(kg, &state, label);
+                       /* update path state, count as transparent */
+                       path_state_next(kg, &state, LABEL_TRANSPARENT);
 
-               /* setup ray */
-               ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
-               ray.D = bsdf_omega_in;
-               ray.t = FLT_MAX;
+                       /* setup ray position, direction stays unchanged */
+                       ray.P = ray_offset(sd.P, -sd.Ng);
 #ifdef __RAY_DIFFERENTIALS__
-               ray.dP = sd.dP;
-               ray.dD = bsdf_domega_in;
+                       ray.dP = sd.dP;
 #endif
+
+                       /* enter/exit volume */
+                       kernel_volume_enter_exit(kg, &sd, &state.volume_shader);
+               }
+#endif
+               else {
+                       /* no bsdf or volume? we're done */
+                       break;
+               }
        }
 }
 
@@ -324,54 +363,80 @@ ccl_device_inline bool kernel_path_integrate_lighting(KernelGlobals *kg, RNG *rn
 #endif
 
        /* no BSDF? we can stop here */
-       if(!(sd->flag & SD_BSDF))
-               return false;
-
-       /* sample BSDF */
-       float bsdf_pdf;
-       BsdfEval bsdf_eval;
-       float3 bsdf_omega_in;
-       differential3 bsdf_domega_in;
-       float bsdf_u, bsdf_v;
-       path_rng_2D(kg, rng, sample, num_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-       int label;
+       if(sd->flag & SD_BSDF) {
+               /* sample BSDF */
+               float bsdf_pdf;
+               BsdfEval bsdf_eval;
+               float3 bsdf_omega_in;
+               differential3 bsdf_domega_in;
+               float bsdf_u, bsdf_v;
+               path_rng_2D(kg, rng, sample, num_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
+               int label;
 
-       label = shader_bsdf_sample(kg, sd, bsdf_u, bsdf_v, &bsdf_eval,
-               &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+               label = shader_bsdf_sample(kg, sd, bsdf_u, bsdf_v, &bsdf_eval,
+                       &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
 
-       if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
-               return false;
+               if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
+                       return false;
 
-       /* modify throughput */
-       path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
+               /* modify throughput */
+               path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
 
-       /* set labels */
-       if(!(label & LABEL_TRANSPARENT)) {
-               *ray_pdf = bsdf_pdf;
+               /* set labels */
+               if(!(label & LABEL_TRANSPARENT)) {
+                       *ray_pdf = bsdf_pdf;
 #ifdef __LAMP_MIS__
-               *ray_t = 0.0f;
+                       *ray_t = 0.0f;
 #endif
-               *min_ray_pdf = fminf(bsdf_pdf, *min_ray_pdf);
-       }
+                       *min_ray_pdf = fminf(bsdf_pdf, *min_ray_pdf);
+               }
 
-       /* update path state */
-       path_state_next(kg, state, label);
+               /* update path state */
+               path_state_next(kg, state, label);
 
-       /* setup ray */
-       ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
-       ray->D = bsdf_omega_in;
+               /* setup ray */
+               ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
+               ray->D = bsdf_omega_in;
 
-       if(state->bounce == 0)
-               ray->t -= sd->ray_length; /* clipping works through transparent */
-       else
-               ray->t = FLT_MAX;
+               if(state->bounce == 0)
+                       ray->t -= sd->ray_length; /* clipping works through transparent */
+               else
+                       ray->t = FLT_MAX;
+
+#ifdef __RAY_DIFFERENTIALS__
+               ray->dP = sd->dP;
+               ray->dD = bsdf_domega_in;
+#endif
+
+#ifdef __VOLUME__
+               /* enter/exit volume */
+               if(label & LABEL_TRANSMIT)
+                       kernel_volume_enter_exit(kg, sd, &state->volume_shader);
+#endif
+               return true;
+       }
+#ifdef __VOLUME__
+       else if(sd->flag & SD_HAS_ONLY_VOLUME) {
+               /* no surface shader but have a volume shader? act transparent */
+
+               /* update path state, count as transparent */
+               path_state_next(kg, state, LABEL_TRANSPARENT);
 
+               /* setup ray position, direction stays unchanged */
+               ray->P = ray_offset(sd->P, -sd->Ng);
 #ifdef __RAY_DIFFERENTIALS__
-       ray->dP = sd->dP;
-       ray->dD = bsdf_domega_in;
+               ray->dP = sd->dP;
+#endif
+
+               /* enter/exit volume */
+               kernel_volume_enter_exit(kg, sd, &state->volume_shader);
+               return true;
+       }
 #endif
-       
-       return true;
+       else {
+               /* no bsdf or volume? */
+               return false;
+       }
 }
 
 #endif
@@ -398,7 +463,7 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
        int num_samples = 0;
 #endif
 
-       path_state_init(&state);
+       path_state_init(kg, &state);
 
        /* path iteration */
        for(;; rng_offset += PRNG_BOUNCE_NUM) {
@@ -448,6 +513,15 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
                }
 #endif
 
+#ifdef __VOLUME__
+               /* volume attenuation */
+               if(state.volume_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, state.volume_shader);
+               }
+#endif
+
                if(!hit) {
                        /* eval background shader if nothing hit */
                        if(kernel_data.background.transparent && (state.flag & PATH_RAY_CAMERA)) {
@@ -652,53 +726,80 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
                }
 #endif
 
-               /* no BSDF? we can stop here */
-               if(!(sd.flag & SD_BSDF))
-                       break;
-
-               /* sample BSDF */
-               float bsdf_pdf;
-               BsdfEval bsdf_eval;
-               float3 bsdf_omega_in;
-               differential3 bsdf_domega_in;
-               float bsdf_u, bsdf_v;
-               path_rng_2D(kg, rng, sample, num_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-               int label;
+               if(sd.flag & SD_BSDF) {
+                       /* sample BSDF */
+                       float bsdf_pdf;
+                       BsdfEval bsdf_eval;
+                       float3 bsdf_omega_in;
+                       differential3 bsdf_domega_in;
+                       float bsdf_u, bsdf_v;
+                       path_rng_2D(kg, rng, sample, num_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
+                       int label;
 
-               label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
-                       &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+                       label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
+                               &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
 
-               if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
-                       break;
+                       if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
+                               break;
 
-               /* modify throughput */
-               path_radiance_bsdf_bounce(&L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
+                       /* modify throughput */
+                       path_radiance_bsdf_bounce(&L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
 
-               /* set labels */
-               if(!(label & LABEL_TRANSPARENT)) {
-                       ray_pdf = bsdf_pdf;
+                       /* set labels */
+                       if(!(label & LABEL_TRANSPARENT)) {
+                               ray_pdf = bsdf_pdf;
 #ifdef __LAMP_MIS__
-                       ray_t = 0.0f;
+                               ray_t = 0.0f;
+#endif
+                               min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
+                       }
+
+                       /* update path state */
+                       path_state_next(kg, &state, label);
+
+                       /* setup ray */
+                       ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
+                       ray.D = bsdf_omega_in;
+
+#ifdef __RAY_DIFFERENTIALS__
+                       ray.dP = sd.dP;
+                       ray.dD = bsdf_domega_in;
 #endif
-                       min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
+
+#ifdef __VOLUME__
+                       /* enter/exit volume */
+                       if(label & LABEL_TRANSMIT)
+                               kernel_volume_enter_exit(kg, &sd, &state.volume_shader);
+#endif
+
                }
+#ifdef __VOLUME__
+               else if(sd.flag & SD_HAS_ONLY_VOLUME) {
+                       /* no surface shader but have a volume shader? act transparent */
 
-               /* update path state */
-               path_state_next(kg, &state, label);
+                       /* update path state, count as transparent */
+                       path_state_next(kg, &state, LABEL_TRANSPARENT);
 
-               /* setup ray */
-               ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
-               ray.D = bsdf_omega_in;
+                       /* setup ray position, direction stays unchanged */
+                       ray.P = ray_offset(sd.P, -sd.Ng);
+#ifdef __RAY_DIFFERENTIALS__
+                       ray.dP = sd.dP;
+#endif
+
+                       /* enter/exit volume */
+                       kernel_volume_enter_exit(kg, &sd, &state.volume_shader);
+               }
+#endif
+               else {
+                       /* no bsdf or volume? we're done */
+                       break;
+               }
 
+               /* adjust ray distance for clipping */
                if(state.bounce == 0)
                        ray.t -= sd.ray_length; /* clipping works through transparent */
                else
                        ray.t = FLT_MAX;
-
-#ifdef __RAY_DIFFERENTIALS__
-               ray.dP = sd.dP;
-               ray.dD = bsdf_domega_in;
-#endif
        }
 
        float3 L_sum = path_radiance_sum(kg, &L);
@@ -853,6 +954,12 @@ ccl_device_noinline void kernel_branched_path_integrate_lighting(KernelGlobals *
                        bsdf_ray.time = sd->time;
 #endif
 
+#ifdef __VOLUME__
+                       /* enter/exit volume */
+                       if(label & LABEL_TRANSMIT)
+                               kernel_volume_enter_exit(kg, sd, &ps.volume_shader);
+#endif
+
                        kernel_path_indirect(kg, rng, sample*num_samples + j, bsdf_ray, buffer,
                                tp*num_samples_inv, num_samples, aa_samples*num_samples,
                                min_ray_pdf, bsdf_pdf, ps, rng_offset+PRNG_BOUNCE_NUM, L);
@@ -883,7 +990,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
        int aa_samples = 0;
 #endif
 
-       path_state_init(&state);
+       path_state_init(kg, &state);
 
        for(;; rng_offset += PRNG_BOUNCE_NUM) {
                /* intersect scene */
@@ -905,10 +1012,21 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
                        lcg_state = lcg_init(*rng + rng_offset + sample*0x51633e2d);
                }
 
-               if(!scene_intersect(kg, &ray, visibility, &isect, &lcg_state, difl, extmax)) {
+               bool hit = scene_intersect(kg, &ray, visibility, &isect, &lcg_state, difl, extmax);
 #else
-               if(!scene_intersect(kg, &ray, visibility, &isect)) {
+               bool hit = scene_intersect(kg, &ray, visibility, &isect);
 #endif
+
+#ifdef __VOLUME__
+               /* volume attenuation */
+               if(state.volume_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, state.volume_shader);
+               }
+#endif
+
+               if(!hit) {
                        /* eval background shader if nothing hit */
                        if(kernel_data.background.transparent) {
                                L_transparent += average(throughput);
@@ -1062,19 +1180,26 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
                }
 #endif
 
-               /* lighting */
-               kernel_branched_path_integrate_lighting(kg, rng, sample, aa_samples,
-                       &sd, throughput, 1.0f, ray_pdf, ray_pdf, state, rng_offset, &L, buffer);
+               if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
+                       /* lighting */
+                       kernel_branched_path_integrate_lighting(kg, rng, sample, aa_samples,
+                               &sd, throughput, 1.0f, ray_pdf, ray_pdf, state, rng_offset, &L, buffer);
 
-               /* continue in case of transparency */
-               throughput *= shader_bsdf_transparency(kg, &sd);
+                       /* continue in case of transparency */
+                       throughput *= shader_bsdf_transparency(kg, &sd);
 
-               if(is_zero(throughput))
-                       break;
+                       if(is_zero(throughput))
+                               break;
+               }
 
                path_state_next(kg, &state, LABEL_TRANSPARENT);
                ray.P = ray_offset(sd.P, -sd.Ng);
                ray.t -= sd.ray_length; /* clipping works through transparent */
+
+#ifdef __VOLUME__
+               /* enter/exit volume */
+               kernel_volume_enter_exit(kg, &sd, &state.volume_shader);
+#endif
        }
 
        float3 L_sum = path_radiance_sum(kg, &L);
@@ -1139,7 +1264,7 @@ ccl_device void kernel_path_trace(KernelGlobals *kg,
        /* integrate */
        float4 L;
 
-       if (ray.t != 0.0f)
+       if(ray.t != 0.0f)
                L = kernel_path_integrate(kg, &rng, sample, ray, buffer);
        else
                L = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
@@ -1171,7 +1296,7 @@ ccl_device void kernel_branched_path_trace(KernelGlobals *kg,
        /* integrate */
        float4 L;
 
-       if (ray.t != 0.0f)
+       if(ray.t != 0.0f)
                L = kernel_branched_path_integrate(kg, &rng, sample, ray, buffer);
        else
                L = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
index 0ded332b3b974eed91d67879932de2849b6fcf1c..2df8f56fd45edcceac8ba12ad4e8242a1263baea 100644 (file)
@@ -24,9 +24,13 @@ typedef struct PathState {
        int glossy_bounce;
        int transmission_bounce;
        int transparent_bounce;
+
+#ifdef __VOLUME__
+       int volume_shader;
+#endif
 } PathState;
 
-ccl_device_inline void path_state_init(PathState *state)
+ccl_device_inline void path_state_init(KernelGlobals *kg, PathState *state)
 {
        state->flag = PATH_RAY_CAMERA|PATH_RAY_SINGULAR|PATH_RAY_MIS_SKIP;
        state->bounce = 0;
@@ -34,6 +38,11 @@ ccl_device_inline void path_state_init(PathState *state)
        state->glossy_bounce = 0;
        state->transmission_bounce = 0;
        state->transparent_bounce = 0;
+
+#ifdef __VOLUME__
+       /* todo: this assumes camera is always in air, need to detect when it isn't */
+       state->volume_shader = kernel_data.background.volume_shader;
+#endif
 }
 
 ccl_device_inline void path_state_next(KernelGlobals *kg, PathState *state, int label)
index 6be8b66c1507b30cbb61dbd90a83a036e88d4d83..a2f4e0a888573a87c375e8cf0998413a8958dbf3 100644 (file)
@@ -388,9 +388,53 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals *kg, ShaderDat
        sd->object = ~0;
 #endif
        sd->prim = ~0;
+#ifdef __UV__
+       sd->u = 0.0f;
+       sd->v = 0.0f;
+#endif
+
+#ifdef __DPDU__
+       /* dPdu/dPdv */
+       sd->dPdu = make_float3(0.0f, 0.0f, 0.0f);
+       sd->dPdv = make_float3(0.0f, 0.0f, 0.0f);
+#endif
+
+#ifdef __RAY_DIFFERENTIALS__
+       /* differentials */
+       sd->dP = ray->dD;
+       differential_incoming(&sd->dI, sd->dP);
+       sd->du.dx = 0.0f;
+       sd->du.dy = 0.0f;
+       sd->dv.dx = 0.0f;
+       sd->dv.dy = 0.0f;
+#endif
+}
+
+/* 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)
+{
+       /* 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);
+#ifdef __OBJECT_MOTION__
+       sd->time = ray->time;
+#endif
+       sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */
+       sd->ray_depth = bounce;
+
+#ifdef __INSTANCING__
+       sd->object = ~0; /* todo: fill this for texture coordinates */
+#endif
+       sd->prim = ~0;
 #ifdef __HAIR__
        sd->segment = ~0;
 #endif
+
 #ifdef __UV__
        sd->u = 0.0f;
        sd->v = 0.0f;
index 4c1548af6e10a3729f76260d4fc0484e51bad461..aaf6ce10feff449f02367a8cfa6105373d0ab618 100644 (file)
@@ -22,7 +22,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *
 
        if(ray->t == 0.0f)
                return false;
-       
+
        Intersection isect;
 #ifdef __HAIR__
        bool result = scene_intersect(kg, ray, PATH_RAY_SHADOW_OPAQUE, &isect, NULL, 0.0f, 0.0f);
@@ -45,6 +45,10 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *
                        float3 Pend = ray->P + ray->D*ray->t;
                        int bounce = state->transparent_bounce;
 
+#ifdef __VOLUME__
+                       int volume_shader = state->volume_shader;
+#endif
+
                        for(;;) {
                                if(bounce >= kernel_data.integrator.transparent_max_bounce) {
                                        return true;
@@ -67,6 +71,13 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *
 #else
                                if(!scene_intersect(kg, ray, PATH_RAY_SHADOW_TRANSPARENT, &isect)) {
 #endif
+
+#ifdef __VOLUME__
+                                       /* attenuation for last line segment towards light */
+                                       if(volume_shader != SHADER_NO_ID)
+                                               throughput *= kernel_volume_get_shadow_attenuation(kg, state, ray, volume_shader);
+#endif
+
                                        *shadow *= throughput;
                                        return false;
                                }
@@ -74,20 +85,48 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *
                                if(!shader_transparent_shadow(kg, &isect))
                                        return true;
 
+#ifdef __VOLUME__
+                               /* attenuation between last surface and next surface */
+                               if(volume_shader != SHADER_NO_ID) {
+                                       Ray segment_ray = *ray;
+                                       segment_ray.t = isect.t;
+                                       throughput *= kernel_volume_get_shadow_attenuation(kg, state, &segment_ray, volume_shader);
+                               }
+#endif
+
+                               /* setup shader data at surface */
                                ShaderData sd;
                                shader_setup_from_ray(kg, &sd, &isect, ray, state->bounce+1);
-                               shader_eval_surface(kg, &sd, 0.0f, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW);
 
-                               throughput *= shader_bsdf_transparency(kg, &sd);
+                               /* attenuation from transparent surface */
+                               if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
+                                       shader_eval_surface(kg, &sd, 0.0f, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW);
+                                       throughput *= shader_bsdf_transparency(kg, &sd);
+                               }
 
+                               /* move ray forward */
                                ray->P = ray_offset(sd.P, -sd.Ng);
                                if(ray->t != FLT_MAX)
                                        ray->D = normalize_len(Pend - ray->P, &ray->t);
 
+#ifdef __VOLUME__
+                               /* exit/enter volume */
+                               if(sd.flag & SD_BACKFACING)
+                                       volume_shader = kernel_data.background.volume_shader;
+                               else
+                                       volume_shader = (sd.flag & SD_HAS_VOLUME)? sd.shader: SHADER_NO_ID;
+#endif
+
                                bounce++;
                        }
                }
        }
+#ifdef __VOLUME__
+       else if(!result && state->volume_shader != SHADER_NO_ID) {
+               /* apply attenuation from current volume shader */
+               *shadow *= kernel_volume_get_shadow_attenuation(kg, state, ray, state->volume_shader);
+       }
+#endif
 #endif
 
        return result;
index f9742ad3b77ff935efbc1c83d3bcd707a1dd0449..707de3160ac874e6fb82d47282e1b50359cf7416 100644 (file)
@@ -58,12 +58,14 @@ CCL_NAMESPACE_BEGIN
 #endif
 #define __SUBSURFACE__
 #define __CMJ__
+#define __VOLUME__
 #endif
 
 #ifdef __KERNEL_CUDA__
 #define __KERNEL_SHADING__
 #define __KERNEL_ADV_SHADING__
 #define __BRANCHED_PATH__
+//#define __VOLUME__
 #endif
 
 #ifdef __KERNEL_OPENCL__
@@ -478,7 +480,8 @@ typedef enum ShaderContext {
        SHADER_CONTEXT_EMISSION = 2,
        SHADER_CONTEXT_SHADOW = 3,
        SHADER_CONTEXT_SSS = 4,
-       SHADER_CONTEXT_NUM = 5
+       SHADER_CONTEXT_VOLUME = 5,
+       SHADER_CONTEXT_NUM = 6
 } ShaderContext;
 
 /* Shader Data
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
new file mode 100644 (file)
index 0000000..3ff95e5
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+CCL_NAMESPACE_BEGIN
+
+/* Volume shader properties
+ *
+ * extinction coefficient = absorption coefficient + scattering coefficient
+ * sigma_t = sigma_a + sigma_s */
+
+ccl_device float3 volume_shader_get_extinction_coefficient(ShaderData *sd)
+{
+       float3 sigma_t = 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))
+                       sigma_t += sc->weight;
+       }
+
+       return sigma_t;
+}
+
+ccl_device float3 volume_shader_get_scattering_coefficient(ShaderData *sd)
+{
+       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;
+       }
+
+       return sigma_s;
+}
+
+ccl_device float3 volume_shader_get_absorption_coefficient(ShaderData *sd)
+{
+       float3 sigma_a = 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;
+       }
+
+       return sigma_a;
+}
+
+/* evaluate shader to get extinction coefficient at P */
+ccl_device float3 volume_extinction_sample(KernelGlobals *kg, ShaderData *sd, int path_flag, ShaderContext ctx, float3 P)
+{
+       sd->P = P;
+
+       shader_eval_volume(kg, sd, 0.0f, path_flag, ctx);
+
+       return volume_shader_get_extinction_coefficient(sd);
+}
+
+ccl_device float3 volume_color_attenuation(float3 sigma, float t)
+{
+       return make_float3(expf(-sigma.x * t), expf(-sigma.y * t), expf(-sigma.z * 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, int shader)
+{
+       ShaderData sd;
+       shader_setup_from_volume(kg, &sd, segment_ray, shader, state->bounce);
+
+       /* do we have a volume shader? */
+       if(!(sd.flag & SD_HAS_VOLUME))
+               return make_float3(1.0f, 1.0f, 1.0f);
+
+       /* single shader evaluation at the start */
+       ShaderContext ctx = SHADER_CONTEXT_SHADOW;
+       int path_flag = PATH_RAY_SHADOW;
+       float3 attenuation;
+
+       //if(sd.flag & SD_HOMOGENEOUS_VOLUME) {
+               /* homogenous volume: assume shader evaluation at the starts gives
+                * the extinction coefficient for the entire line segment */
+
+               /* todo: could this use sigma_t_cache? */
+               float3 sigma_t = volume_extinction_sample(kg, &sd, path_flag, ctx, segment_ray->P);
+
+               attenuation = volume_color_attenuation(sigma_t, segment_ray->t);
+       //}
+
+       return attenuation;
+}
+
+/* Volume Stack */
+
+/* todo: this assumes no overlapping volumes, needs to become a stack */
+ccl_device void kernel_volume_enter_exit(KernelGlobals *kg, ShaderData *sd, int *volume_shader)
+{
+       if(sd->flag & SD_BACKFACING)
+               *volume_shader = kernel_data.background.volume_shader;
+       else
+               *volume_shader = (sd->flag & SD_HAS_VOLUME)? sd->shader: SHADER_NO_ID;
+}
+
+CCL_NAMESPACE_END
+
index dca9c3a91e5d2fc01ba0c0f4308f85722755a0e6..70ac1d3d37c0baf4f7c84eec7118e9d3f0524feb 100644 (file)
@@ -93,6 +93,29 @@ ccl_device_inline ShaderClosure *svm_node_closure_get_bsdf(ShaderData *sd, float
 #endif
 }
 
+ccl_device_inline ShaderClosure *svm_node_closure_get_absorption(ShaderData *sd, float mix_weight)
+{
+#ifdef __MULTI_CLOSURE__
+       ShaderClosure *sc = &sd->closure[sd->num_closure];
+       float3 weight = (make_float3(1.0f, 1.0f, 1.0f) - sc->weight) * mix_weight;
+       float sample_weight = fabsf(average(weight));
+
+       if(sample_weight > CLOSURE_WEIGHT_CUTOFF && sd->num_closure < MAX_CLOSURE) {
+               sc->weight = weight;
+               sc->sample_weight = sample_weight;
+               sd->num_closure++;
+#ifdef __OSL__
+               sc->prim = NULL;
+#endif
+               return sc;
+       }
+
+       return NULL;
+#else
+       return &sd->closure;
+#endif
+}
+
 ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, float randb, int path_flag, int *offset)
 {
        uint type, param1_offset, param2_offset;
@@ -478,7 +501,7 @@ ccl_device void svm_node_closure_volume(KernelGlobals *kg, ShaderData *sd, float
 
        switch(type) {
                case CLOSURE_VOLUME_ABSORPTION_ID: {
-                       ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight * density);
+                       ShaderClosure *sc = svm_node_closure_get_absorption(sd, mix_weight * density);
 
                        if(sc) {
                                sd->flag |= volume_absorption_setup(sc);
index 52e355df3825641e6fe87377fdc8e027b8972230..da790b6fcc9be4ddb65eda934b4c75cf4630e303 100644 (file)
@@ -133,7 +133,6 @@ void Scene::device_update(Device *device_, Progress& progress)
        /* The order of updates is important, because there's dependencies between
         * the different managers, using data computed by previous managers.
         *
-        * - Background generates shader graph compiled by shader manager.
         * - Image manager uploads images used by shaders.
         * - Camera may be used for adapative subdivison.
         * - Displacement shader must have all shader data available.
@@ -142,11 +141,6 @@ void Scene::device_update(Device *device_, Progress& progress)
        
        image_manager->set_pack_images(device->info.pack_images);
 
-       progress.set_status("Updating Background");
-       background->device_update(device, &dscene, this);
-
-       if(progress.get_cancel()) return;
-
        progress.set_status("Updating Shaders");
        shader_manager->device_update(device, &dscene, this, progress);
 
@@ -157,6 +151,11 @@ void Scene::device_update(Device *device_, Progress& progress)
 
        if(progress.get_cancel()) return;
 
+       progress.set_status("Updating Background");
+       background->device_update(device, &dscene, this);
+
+       if(progress.get_cancel()) return;
+
        progress.set_status("Updating Camera");
        camera->device_update(device, &dscene, this);
 
index 675d0ab84894d2a07866a5a911917a07ab2011f4..ab7ee6b4326022a7cdf1ef12ac5ee1bcf7426e77 100644 (file)
@@ -193,6 +193,7 @@ shader_node_categories = [
         NodeItem("ShaderNodeBackground"),
         NodeItem("ShaderNodeAmbientOcclusion"),
         NodeItem("ShaderNodeHoldout"),
+        NodeItem("ShaderNodeVolumeAbsorption"),
         ]),
     ShaderNewNodeCategory("SH_NEW_TEXTURE", "Texture", items=[
         NodeItem("ShaderNodeTexImage"),
index e2c20699faba3aa12b72cad2d6b0a8a514d34860..11c27eb0334a8d4d94c3ce84b6c9bb6512201ec6 100644 (file)
@@ -3499,7 +3499,7 @@ static void registerShaderNodes(void)
        register_node_type_sh_bsdf_hair();
        register_node_type_sh_emission();
        register_node_type_sh_holdout();
-       //register_node_type_sh_volume_absorption();
+       register_node_type_sh_volume_absorption();
        //register_node_type_sh_volume_scatter();
        register_node_type_sh_subsurface_scattering();
        register_node_type_sh_mix_shader();
index da599f8608d6f1bc5977102e0c190041dc8e5d62..f7628375a30c20911fa8c71323a36016c37c93d3 100644 (file)
@@ -921,6 +921,21 @@ static void node_shader_buts_subsurface(uiLayout *layout, bContext *C, PointerRN
        uiItemR(layout, ptr, "falloff", 0, "", ICON_NONE);
 }
 
+
+static void node_shader_buts_volume(uiLayout *layout, bContext *C, PointerRNA *UNUSED(ptr))
+{
+       /* SSS does not work on GPU yet */
+       PointerRNA scene = CTX_data_pointer_get(C, "scene");
+       if (scene.data) {
+               PointerRNA cscene = RNA_pointer_get(&scene, "cycles");
+
+               if (cscene.data && RNA_enum_get(&cscene, "device") == 1)
+                       uiItemL(layout, IFACE_("Volumes not supported on GPU"), ICON_ERROR);
+               else
+                       uiItemL(layout, IFACE_("Volumes are work in progress"), ICON_ERROR);
+       }
+}
+
 static void node_shader_buts_toon(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
 {
        uiItemR(layout, ptr, "component", 0, "", ICON_NONE);
@@ -1064,6 +1079,12 @@ static void node_shader_set_butfunc(bNodeType *ntype)
                case SH_NODE_SUBSURFACE_SCATTERING:
                        ntype->draw_buttons = node_shader_buts_subsurface;
                        break;
+               case SH_NODE_VOLUME_SCATTER:
+                       ntype->draw_buttons = node_shader_buts_volume;
+                       break;
+               case SH_NODE_VOLUME_ABSORPTION:
+                       ntype->draw_buttons = node_shader_buts_volume;
+                       break;
                case SH_NODE_BSDF_TOON:
                        ntype->draw_buttons = node_shader_buts_toon;
                        break;