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:
-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;