[Bf-cycles] Need help with shaders

storm kartochka22 at yandex.ru
Fri Sep 30 17:35:37 CEST 2011


On Tue, 2011-09-27 at 22:23 +0200, Brecht Van Lommel wrote:
> I've added Transparent Volume and Isotropic Volume nodes now. They
> don't actually do anything, it's just the code for getting them from
> blender into the kernel, but might be enough to get you started.
> 
> Brecht.
> 

Great, i need to rewrite that stupid volume_bsdf_diffuse node in proper
way. But cannot resist, I got first Suzanne head that resemble marble so
that is half-working patch.

Please don't even look inside, it just pile of #ifdefs that can eat cats
or worse!

Don't SAVE your scene with that patch, it have nodes ID that surely be
rewriten in next revision and screw you precious data!

I am not kidding, dont even mind compile it and put on something as
graphicall.org, too risky to lose data.

Apply patch, compile as usual, run.

Assign "Volume BSDF"  to Environment volume slot, double check it have F
= 0.

Create some closed mesh, assign Transparent as Surface shader, set Color
0 (object disappear).

Assign "Volume BSDF to mesh Volume shader slot. Assign Color to 1, or
close to 1. Try to change F. For now i use folmula that Density mean
attenuation at volume with M_E/10 thickness. Use 1 for fun real
moon-like shader, use 0.95 for marble-like, etc.




Insert Environment volume slot with "Volume BSDF". Not proper Brecht
"Transparent Volume" or "Isotropic Volume", use my half arsed variant
named "Volume BSDF". Other nodes just dont work. I still not understand
how to assign default volume, so you must do it every time! If you do
not assign it to Environment Volume input you get cool gray blinking
horizontal bands, it is feature, not bug ^^.

My "Volume BSDF" have 3 input, Color, F, g.

Color work almost as usual diffuse-like nodes, it attenuate light after
ray hit particle.

F and g in theory are parameters of "double peak Henyey-Greenstein"
phase function. In short, g = 0 mean diffuse, g = 1 - tiny singular ray,
F control what half sphere will prevail, 0 mean uniform sphere, -1 mean
strong backward reflection, 1 strong forward. For Water partcle it
(F=-0.9, g =0.9).

But for now i just use F as density parameter too , so forget what you
read. It will work after i rewrite node using Brecht template and switch
to single peak Henyey-Greenstein, unfortunately nodes support only 2
additional data streams.

After you play with small meshes, try use hardcore Environment volume
for god rays etc. Beware, it slow as molasses.

Try something like F = 0.01, Color =1 g = 0, or even less F. Use local
light source, background so far, at infinity, it always be absorbed by
shader with any F greater then 0.

Known Bugs:

Cannot assign default Volume slot, every time do it by hand.

Only 1 level of inclusion supported, pure glass bottle will work, glass
bottle on fog street, but air bubble inside glass do not. Need stack of
media_shader, i have problem with proper implementation.

Suzanne have 2 black circles around eyes, some epsilon problem? Looks
cool with marble-like hi density shader. Cannot find solution for weeks.

Water like particles ( clouds) require insane ammount of multiple
scattering to look good, i doubt we can workaround it. Just w8 another
CPU generation, 20+GHZ, 20000 cores, etc.

Awful code style, comments etc, it just dump of WIP just for fun, not
for inclusion in trunk.

Index: intern/cycles/render/scene.h
===================================================================
--- intern/cycles/render/scene.h	(revision 40716)
+++ intern/cycles/render/scene.h	(working copy)
@@ -156,6 +156,7 @@
 	int default_surface;
 	int default_light;
 	int default_background;
+	int default_volume;
 
 	/* device */
 	Device *device;
Index: intern/cycles/render/shader.cpp
===================================================================
--- intern/cycles/render/shader.cpp	(revision 40716)
+++ intern/cycles/render/shader.cpp	(working copy)
@@ -219,6 +219,25 @@
 		scene->default_surface = scene->shaders.size() - 1;
 	}
 
+	/* default volume */
+	{
+		graph = new ShaderGraph();
+
+		closure = graph->add(new DiffuseBsdfVolumeNode());
+		closure->input("Color")->value = make_float3(0.0f, 0.0f, 0.0f);
+		closure->input("F")->value.x = 0.0f;
+		closure->input("g")->value.x = 0.0f;
+		out = graph->output();
+
+		graph->connect(closure->output("Volume"), out->input("Volume"));
+
+		shader = new Shader();
+		shader->name = "default_volume";
+		shader->graph = graph;
+		scene->shaders.push_back(shader);
+		scene->default_volume = scene->shaders.size() - 1;
+	}
+
 	/* default light */
 	{
 		graph = new ShaderGraph();
Index: intern/cycles/render/nodes.cpp
===================================================================
--- intern/cycles/render/nodes.cpp	(revision 40716)
+++ intern/cycles/render/nodes.cpp	(working copy)
@@ -1193,6 +1193,74 @@
 	compiler.add(this, "node_diffuse_bsdf");
 }
 
+/* Volume BSDF Closure */
+
+DiffuseBsdfVolumeNode::DiffuseBsdfVolumeNode()
+: ShaderNode("my_volume")
+{
+	closure = CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID;
+
+        add_input("Color", SHADER_SOCKET_COLOR, make_float3(0.0f, 0.0f,
0.0f));
+
+//	add_input("Density", SHADER_SOCKET_FLOAT, 0.0f);
+	add_input("F", SHADER_SOCKET_FLOAT, 0.0f);
+	add_input("g", SHADER_SOCKET_FLOAT, 0.0f);
+	add_output("Volume", SHADER_SOCKET_CLOSURE);
+}
+
+void DiffuseBsdfVolumeNode::compile(SVMCompiler& compiler)
+{
+	ShaderInput *color_in = input("Color");
+
+	if(color_in->link) {
+		compiler.stack_assign(color_in);
+		compiler.add_node(NODE_CLOSURE_WEIGHT, color_in->stack_offset);
+	}
+	else
+		compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color_in->value);
+
+//	ShaderInput *Density_in = input("Density");
+	ShaderInput *F_in = input("F");
+#if 0
+	if(F_in->link) {
+		compiler.stack_assign(F_in);
+		compiler.add_node(NODE_CLOSURE_WEIGHT, F_in->stack_offset);
+	}
+	else
+		compiler.add_node(NODE_CLOSURE_SET_WEIGHT, F_in->value);
+#endif
+
+
+	ShaderInput *g_in = input("g");
+#if 0
+	if(g_in->link) {
+		compiler.stack_assign(g_in);
+		compiler.add_node(NODE_CLOSURE_WEIGHT, g_in->stack_offset);
+	}
+	else
+		compiler.add_node(NODE_CLOSURE_SET_WEIGHT, g_in->value);
+#endif
+
+
+//	compiler.add_node(NODE_CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN,
CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID);
+	compiler.add_node(NODE_CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN,
+		compiler.encode_uchar4(closure,
+//			(Density_in)? Density_in->stack_offset: SVM_STACK_INVALID,
+			(F_in)? F_in->stack_offset: SVM_STACK_INVALID,
+			(g_in)? g_in->stack_offset: SVM_STACK_INVALID,
+			compiler.closure_mix_weight_offset()),
+//		__float_as_int((Density_in)? Density_in->value.x: 0.0f),
+		__float_as_int((F_in)? F_in->value.x: 0.0f),
+		__float_as_int((g_in)? g_in->value.x: 0.0f));
+	
+}
+
+void DiffuseBsdfVolumeNode::compile(OSLCompiler& compiler)
+{
+	compiler.add(this, "node_diffuse_bsdf_volume");
+}
+
+
 /* Translucent BSDF Closure */
 
 TranslucentBsdfNode::TranslucentBsdfNode()
