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-28 19:56:19 +0400
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2013-12-28 19:57:10 +0400
commite369a5c48529864118d49222dde3d530d58ebeae (patch)
tree8d803e5318c57b7bfbdd321da1393e4d71878d35
parent133f770ab3e7a6db49562cb73cb7aa5f75cf13f8 (diff)
Cycles Volume Render: support for rendering of homogeneous volume with absorption.
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.
-rw-r--r--intern/cycles/blender/addon/properties.py6
-rw-r--r--intern/cycles/blender/addon/ui.py12
-rw-r--r--intern/cycles/blender/blender_shader.cpp3
-rw-r--r--intern/cycles/kernel/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/kernel_path.h349
-rw-r--r--intern/cycles/kernel/kernel_path_state.h11
-rw-r--r--intern/cycles/kernel/kernel_shader.h44
-rw-r--r--intern/cycles/kernel/kernel_shadow.h45
-rw-r--r--intern/cycles/kernel/kernel_types.h5
-rw-r--r--intern/cycles/kernel/kernel_volume.h124
-rw-r--r--intern/cycles/kernel/svm/svm_closure.h25
-rw-r--r--intern/cycles/render/scene.cpp11
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/intern/node.c2
-rw-r--r--source/blender/editors/space_node/drawnode.c21
15 files changed, 529 insertions, 131 deletions
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index f5c052e7f0c..e446e4711e4 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -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):
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 83483cbcae9..02d1dfb1542 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -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
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 0c3a32af20d..6ec72c33466 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -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) {
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 63637991c29..860909818b3 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -51,6 +51,7 @@ set(SRC_HEADERS
kernel_textures.h
kernel_triangle.h
kernel_types.h
+ kernel_volume.h
)
set(SRC_CLOSURE_HEADERS
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index 903efc9d6f5..d318a85fcb7 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -40,6 +40,10 @@
#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);
diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h
index 0ded332b3b9..2df8f56fd45 100644
--- a/intern/cycles/kernel/kernel_path_state.h
+++ b/intern/cycles/kernel/kernel_path_state.h
@@ -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)
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 6be8b66c150..a2f4e0a8885 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -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;
diff --git a/intern/cycles/kernel/kernel_shadow.h b/intern/cycles/kernel/kernel_shadow.h
index 4c1548af6e1..aaf6ce10fef 100644
--- a/intern/cycles/kernel/kernel_shadow.h
+++ b/intern/cycles/kernel/kernel_shadow.h
@@ -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;
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index f9742ad3b77..707de3160ac 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -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
index 00000000000..3ff95e57733
--- /dev/null
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -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
+
diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h
index dca9c3a91e5..70ac1d3d37c 100644
--- a/intern/cycles/kernel/svm/svm_closure.h
+++ b/intern/cycles/kernel/svm/svm_closure.h
@@ -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);
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index 52e355df382..da790b6fcc9 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -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);
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 675d0ab8489..ab7ee6b4326 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -193,6 +193,7 @@ shader_node_categories = [
NodeItem("ShaderNodeBackground"),
NodeItem("ShaderNodeAmbientOcclusion"),
NodeItem("ShaderNodeHoldout"),
+ NodeItem("ShaderNodeVolumeAbsorption"),
]),
ShaderNewNodeCategory("SH_NEW_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexImage"),
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index e2c20699fab..11c27eb0334 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -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();
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index da599f8608d..f7628375a30 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -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;