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; + 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; - /* 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); - } + 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; - ray.t = FLT_MAX; + /* 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; + 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 + } +#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; +#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; + 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; - /* 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; + ray->dP = sd->dP; + ray->dD = bsdf_domega_in; #endif - - return true; + +#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; +#endif + + /* enter/exit volume */ + kernel_volume_enter_exit(kg, sd, &state->volume_shader); + return true; + } +#endif + 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; + 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; - /* 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); + 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 + +#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, 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; +#endif + + /* enter/exit volume */ + kernel_volume_enter_exit(kg, &sd, &state.volume_shader); + } +#endif + else { + /* no bsdf or volume? we're done */ + break; } - /* 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; - + /* 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;