@@ -1375,7 +1443,7 @@
 
 void TransparentVolumeNode::compile(OSLCompiler& compiler)
 {
-	compiler.add(this, "node_isotropic_volume");
+	compiler.add(this, "node_transparent_volume");
 }
 
 /* Isotropic Volume Closure */
Index: intern/cycles/render/integrator.h
===================================================================
--- intern/cycles/render/integrator.h	(revision 40716)
+++ intern/cycles/render/integrator.h	(working copy)
@@ -42,6 +42,7 @@
 
 	bool no_caustics;
 	float blur_caustics;
+	bool no_volumetric;
 	bool need_update;
 
 	Integrator();
Index: intern/cycles/render/nodes.h
===================================================================
--- intern/cycles/render/nodes.h	(revision 40716)
+++ intern/cycles/render/nodes.h	(working copy)
@@ -194,7 +194,26 @@
 
 	ShaderSocketType from, to;
 };
+#if 0
+class VolumeBsdfDiffuseNode : public ShaderNode {
+public:
+	SHADER_NODE_CLASS(VolumeBsdfDiffuseNode)
 
+	void compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput
*param2);
+
+	ClosureType closure;
+};
+#else
+class DiffuseBsdfVolumeNode : public ShaderNode {
+public:
+	SHADER_NODE_CLASS(DiffuseBsdfVolumeNode)
+
+	void compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput
*param2);
+
+	ClosureType closure;
+};
+#endif
+
 class BsdfNode : public ShaderNode {
 public:
 	SHADER_NODE_CLASS(BsdfNode)
Index: intern/cycles/render/integrator.cpp
===================================================================
--- intern/cycles/render/integrator.cpp	(revision 40716)
+++ intern/cycles/render/integrator.cpp	(working copy)
@@ -41,6 +41,7 @@
 	no_caustics = false;
 	blur_caustics = 0.0f;
 
+	no_volumetric = false;
 	need_update = true;
 }
 
@@ -79,6 +80,8 @@
 	kintegrator->no_caustics = no_caustics;
 	kintegrator->blur_caustics = blur_caustics;
 
+	kintegrator->no_volumetric = no_volumetric;
+
 	/* sobol directions table */
 	int dimensions = PRNG_BASE_NUM + (max_bounce + 2)*PRNG_BOUNCE_NUM;
 	uint *directions =
dscene->sobol_directions.resize(SOBOL_BITS*dimensions);
@@ -109,7 +112,8 @@
 		transparent_probalistic == integrator.transparent_probalistic &&
 		transparent_shadows == integrator.transparent_shadows &&
 		no_caustics == integrator.no_caustics &&
-		blur_caustics == integrator.blur_caustics);
+		blur_caustics == integrator.blur_caustics &&
+		no_volumetric == integrator.no_volumetric);
 }
 
 void Integrator::tag_update(Scene *scene)
Index: intern/cycles/app/cycles_xml.cpp
===================================================================
--- intern/cycles/app/cycles_xml.cpp	(revision 40716)
+++ intern/cycles/app/cycles_xml.cpp	(working copy)
@@ -258,6 +258,7 @@
 	xml_read_int(&integrator->min_bounce, node, "min_bounce");
 	xml_read_int(&integrator->max_bounce, node, "max_bounce");
 	xml_read_bool(&integrator->no_caustics, node, "no_caustics");
+	xml_read_bool(&integrator->no_volumetric, node, "no_volumetric");
 	xml_read_float(&integrator->blur_caustics, node, "blur_caustics");
 }
 
@@ -408,6 +409,16 @@
 		else if(string_iequals(node.name(), "diffuse_bsdf")) {
 			snode = new DiffuseBsdfNode();
 		}
+		else if(string_iequals(node.name(), "diffuse_bsdf_volume")) {
+//			VolumeBsdfDiffuseNode *phase = new VolumeBsdfDiffuseNode();
+			DiffuseBsdfVolume *phase = new DiffuseBsdfVolumeNode();
+
+//			xml_read_float(&phase->F, node, "F");
+//			xml_read_float(&phase->g1, node, "g");
+//			xml_read_float(&phase->g2, node, "g2 asymmetric factor");
+			
+			snode = phase;
+		}
 		else if(string_iequals(node.name(), "translucent_bsdf")) {
 			snode = new TranslucentBsdfNode();
 		}
Index: intern/cycles/kernel/kernel_path.h
===================================================================
--- intern/cycles/kernel/kernel_path.h	(revision 40716)
+++ intern/cycles/kernel/kernel_path.h	(working copy)
@@ -162,6 +162,19 @@
 	return average(throughput);
 }
 
+#ifdef __TRANSPARENT_SHADOWS__
+__device bool shader_transparent_shadow(KernelGlobals *kg, Intersection
*isect)
+{
+	int prim = kernel_tex_fetch(__prim_index, isect->prim);
+	float4 Ns = kernel_tex_fetch(__tri_normal, prim);
+	int shader = __float_as_int(Ns.w);
+
+	/* todo: add shader flag to check this */
+
+	return true;
+}
+#endif
+
 __device_inline bool shadow_blocked(KernelGlobals *kg, PathState
*state, Ray *ray, Intersection *isect, float3 *light_L)
 {
 	if(ray->t == 0.0f)
@@ -227,6 +240,371 @@
 	return result;
 }
 
+
+#define __MY_VOLUME 1
+
+#ifdef __MY_VOLUME
+
+#define my_rand() (path_rng(kg, rng, pass, rng_offset + PRNG_BSDF))
+
+float __device get_sigma_sample(KernelGlobals *kg, ShaderData *sd,
float randv, int path_flag, float3 p)
+{
+        sd->P = p;
+        shader_eval_volume(kg, sd, randv, path_flag);
+//	float v = sd->closure.weight[0];
+	float v = sd->closure.data0;
+#if 1
+	if( v > (1 - 0.01)) v = 1 - 0.01;
+	return -log( 1 - v);
+#else
+	return v;
+#endif
+}
+
+__device int
kenel_volume_is_something_hit_in_interval_woodcock( KernelGlobals *kg,
RNG* rng, int rng_offset, int pass, float randv, ShaderData *sd, Ray
ray, int path_flag, float start, float end, float* new_t)
+{
+	float magic_eps = 0.001;
+	int i = 0, max_iter = 10;
+	float max_prob = 0.75; // google woodcock algorithm, must be
preprocessed, max density in volume, must be as close to density as
possible or we got tiny step and spend milenniums marching single volume
ray segment. 0.75 just for my experiments.
+				
+//	float max_sigma_t = -log(1 - max_prob);
+	float max_sigma_t = max_prob;
+
+	float end_prob = 1 - exp( -max_sigma_t *( end - start));
+	float init_rand = 1 - my_rand() * end_prob;
+//	float init_rand = my_rand();
+
+	float t = -log( init_rand ) / max_sigma_t;
+
+	if ((t + start) >= (end - magic_eps) )
+	{
+		*new_t = end;
+		return 0;
+	}
+	while( get_sigma_sample(kg, sd, my_rand(), path_flag, ray.P + ray.D *
(t + start))/ max_sigma_t < my_rand() && 
+		(t +start )< (end - magic_eps) &&
+		i < max_iter)
+		t += -log( my_rand()) / max_sigma_t;
+
+	if ((t + start)< (end - magic_eps) && i < max_iter)
+	{
+		*new_t = t + start;
+	        sd->P = ray.P + ray.D * (t + start);
+		return 1;
+	}
+
+	*new_t = end;
+	return 0;
+}
+
+// sample volume data at start point, return false if our ray can go
after end point (no hit, media transparence)
+// return true if hit, fill new hit t value inside serment [start,end]
+__device int kenel_volume_is_something_hit_in_interval( KernelGlobals
*kg, float randv, ShaderData *sd, Ray ray, int path_flag, float
*attenuation, float start, float end, float* new_t)
+{
+	float sigma;
+	float magic_eps = 0.00001;
+//	float magic_eps = 0.001;
+	float distance = end-start;
+
+	if(distance < magic_eps)
+	{
+//		*new_t = end; // tiny volume, assume transparent.
+		 return 0;
+	}
+	if( randv * *attenuation < magic_eps) // protect log (0) when sampling
near infinity ray
+	{
+//		*new_t = end; 
+		 return 0;
+	}
+
+        sd->P = ray.P + ray.D * start;
+        shader_eval_volume(kg, sd, randv, path_flag);
+//	float prob = sd->closure.weight[0];
+	float prob = sd->closure.data0;
+	if( prob > (1.0 - magic_eps)) // almost solid object, protect log(0)
+	{
+		*new_t = start;
+		return 1;
+	}
+//	sigma = -log( 1 - prob);
+//	sigma = prob;
+	// treating shader output as attenuation at M_E/10 distance , e =
-log(shader_weight) / sigma,  sigma * e = -log (shader_weight)
+	sigma = -log( 1 - prob) / (M_E / 10);
+	if( sigma < magic_eps ) // protect div by 0, 
+	{
+//		*new_t = end; // very transparent volume
+		 return 0;
+	}
+
+	float sample_distance = -log(randv) / sigma + start;
+
+//	if (sample_distance > distance) return 0; // nothing hit
+//	if ((sample_distance - start )> distance) return 0; // nothing hit
+	if (sample_distance > end) // nothing hit in between [start, end]
+	{
+//		*new_t = sample_distance; 
+		 return 0;
+	}
+	
+	// we hit particle !
+	*new_t = sample_distance;
+        sd->P = ray.P + ray.D * sample_distance;
+	return 1;
+}
+
+// almost same as scene_intersect() but with particle. Cannot check
triangle data, so must used afler real scene_intersect()
+// only homogeneous ifdef branch uncommended, other WIP (woodcock alg)
or just trash.
+// I leave commented code just for fun, see how stupid i was at
beginning. psum, pmul, head, tail, oh my ...
+// but it somewhat works, last commented out loop give first cool
looking volume blob picture posted at blenderatrist
+__device int kernel_march_volume(KernelGlobals *kg, RNG* rng, int
rng_offset, int pass, float randv, float randp, ShaderData *sd, Ray ray,
float distance, float* particle_isect_t, int path_flag)
+{
+//       float step = 2.5;
+//       int max_steps = 50;
+//       float t_limit = min(step * max_steps, distance);
+       Intersection isect;
+
+//	distance  = min(distance, 1000.0f);
+	float distance_eps = 0.999999f; // ensure we never check particle
behind/at surface to stop light leaks, so decrease distance a bit
+	distance *= distance_eps;
+//	distance -= 0.0002;
+//	int num_steps = floor(distance / step);
+//	int head_steps = min(max_steps, num_steps); //
*-----+----+----+----O-----------------*
+							// |    head (optional) |     tail        |
+//	float tail = distance - step * (float)head_steps;
+	int i;
+	float t;
+//	float psum = 0;
+	float pmul = 1;
+
+
+	t = 0.000001;	
+	
+#if 1
+	if ( distance < 0.001) return 0; //escape from bottle when scattering
in solid
+	// check nose
+
+	float lastx = 0;
+	float last_prob = 1; // ?? +inf?
+	t = 0.000001;
+//	float last_t = 0.00001;
+//	float last_t = 0.0;
+	float last_t = 0.001;
+
+#if 0
+	// render close obgects faster, based in pass number. 
+	// to save unbiasness, scale random to stratified sub intervals, cycle
them, so unbiasness only for frames whan another stata cycle begin.
+	if(path_flag & PATH_RAY_CAMERA)
+	{
+		int vstrata_num = 32; 
+		int passes_per_strata_change = 1;
+		int vstrata = vstrata_num - 1 - ((pass % (passes_per_strata_change *
vstrata_num)) % vstrata_num);
+		randv = (randv + vstrata) / vstrata_num;
+	}
+#endif
+
+#if 1
+	// homogeneous media
+	if (kenel_volume_is_something_hit_in_interval( kg, randv, sd, ray,
path_flag, &pmul, t, distance, particle_isect_t))
+		return 1;
+	return 0;
+#if 0
+	float isect_t;
+	if (kenel_volume_is_something_hit_in_interval_woodcock( kg, rng,
rng_offset, pass, randv, sd, ray, path_flag, t, distance, &isect_t))
+	{
+		*particle_isect_t = isect_t;
+		return 1;
+	};
+
+	return 0;
+#endif
+#endif
+#if 0
+	// check head
+	for( i = 0; i < head_steps; i++)
+	{
+		t = step * (float)i + step * randp;
+		if (kenel_volume_is_something_hit_in_interval( kg, randv, sd, ray,
path_flag, &pmul, last_t, t, particle_isect_t))
+			return 1;
+		last_t = t;
+	}
+
+	// check tail
+	if (kenel_volume_is_something_hit_in_interval( kg, randv, sd, ray,
path_flag, &pmul, last_t, distance, particle_isect_t))
+		return 1;
+
+	return 0; 
+#endif
+#else
+	if ( distance < 0.001) return 0; //escape from bottle when scattering
in solid
+#if 0
+	randp = path_rng(kg, rng, pass, rng_offset + PRNG_BSDF);
+	t = distance * randp;
+	sd->P = ray.P + ray.D*t;
+	shader_eval_volume(kg, sd, randv, path_flag);
+	if( sd->closure.weight[0] > randv)
+	{
+        	*particle_isect_t = t;
+        	return 1;
+	}
+	return 0;
+#endif
+	psum=0;
+       float min_step = (step < t_limit)? step:t_limit;
+       for(isect.t = 0 + randv * min_step; isect.t < t_limit; isect.t
+= step)
+       {
+               sd->P = ray.P + ray.D*isect.t;
+               shader_eval_volume(kg, sd, randv, path_flag);
+//               sd->closure.weight[0] = 0.25;
+//               if (sd->closure.weight[0] > randv * min_step) // use
Red component of shader output as volume density
+//               if (sd->closure.weight[0] > randv) // use Red
component of shader output as volume density
+               psum += sd->closure.weight[0];
+		float w = (sd->closure.weight[0] < 1)? sd->closure.weight[0] : 1;
+		if (w < 0) w = 0;
+		pmul *= (1 - w);
+//               if (psum > randv) // use Red component of shader
output as volume density
+               if ((1-pmul) > randv) // use Red component of shader
output as volume density
+               {
+                       *particle_isect_t = isect.t;
+                       return 1;
+               }
+       }
+       return 0;
+#endif
+}
+
+enum vol_result_t {
+	VOLUME_PATH_TERMINATED,
+	VOLUME_PATH_CONTINUE,
+	VOLUME_PATH_PARTICLE_MISS
+};
+
+__device vol_result_t kernel_path_trace_volume(KernelGlobals *kg, RNG
*rng, int rng_offset, int sample, Ray * ray, Intersection *isect, float
isect_t, PathState *state, int media_volume_shader, float3 *throughput,
float3 * L, float * ray_pdf)
+{
+	ShaderData vsd;
+	shader_setup_from_volume(kg, &vsd, ray, media_volume_shader);
+	float randv = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF);
+	float randp = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF);
+	float particle_isect_t;
+	if(  !kernel_data.integrator.no_volumetric && kernel_march_volume(kg,
rng, rng_offset, sample, randv, randp, &vsd, *ray, isect_t,
&particle_isect_t, state->flag))
+	{
+		/* trace volume before environment */
+
+		float rbsdf = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF);
+		shader_eval_volume(kg, &vsd, rbsdf, state->flag);
+#ifdef __EMISSION__
+		/* emission */
+		if(vsd.flag & SD_EMISSION)
+			*L += *throughput*indirect_emission(kg, &vsd, particle_isect_t,
state->flag, *ray_pdf);
+#endif
+
+		/* path termination. this is a strange place to put the termination,
it's
+		   mainly due to the mixed in MIS that we use. gives too many
unneeded
+		   shader evaluations, only need emission if we are going to
terminate */
+		float probability = path_state_terminate_probability(kg, state,
*throughput);
+		float terminate = path_rng(kg, rng, sample, rng_offset +
PRNG_TERMINATE);
+
+		if(terminate >= probability)
+//			break;
+			return VOLUME_PATH_TERMINATED;
+
+		*throughput /= probability;
+
+#ifdef __EMISSION__
+		if(kernel_data.integrator.use_direct_light && !
kernel_data.integrator.no_direct_lighting) {
+			/* sample illumination from lights to find path contribution */
+			if(vsd.flag & SD_BSDF_HAS_EVAL) {
+				float light_t = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT);
+				float light_o = path_rng(kg, rng, sample, rng_offset +
PRNG_LIGHT_F);
+				float light_u = path_rng(kg, rng, sample, rng_offset +
PRNG_LIGHT_U);
+				float light_v = path_rng(kg, rng, sample, rng_offset +
PRNG_LIGHT_V);
+
+				Ray light_ray;
+				float3 light_L;
+
+#ifdef __MULTI_LIGHT__
+				/* index -1 means randomly sample from distribution */
+				int i = (kernel_data.integrator.num_distribution)? -1: 0;
+
+				for(; i < kernel_data.integrator.num_all_lights; i++) {
+#else
+				const int i = -1;
+#endif
+
+					if(direct_emission(kg, &vsd, light_t, i, light_o, light_u,
light_v, &light_ray, &light_L)) {
+						/* trace shadow ray */
+						// trace shadow in volume
+						isect->t = particle_isect_t;
+						if(!shadow_blocked(kg, state, &light_ray, isect, &light_L))
+						{
+							ShaderData tsd;
+							shader_setup_from_volume(kg, &tsd, &light_ray,
media_volume_shader);
+							float trandv = path_rng(kg, rng, sample, rng_offset +
PRNG_BSDF);
+							float trandp = path_rng(kg, rng, sample, rng_offset +
PRNG_BSDF);
+							float tparticle_isect_t;
+							if( ! kernel_march_volume(kg, rng, rng_offset, sample, trandv,
trandp, &tsd, light_ray, light_ray.t, &tparticle_isect_t,
PATH_RAY_SHADOW))
+								*L += *throughput*light_L;
+						}
+					}
+#ifdef __MULTI_LIGHT__
+				}
+#endif
+			}
+		}
+#endif
+		/* sample BSDF */
+		float bsdf_pdf;
+		float3 bsdf_eval;
+		float3 bsdf_omega_in;
+		differential3 bsdf_domega_in;
+		float bsdf_u = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF_U);
+		float bsdf_v = path_rng(kg, rng, sample, rng_offset + PRNG_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_volume_bsdf_sample(kg, &vsd, bsdf_u, bsdf_v,
&bsdf_eval,
+			&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+
+		shader_release(kg, &vsd);
+
+		if(bsdf_pdf == 0.0f || is_zero(bsdf_eval))
+//			break;
+			return VOLUME_PATH_TERMINATED;
+
+		/* modify throughput */
+		*throughput *= bsdf_eval/bsdf_pdf;
+
+		/* set labels */
+#ifdef __EMISSION__
+		*ray_pdf = bsdf_pdf;
+#endif
+
+		/* update path state */
+		path_state_next(state, label);
+
+		/* setup ray */
+//					ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -vsd.Ng:
vsd.Ng);
+		ray->P = vsd.P;
+		ray->D = bsdf_omega_in;
+		ray->t = FLT_MAX;
+#ifdef __RAY_DIFFERENTIALS__
+		ray->dP = vsd.dP;
+		ray->dD = bsdf_domega_in;
+#endif
+		return VOLUME_PATH_CONTINUE;
+	}
+	return VOLUME_PATH_PARTICLE_MISS;
+}
+
+#define swap_media() \
+		if (label & LABEL_TRANSMIT) \
+			if (media_volume_shader == kernel_data.background.shader) \
+				media_volume_shader = sd.shader; \
+			else \
+				media_volume_shader = kernel_data.background.shader;
+
+#endif
+
 __device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int
sample, Ray ray, float3 throughput)
 {
 	/* initialize */
@@ -239,6 +617,10 @@
 	PathState state;
 	int rng_offset = PRNG_BASE_NUM;
 
+	kernel_data.integrator.no_direct_lighting = 0;
+	int media_volume_shader = kernel_data.background.shader; // assume
camera always in air
+
+
 	path_state_init(&state);
 
 	/* path iteration */
@@ -254,17 +636,39 @@
 			}
 			else {
 #ifdef __BACKGROUND__
+#ifndef __MY_VOLUME
 				ShaderData sd;
 				shader_setup_from_background(kg, &sd, &ray);
 				L += throughput*shader_eval_background(kg, &sd, state.flag);
 				shader_release(kg, &sd);
 #else
+				vol_result_t vol_result = kernel_path_trace_volume(kg, rng,
rng_offset, sample, &ray, &isect, FLT_MAX, &state, media_volume_shader,
&throughput, &L, &ray_pdf);
+				if (vol_result == VOLUME_PATH_TERMINATED)
+					break; 
+				else if (vol_result == VOLUME_PATH_CONTINUE)
+					continue;
+				else
+				{
+                                	ShaderData sd;
+					shader_setup_from_background(kg, &sd, &ray);
+					L += throughput*shader_eval_background(kg, &sd, state.flag);
+					shader_release(kg, &sd);
+				}
+#endif
+#else
 				L += throughput*make_float3(0.8f, 0.8f, 0.8f);
 #endif
 			}
 
 			break;
 		}
+#ifdef __MY_VOLUME
+		vol_result_t vol_result = kernel_path_trace_volume(kg, rng,
rng_offset, sample, &ray, &isect, isect.t, &state, media_volume_shader,
&throughput, &L, &ray_pdf);
+		if (vol_result == VOLUME_PATH_TERMINATED)
+			break; 
+		else if (vol_result == VOLUME_PATH_CONTINUE)
+			continue;
+#endif
 
 		/* setup shading */
 		ShaderData sd;
@@ -299,7 +703,7 @@
 		throughput /= probability;
 
 #ifdef __EMISSION__
-		if(kernel_data.integrator.use_direct_light) {
+		if(kernel_data.integrator.use_direct_light && !
kernel_data.integrator.no_direct_lighting) {
 			/* sample illumination from lights to find path contribution */
 			if(sd.flag & SD_BSDF_HAS_EVAL) {
 				float light_t = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT);
@@ -321,7 +725,15 @@
 					if(direct_emission(kg, &sd, i, light_t, light_o, light_u, light_v,
&light_ray, &light_L)) {
 						/* trace shadow ray */
 						if(!shadow_blocked(kg, &state, &light_ray, &isect, &light_L))
-							L += throughput*light_L;
+						{
+							ShaderData tsd;
+							shader_setup_from_volume(kg, &tsd, &light_ray,
media_volume_shader);
+							float trandv = path_rng(kg, rng, sample, rng_offset +
PRNG_BSDF);
+							float trandp = path_rng(kg, rng, sample, rng_offset +
PRNG_BSDF);
+							float tparticle_isect_t;
+							if( ! kernel_march_volume(kg, rng, rng_offset, sample, trandv,
trandp, &tsd, light_ray, light_ray.t, &tparticle_isect_t,
PATH_RAY_SHADOW))
+								L += throughput*light_L;
+						}
 					}
 #ifdef __MULTI_LIGHT__
 				}
@@ -370,6 +782,7 @@
 		ray.dP = sd.dP;
 		ray.dD = bsdf_domega_in;
 #endif
+		swap_media();
 	}
 
 	return make_float4(L.x, L.y, L.z, 1.0f - Ltransparent);
Index: intern/cycles/kernel/kernel_types.h
===================================================================
--- intern/cycles/kernel/kernel_types.h	(revision 40716)
+++ intern/cycles/kernel/kernel_types.h	(working copy)
@@ -368,6 +368,10 @@
 	/* caustics */
 	int no_caustics;
 	float blur_caustics;
+
+	/* volumetric */
+	int no_volumetric;
+	int no_direct_lighting;
 } KernelIntegrator;
 
 typedef struct KernelBVH {
Index: intern/cycles/kernel/kernel_shader.h
===================================================================
--- intern/cycles/kernel/kernel_shader.h	(revision 40716)
+++ intern/cycles/kernel/kernel_shader.h	(working copy)
@@ -272,6 +272,44 @@
 #endif
 }
 
+/* ShaderData setup from ray into background or triangle, assume input
ray.t +INF or intersection point distance */
+
+__device_inline void shader_setup_from_volume(KernelGlobals *kg,
ShaderData *sd, const Ray *ray, int media_shader)
+{
+	/* vectors */
+	sd->P = ray->P;
+	sd->N = -ray->D;  
+	sd->Ng = -ray->D;
+	sd->I = -ray->D;
+	sd->shader = media_shader;
+	sd->flag = kernel_tex_fetch(__shader_flag, sd->shader & SHADER_MASK);
+
+#ifdef __INSTANCING__
+	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
+}
+
 /* BSDF */
 
 #ifdef __MULTI_CLOSURE__
@@ -555,6 +593,15 @@
 
 /* Volume Evaluation */
 
+__device float double_peaked_henyey_greenstein(float cos_theta, float
m_F, float m_g);
+//{
+//        float q1 = (1.0f-sqrt(m_g))/pow(1.0f
+sqrt(m_g)-2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+//        float q2 = (1.0f-sqrt(m_g))/pow(1.0f
+sqrt(m_g)+2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+//        float p = (1.0f+m_F)/2.0f*q1+(1.0f-m_F)/2.0f*q2;
+//        return p;
+//};
+
+
 __device void shader_eval_volume(KernelGlobals *kg, ShaderData *sd,
 	float randb, int path_flag)
 {
@@ -563,10 +610,98 @@
 	OSLShader::eval_volume(kg, sd, randb, path_flag);
 #else
 	svm_eval_nodes(kg, sd, SHADER_TYPE_VOLUME, randb, path_flag);
+	// hack, compute transparent probability of current BSDF (double
peaked Heneye-Gneenstein), overiding actual weight.
+//	const ShaderClosure *sc = &sd->closure;
+//	float sigma_tr = 1 - double_peaked_henyey_greenstein(-1, sc->data0,
sc->data1 );
+//	sd->closure.weight = make_float3( sigma_tr, sigma_tr, sigma_tr);
 #endif
 #endif
 }
 
+__device int shader_volume_bsdf_sample(KernelGlobals *kg, const
ShaderData *sd,
+	float randu, float randv, float3 *eval,
+	float3 *omega_in, differential3 *domega_in, float *pdf)
+{
+#ifdef __MULTI_CLOSURE__
+	int sampled = 0;
+
+	if(sd->num_closure > 1) {
+		/* pick a BSDF closure based on sample weights */
+		float sum = 0.0f;
+
+		for(sampled = 0; sampled < sd->num_closure; sampled++) {
+			const ShaderClosure *sc = &sd->closure[sampled];
+			
+			if(CLOSURE_IS_BSDF(sc->type))
+				sum += sc->sample_weight;
+		}
+
+		float r = sd->randb_closure*sum;
+		sum = 0.0f;
+
+		for(sampled = 0; sampled < sd->num_closure; sampled++) {
+			const ShaderClosure *sc = &sd->closure[sampled];
+			
+			if(CLOSURE_IS_BSDF(sc->type)) {
+				sum += sd->closure[sampled].sample_weight;
+
+				if(r <= sum)
+					break;
+			}
+		}
+
+		if(sampled == sd->num_closure) {
+			*pdf = 0.0f;
+			return LABEL_NONE;
+		}
+	}
+
+	const ShaderClosure *sc = &sd->closure[sampled];
+	int label;
+
+	*pdf = 0.0f;
+#ifdef __OSL__
+	label = OSLShader::bsdf_sample(sd, sc, randu, randv, *eval, *omega_in,
*domega_in, *pdf);
+#else
+	label = svm_bsdf_sample(sd, sc, randu, randv, eval, omega_in,
domega_in, pdf);
+#endif
+
+	*eval *= sc->weight;
+
+	if(sd->num_closure > 1 && *pdf != 0.0f) {
+		float sweight = sc->sample_weight;
+		*eval = _shader_bsdf_multi_eval(sd, *omega_in, pdf, sampled, *eval,
*pdf*sweight, sweight);
+	}
+
+	return label;
+#else
+	/* sample the single closure that we picked */
+	*pdf = 0.0f;
+	int label = svm_volume_bsdf_sample(sd, &sd->closure, randu, randv,
eval, omega_in, domega_in, pdf);
+	*eval *= sd->closure.weight;
+	return label;
+#endif
+}
+#if 0
+__device int shader_volume_bsdf_sample(KernelGlobals *kg, const
ShaderData *sd,
+	float randu, float randv, float3 *eval,
+	float3 *omega_in, differential3 *domega_in, float *pdf)
+{
+	int label;
+
+	*pdf = 0.0f;
+
+#ifdef WITH_OSL
+	if(kg->osl.use)
+		label = OSLShader::bsdf_sample(sd, randu, randv, *eval, *omega_in,
*domega_in, *pdf);
+	else
+#endif
+		label = svm_volume_bsdf_sample(sd, randu, randv, eval, omega_in,
domega_in, pdf);
+
+	return label;
+}
+#endif
+
 /* Displacement Evaluation */
 
 __device void shader_eval_displacement(KernelGlobals *kg, ShaderData
*sd)
Index: intern/cycles/kernel/osl/osl_closures.cpp
===================================================================
--- intern/cycles/kernel/osl/osl_closures.cpp	(revision 40716)
+++ intern/cycles/kernel/osl/osl_closures.cpp	(working copy)
@@ -69,6 +69,7 @@
 void OSLShader::register_closures(OSL::ShadingSystem *ss)
 {
 	register_closure(ss, "diffuse", OSL_CLOSURE_BSDF_DIFFUSE_ID,
bsdf_diffuse_params, bsdf_diffuse_prepare);
+	register_closure(ss, "volume", OSL_CLOSURE_VOLUME_BSDF_DIFFUSE_ID,
volume_bsdf_diffuse_params, volume_bsdf_diffuse_prepare);
 	register_closure(ss, "translucent", OSL_CLOSURE_BSDF_TRANSLUCENT_ID,
bsdf_translucent_params, bsdf_translucent_prepare);
 	register_closure(ss, "reflection", OSL_CLOSURE_BSDF_REFLECTION_ID,
bsdf_reflection_params, bsdf_reflection_prepare);
 	register_closure(ss, "refraction", OSL_CLOSURE_BSDF_REFRACTION_ID,
bsdf_refraction_params, bsdf_refraction_prepare);
Index: intern/cycles/kernel/svm/svm_closure.h
===================================================================
--- intern/cycles/kernel/svm/svm_closure.h	(revision 40716)
+++ intern/cycles/kernel/svm/svm_closure.h	(working copy)
@@ -192,6 +192,53 @@
 	}
 }
 
+
+__device void
svm_node_closure_bsdf_double_peaked_henyey_greenstein(KernelGlobals *kg,
ShaderData *sd, float *stack, uint4 node, float randb, int path_flag)
+{
+	uint type, param1_offset, param2_offset;
+
+#ifdef __MULTI_CLOSURE__
+	uint mix_weight_offset;
+	decode_node_uchar4(node.y, &type, &param1_offset, &param2_offset,
&mix_weight_offset);
+	float mix_weight = (stack_valid(mix_weight_offset)?
stack_load_float(stack, mix_weight_offset): 1.0f);
+
+	if(mix_weight == 0.0f)
+		return;
+#else
+	decode_node_uchar4(node.y, &type, &param1_offset, &param2_offset,
NULL);
+	float mix_weight = 1.0f;
+#endif
+
+	float param1 = (stack_valid(param1_offset))? stack_load_float(stack,
param1_offset): __int_as_float(node.z);
+	float param2 = (stack_valid(param2_offset))? stack_load_float(stack,
param2_offset): __int_as_float(node.w);
+
+	switch(type) {
+		case CLOSURE_BSDF_DIFFUSE_ID: {
+			ShaderClosure *sc = svm_node_closure_get(sd);
+			svm_node_closure_set_mix_weight(sc, mix_weight);
+			bsdf_diffuse_setup(sd, sc);
+			break;
+		}
+		case CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID: {
+			ShaderClosure *sc = svm_node_closure_get(sd);
+			svm_node_closure_set_mix_weight(sc, mix_weight);
+
+//			clamp(param1, 0.0f, 1.0f);
+			float F = param1; // asymmetric factor
+			float g = param2; // 
+//			bsdf_translucent_setup(sd, sc);
+			sc->data0 = F;
+			sc->data1 = g;
+//			printf("F= %g g= %g", F, g);
+			sc->type = CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID;
+			sd->flag |= SD_BSDF|SD_BSDF_HAS_EVAL;
+			break;
+		}
+		default:
+			break;
+	}
+}
+
 __device void svm_node_closure_volume(KernelGlobals *kg, ShaderData
*sd, float *stack, uint4 node, int path_flag)
 {
 	uint type, param1_offset, param2_offset;
Index: intern/cycles/kernel/svm/volume.h
===================================================================
--- intern/cycles/kernel/svm/volume.h	(revision 40716)
+++ intern/cycles/kernel/svm/volume.h	(working copy)
@@ -16,6 +16,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+#ifndef __VOLUME_H
+#define __VOLUME_H
+
 CCL_NAMESPACE_BEGIN
 
 /* note: the interfaces here are just as an example, need to figure
@@ -51,6 +54,13 @@
 
 /* VOLUME CLOSURE */
 
+typedef struct VolumeBsdfClosure {
+	//float3 m_N;
+	float F;
+	float g1;
+	float g2;
+} VolumeBsdfClosure;
+
 __device float3 volume_eval_phase(const ShaderData *sd, const
ShaderClosure *sc, const float3 omega_in, const float3 omega_out)
 {
 	float3 eval;
@@ -70,5 +80,94 @@
 	return eval;
 }
 
+// given cosinus between rays, return probability density that photon
bounce to that direction
+// F and g parameters controling how far it difference from uniform
sphere. g=0 uniform diffusion-like, g = 1 - very close to sharp single
ray,
+// F = 0.5 - uniform, F = 0 - most backward reflect, F = 1 most transit
+// (F=0.5, g=0) - sphere, (F=0.5, g=1) -very polished half transparent
mirror, (F=0 g=1) perfect mirror, (F=1 g=1) perfect transparent glass.
+
+__device float double_peaked_henyey_greenstein(float cos_theta, float
m_F, float m_g)
+{
+#if 0
+	float q1 = (1.0f-sqrt(m_g))/pow(1.0f
+sqrt(m_g)-2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+	float q2 = (1.0f-sqrt(m_g))/pow(1.0f
+sqrt(m_g)+2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+	float p = (1.0f+m_F)/2.0f*q1+(1.0f-m_F)/2.0f*q2;
+#else
+	float q1 = (1.0f-m_g*m_g)/pow(1.0f
+m_g*m_g-2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+	float q2 = (1.0f-m_g*m_g)/pow(1.0f+m_g*m_g
+2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+	float p = (1.0f+m_F)/2.0f*q1+(1.0f-m_F)/2.0f*q2;
+#endif
+	return p;
+};
+
+// just return bsdf at input vector
+__device float3 bsdf_double_peaked_henyey_greenstein_eval(const
ShaderData *sd, const ShaderClosure *sc, const float3 I, float3
omega_in, float *pdf)
+{
+	float m_F = sc->data0;
+	float m_g = sc->data1;
+
+	float cos_theta = dot( sd->I, omega_in);
+	*pdf = double_peaked_henyey_greenstein( cos_theta, m_F, m_g);
+	return make_float3( *pdf, *pdf, *pdf);
+}
+
+// calculate sample vector, return it and bsdf
+__device int bsdf_double_peaked_henyey_greenstein_sample(const
ShaderData *sd, const ShaderClosure *sc, float randu, float randv,
float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3
*domega_in_dy, float *pdf)
+{
+	float m_F = sc->data0;
+	float m_g = sc->data1;
+	float3 m_N = sd->N;
+
+//	sample_cos_hemisphere(m_N, randu, randv, omega_in, pdf);
+
+	*omega_in = sample_uniform_sphere( randu, randv);
+
+	float cos_theta = dot( sd->I, *omega_in);
+#if 0
+	cos_theta = 1;
+	m_F = 0;
+	m_g = 0;
+#endif
+//	float q1 = (1.0f-sqrt(m_g))/pow(1.0f
+sqrt(m_g)-2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+//	float q2 = (1.0f-sqrt(m_g))/pow(1.0f
+sqrt(m_g)+2.0f*m_g*cos_theta,1.5f)/4.0f/M_PI_F;
+//	float p = (1.0f+m_F)/2.0f*q1+(1.0f-m_F)/2.0f*q2;
+//	p = M_1_PI_F;
+//	*pdf = p;
+	*pdf = double_peaked_henyey_greenstein(cos_theta, m_F, m_g);
+
+//	*pdf = cos_theta;
+	*eval = make_float3(*pdf, *pdf, *pdf);
+	*eval *= sc->weight[1]; // absorb?
+//	*eval = make_float3(sc->data1, *pdf, *pdf);
+#ifdef __RAY_DIFFERENTIALS__
+		// TODO: find a better approximation for the diffuse bounce
+		*domega_in_dx = (2 * dot(m_N, sd->dI.dx)) * m_N - sd->dI.dx;
+		*domega_in_dy = (2 * dot(m_N, sd->dI.dy)) * m_N - sd->dI.dy;
+		*domega_in_dx *= 125.0f;
+		*domega_in_dy *= 125.0f;
+#endif
+	return LABEL_REFLECT|LABEL_DIFFUSE;
+}
+
+__device int svm_volume_bsdf_sample(const ShaderData *sd, const
ShaderClosure *sc, float randu, float randv, float3 *eval, float3
*omega_in, differential3 *domega_in, float *pdf)
+{
+	int label;
+
+	switch(sc->type) {
+//		case CLOSURE_BSDF_DIFFUSE_ID:
+//			label = bsdf_diffuse_sample(sd, randu, randv, eval, omega_in,
&domega_in->dx, &domega_in->dy, pdf);
+//			break;
+		case CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID:
+			label = bsdf_double_peaked_henyey_greenstein_sample(sd, sc, randu,
randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
+			break;
+		default:
+			label = LABEL_NONE;
+			break;
+	}
+
+//	*eval *= sd->svm_closure_weight;
+
+	return label;
+}
 CCL_NAMESPACE_END
 
+#endif
Index: intern/cycles/kernel/svm/svm_types.h
===================================================================
--- intern/cycles/kernel/svm/svm_types.h	(revision 40716)
+++ intern/cycles/kernel/svm/svm_types.h	(working copy)
@@ -84,7 +84,8 @@
 	NODE_TEX_ENVIRONMENT = 4600,
 	NODE_CLOSURE_HOLDOUT = 4700,
 	NODE_BLEND_WEIGHT = 4800,
-	NODE_CLOSURE_VOLUME = 4900
+	NODE_CLOSURE_VOLUME = 4900,
+	NODE_CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN = 5000
 } NodeType;
 
 typedef enum NodeAttributeType {
@@ -279,6 +280,7 @@
 	CLOSURE_BSDF_WARD_ID,
 	CLOSURE_BSDF_ASHIKHMIN_VELVET_ID,
 	CLOSURE_BSDF_WESTIN_BACKSCATTER_ID,
+	CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID,
 	CLOSURE_BSDF_WESTIN_SHEEN_ID,
 
 	CLOSURE_BSSRDF_CUBIC_ID,
Index: intern/cycles/kernel/svm/svm_bsdf.h
===================================================================
--- intern/cycles/kernel/svm/svm_bsdf.h	(revision 40716)
+++ intern/cycles/kernel/svm/svm_bsdf.h	(working copy)
@@ -38,6 +38,9 @@
 			label = bsdf_diffuse_sample(sd, sc, randu, randv, eval, omega_in,
&domega_in->dx, &domega_in->dy, pdf);
 			break;
 #ifdef __SVM__
+		case CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID:
+			label = bsdf_double_peaked_henyey_greenstein_sample (sd, sc, randu,
randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
+			break;
 		case CLOSURE_BSDF_TRANSLUCENT_ID:
 			label = bsdf_translucent_sample(sd, sc, randu, randv, eval,
omega_in, &domega_in->dx, &domega_in->dy, pdf);
 			break;
@@ -90,6 +93,9 @@
 			case CLOSURE_BSDF_DIFFUSE_ID:
 				eval = bsdf_diffuse_eval_reflect(sd, sc, sd->I, omega_in, pdf);
 				break;
+			case CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID:
+				eval = bsdf_double_peaked_henyey_greenstein_eval(sd, sc, sd->I,
omega_in, pdf);
+				break;
 #ifdef __SVM__
 			case CLOSURE_BSDF_TRANSLUCENT_ID:
 				eval = bsdf_translucent_eval_reflect(sd, sc, sd->I, omega_in, pdf);
@@ -136,6 +142,9 @@
 			case CLOSURE_BSDF_DIFFUSE_ID:
 				eval = bsdf_diffuse_eval_transmit(sd, sc, sd->I, omega_in, pdf);
 				break;
+			case CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN_ID:
+				eval = bsdf_double_peaked_henyey_greenstein_eval(sd, sc, sd->I,
omega_in, pdf);
+				break;
 #ifdef __SVM__
 			case CLOSURE_BSDF_TRANSLUCENT_ID:
 				eval = bsdf_translucent_eval_transmit(sd, sc, sd->I, omega_in,
pdf);
Index: intern/cycles/kernel/svm/svm.h
===================================================================
--- intern/cycles/kernel/svm/svm.h	(revision 40716)
+++ intern/cycles/kernel/svm/svm.h	(working copy)
@@ -143,6 +143,7 @@
 #include "svm_value.h"
 #include "svm_voronoi.h"
 #include "svm_wood.h"
+#include "volume.h"
 
 CCL_NAMESPACE_BEGIN
 
@@ -175,6 +176,9 @@
 			case NODE_CLOSURE_BSDF:
 				svm_node_closure_bsdf(kg, sd, stack, node, randb, path_flag);
 				break;
+			case NODE_CLOSURE_BSDF_DOUBLE_PEAKED_HENYEY_GREENSTEIN:
+				svm_node_closure_bsdf_double_peaked_henyey_greenstein(kg, sd,
stack, node, randb, path_flag);
+				break;
 			case NODE_CLOSURE_EMISSION:
 				svm_node_closure_emission(sd, stack, node);
 				break;
Index: intern/cycles/kernel/kernel_emission.h
===================================================================
--- intern/cycles/kernel/kernel_emission.h	(revision 40716)
+++ intern/cycles/kernel/kernel_emission.h	(working copy)
@@ -127,7 +127,7 @@
 	/* evaluate emissive closure */
 	float3 L = shader_emissive_eval(kg, sd);
 
-	if(!(path_flag & PATH_RAY_SINGULAR) && (sd->flag &
SD_SAMPLE_AS_LIGHT)) {
+	if(!(path_flag & PATH_RAY_SINGULAR) && (sd->flag & SD_SAMPLE_AS_LIGHT)
&& ! kernel_data.integrator.no_direct_lighting) {
 		/* multiple importance sampling */
 		float pdf = triangle_light_pdf(kg, sd->Ng, sd->I, t);
 		float mis_weight = power_heuristic(bsdf_pdf, pdf);
Index: intern/cycles/blender/blender_shader.cpp
===================================================================
--- intern/cycles/blender/blender_shader.cpp	(revision 40716)
+++ intern/cycles/blender/blender_shader.cpp	(working copy)
@@ -224,6 +224,10 @@
 			node = new DiffuseBsdfNode();
 			break;
 		}
+		case BL::ShaderNode::type_VOLUME_BSDF_DIFFUSE: {
+			node = new DiffuseBsdfVolumeNode();
+			break;
+		}
 		case BL::ShaderNode::type_BSDF_GLOSSY: {
 			BL::ShaderNodeBsdfGlossy b_glossy_node(b_node);
 			GlossyBsdfNode *glossy = new GlossyBsdfNode();
Index: intern/cycles/blender/addon/properties.py
===================================================================
--- intern/cycles/blender/addon/properties.py	(revision 40716)
+++ intern/cycles/blender/addon/properties.py	(working copy)
@@ -49,6 +49,9 @@
         cls.blur_caustics = FloatProperty(name="Blur Caustics",
description="Blur caustics to reduce noise",
             default=0.0, min=0.0, max=1.0)
 
+        cls.no_volumetric = BoolProperty(name="No Volumetric",
description="Turn off volumetric rendering",
+            default=False)
+
         cls.min_bounces = IntProperty(name="Min Bounces",
description="Minimum number of bounces, setting this lower than the
maximum enables probalistic path termination (faster but noisier)",
             default=3, min=0, max=1024)
         cls.max_bounces = IntProperty(name="Max Bounces",
description="Total maximum number of bounces",
Index: intern/cycles/blender/addon/ui.py
===================================================================
--- intern/cycles/blender/addon/ui.py	(revision 40716)
+++ intern/cycles/blender/addon/ui.py	(working copy)
@@ -69,6 +69,8 @@
         sub.prop(cscene, "transparent_min_bounces", text="Min")
         sub.prop(cscene, "no_caustics")
 
+        sub.prop(cscene, "no_volumetric")
+
         col = split.column()
 
         sub = col.column(align=True)
Index: intern/cycles/blender/addon/presets.py
===================================================================
--- intern/cycles/blender/addon/presets.py	(revision 40716)
+++ intern/cycles/blender/addon/presets.py	(working copy)
@@ -33,6 +33,7 @@
         "cycles.max_bounces",
         "cycles.min_bounces",
         "cycles.no_caustics",
+        "cycles.no_volumetric",
         "cycles.diffuse_bounces",
         "cycles.glossy_bounces",
         "cycles.transmission_bounces",
Index: intern/cycles/blender/blender_sync.cpp
===================================================================
--- intern/cycles/blender/blender_sync.cpp	(revision 40716)
+++ intern/cycles/blender/blender_sync.cpp	(working copy)
@@ -150,6 +150,8 @@
 	integrator->no_caustics = get_boolean(cscene, "no_caustics");
 	integrator->blur_caustics = get_float(cscene, "blur_caustics");
 
+	integrator->no_volumetric = get_boolean(cscene, "no_volumetric");
+
 	if(integrator->modified(previntegrator))
 		integrator->tag_update(scene);
 }
Index:
release/scripts/presets/cycles/integrator/limited_global_illumination.py
===================================================================
---
release/scripts/presets/cycles/integrator/limited_global_illumination.py
(revision 40716)
+++
release/scripts/presets/cycles/integrator/limited_global_illumination.py
(working copy)
@@ -4,6 +4,7 @@
 cycles.max_bounces = 8
 cycles.min_bounces = 3
 cycles.no_caustics = True
+cycles.no_volumetric = True
 cycles.diffuse_bounces = 1
 cycles.glossy_bounces = 4
 cycles.transmission_bounces = 8
Index: release/scripts/presets/cycles/integrator/direct_light.py
===================================================================
--- release/scripts/presets/cycles/integrator/direct_light.py	(revision
40716)
+++ release/scripts/presets/cycles/integrator/direct_light.py	(working
copy)
@@ -4,6 +4,7 @@
 cycles.max_bounces = 8
 cycles.min_bounces = 8
 cycles.no_caustics = True
+cycles.no_volumetric = True
 cycles.diffuse_bounces = 0
 cycles.glossy_bounces = 1
 cycles.transmission_bounces = 2
Index:
release/scripts/presets/cycles/integrator/full_global_illumination.py
===================================================================
---
release/scripts/presets/cycles/integrator/full_global_illumination.py
(revision 40716)
+++
release/scripts/presets/cycles/integrator/full_global_illumination.py
(working copy)
@@ -4,6 +4,7 @@
 cycles.max_bounces = 1024
 cycles.min_bounces = 3
 cycles.no_caustics = False
+cycles.no_volumetric = False
 cycles.diffuse_bounces = 1024
 cycles.glossy_bounces = 1024
 cycles.transmission_bounces = 1024
Index: source/blender/blenkernel/BKE_node.h
===================================================================
--- source/blender/blenkernel/BKE_node.h	(revision 40716)
+++ source/blender/blenkernel/BKE_node.h	(working copy)
@@ -490,6 +490,7 @@
 #define SH_NODE_BLEND_WEIGHT			160
 #define SH_NODE_VOLUME_TRANSPARENT		161
 #define SH_NODE_VOLUME_ISOTROPIC		162
+#define SH_NODE_VOLUME_BSDF_DIFFUSE			163
 
 /* custom defines options for Material node */
 #define SH_NODE_MAT_DIFF   1
Index: source/blender/blenkernel/intern/node.c
===================================================================
--- source/blender/blenkernel/intern/node.c	(revision 40716)
+++ source/blender/blenkernel/intern/node.c	(working copy)
@@ -1891,6 +1891,7 @@
 	register_node_type_sh_holdout(ntypelist);
 	register_node_type_sh_mix_shader(ntypelist);
 	register_node_type_sh_add_shader(ntypelist);
+	register_node_type_sh_volume_bsdf_diffuse(ntypelist);
 
 	register_node_type_sh_output_lamp(ntypelist);
 	register_node_type_sh_output_material(ntypelist);
Index: source/blender/makesrna/intern/rna_nodetree_types.h
===================================================================
--- source/blender/makesrna/intern/rna_nodetree_types.h	(revision 40716)
+++ source/blender/makesrna/intern/rna_nodetree_types.h	(working copy)
@@ -69,6 +69,7 @@
 DefNode( ShaderNode,     SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout,
"Holdout", "")
 DefNode( ShaderNode,     SH_NODE_BSDF_ANISOTROPIC, 0,
"BSDF_ANISOTROPIC", BsdfAnisotropic, "Bsdf Anisotropic", "")
 DefNode( ShaderNode,     SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE",
BsdfDiffuse, "Diffuse Bsdf", "")
+DefNode( ShaderNode,     SH_NODE_VOLUME_BSDF_DIFFUSE, 0,
"VOLUME_BSDF_DIFFUSE", VolumeBsdfDiffuse, "Volume Phase Bsdf", "")
 DefNode( ShaderNode,     SH_NODE_BSDF_GLOSSY, def_glossy,
"BSDF_GLOSSY", BsdfGlossy, "Glossy Bsdf", "")
 DefNode( ShaderNode,     SH_NODE_BSDF_GLASS, def_glossy, "BSDF_GLASS",
BsdfGlass, "Glass Bsdf", "")
 DefNode( ShaderNode,     SH_NODE_BSDF_TRANSLUCENT, 0,
"BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent Bsdf", "")
Index: source/blender/nodes/NOD_shader.h
===================================================================
--- source/blender/nodes/NOD_shader.h	(revision 40716)
+++ source/blender/nodes/NOD_shader.h	(working copy)
@@ -67,6 +67,7 @@
 
 void register_node_type_sh_background(ListBase *lb);
 void register_node_type_sh_bsdf_diffuse(ListBase *lb);
+void register_node_type_sh_volume_bsdf_diffuse(ListBase *lb);
 void register_node_type_sh_bsdf_glossy(ListBase *lb);
 void register_node_type_sh_bsdf_glass(ListBase *lb);
 void register_node_type_sh_bsdf_anisotropic(ListBase *lb);
Index: source/blender/nodes/CMakeLists.txt
===================================================================
--- source/blender/nodes/CMakeLists.txt	(revision 40716)
+++ source/blender/nodes/CMakeLists.txt	(working copy)
@@ -128,6 +128,7 @@
 	shader/nodes/node_shader_background.c
 	shader/nodes/node_shader_bsdf_anisotropic.c
 	shader/nodes/node_shader_bsdf_diffuse.c
+	shader/nodes/node_shader_volume_bsdf_diffuse.c
 	shader/nodes/node_shader_bsdf_glossy.c
 	shader/nodes/node_shader_bsdf_glass.c
 	shader/nodes/node_shader_bsdf_translucent.c
-------------- next part --------------
A non-text attachment was scrubbed...
Name: blender_cycles_volume_40716_v1.patch
Type: text/x-patch
Size: 46928 bytes
Desc: not available
Url : http://lists.blender.org/pipermail/bf-cycles/attachments/20110930/eddd1f5d/attachment-0001.bin 


More information about the Bf-cycles mailing list