[Bf-blender-cvs] [a7aee250b82] master: Fix T54420: too much volume render noise with multiple volume objects.

Brecht Van Lommel noreply at git.blender.org
Tue Mar 27 01:51:51 CEST 2018


Commit: a7aee250b82161592bb673adf59508e759a867f4
Author: Brecht Van Lommel
Date:   Mon Mar 26 12:13:36 2018 +0200
Branches: master
https://developer.blender.org/rBa7aee250b82161592bb673adf59508e759a867f4

Fix T54420: too much volume render noise with multiple volume objects.

Random numbers for step offset were correlated, now use stratified samples
which reduces noise as well for some types of volumes, mainly procedural
ones where the step size is bigger than the volume features.

===================================================================

M	intern/cycles/kernel/kernel_path_state.h
M	intern/cycles/kernel/kernel_random.h
M	intern/cycles/kernel/kernel_types.h
M	intern/cycles/kernel/kernel_volume.h

===================================================================

diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h
index 90937dbf86e..479bb22d780 100644
--- a/intern/cycles/kernel/kernel_path_state.h
+++ b/intern/cycles/kernel/kernel_path_state.h
@@ -60,8 +60,6 @@ ccl_device_inline void path_state_init(KernelGlobals *kg,
 	if(kernel_data.integrator.use_volumes) {
 		/* Initialize volume stack with volume we are inside of. */
 		kernel_volume_stack_init(kg, stack_sd, state, ray, state->volume_stack);
-		/* Seed RNG for cases where we can't use stratified samples .*/
-		state->rng_congruential = lcg_init(rng_hash + sample*0x51633e2d);
 	}
 	else {
 		state->volume_stack[0].shader = SHADER_NONE;
diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h
index efb9048beb8..93152e9ff1c 100644
--- a/intern/cycles/kernel/kernel_random.h
+++ b/intern/cycles/kernel/kernel_random.h
@@ -199,6 +199,19 @@ ccl_device_inline void path_state_rng_2D(KernelGlobals *kg,
 	            fx, fy);
 }
 
+ccl_device_inline float path_state_rng_1D_hash(KernelGlobals *kg,
+                                          const ccl_addr_space PathState *state,
+                                          uint hash)
+{
+	/* Use a hash instead of dimension, this is not great but avoids adding
+	 * more dimensions to each bounce which reduces quality of dimensions we
+	 * are already using. */
+	return path_rng_1D(kg,
+	                   cmj_hash_simple(state->rng_hash, hash),
+	                   state->sample, state->num_samples,
+	                   state->rng_offset);
+}
+
 ccl_device_inline float path_branched_rng_1D(
         KernelGlobals *kg,
         uint rng_hash,
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 2945cdb4593..af1ecb05788 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -1107,7 +1107,6 @@ typedef struct PathState {
 #ifdef __VOLUME__
 	int volume_bounce;
 	int volume_bounds_bounce;
-	uint rng_congruential;
 	VolumeStack volume_stack[VOLUME_STACK_SIZE];
 #endif
 } PathState;
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index 88360e5f1ae..86378289b02 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -156,6 +156,24 @@ ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stac
 	return method;
 }
 
+ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg,
+                                               ccl_addr_space PathState *state,
+                                               float t,
+                                               float *step_size,
+                                               float *step_offset)
+{
+	const int max_steps = kernel_data.integrator.volume_max_steps;
+	float step = min(kernel_data.integrator.volume_step_size, t);
+
+	/* compute exact steps in advance for malloc */
+	if(t > max_steps * step) {
+		step = t / (float)max_steps;
+	}
+
+	*step_size = step;
+	*step_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4) * step;
+}
+
 /* Volume Shadows
  *
  * These functions are used to attenuate shadow rays to lights. Both absorption
@@ -188,8 +206,8 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
 
 	/* prepare for stepping */
 	int max_steps = kernel_data.integrator.volume_max_steps;
-	float step = kernel_data.integrator.volume_step_size;
-	float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step;
+	float step_offset, step_size;
+	kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
 
 	/* compute extinction at the start */
 	float t = 0.0f;
@@ -198,14 +216,15 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
 
 	for(int i = 0; i < max_steps; i++) {
 		/* advance to new position */
-		float new_t = min(ray->t, (i+1) * step);
-		float dt = new_t - t;
+		float new_t = min(ray->t, (i+1) * step_size);
 
-		/* use random position inside this segment to sample shader */
-		if(new_t == ray->t)
-			random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
+		/* use random position inside this segment to sample shader, adjust
+		 * for last step that is shorter than other steps. */
+		if(new_t == ray->t) {
+			step_offset *= (new_t - t) / step_size;
+		}
 
-		float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+		float3 new_P = ray->P + ray->D * (t + step_offset);
 		float3 sigma_t;
 
 		/* compute attenuation over segment */
@@ -504,8 +523,8 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
 
 	/* prepare for stepping */
 	int max_steps = kernel_data.integrator.volume_max_steps;
-	float step_size = kernel_data.integrator.volume_step_size;
-	float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step_size;
+	float step_offset, step_size;
+	kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
 
 	/* compute coefficients at the start */
 	float t = 0.0f;
@@ -522,11 +541,13 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
 		float new_t = min(ray->t, (i+1) * step_size);
 		float dt = new_t - t;
 
-		/* use random position inside this segment to sample shader */
-		if(new_t == ray->t)
-			random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
+		/* use random position inside this segment to sample shader,
+		* for last shorter step we remap it to fit within the segment. */
+		if(new_t == ray->t) {
+			step_offset *= (new_t - t) / step_size;
+		}
 
-		float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+		float3 new_P = ray->P + ray->D * (t + step_offset);
 		VolumeShaderCoefficients coeff;
 
 		/* compute segment */
@@ -694,19 +715,12 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 
 	/* prepare for volume stepping */
 	int max_steps;
-	float step_size, random_jitter_offset;
+	float step_size, step_offset;
 
 	if(heterogeneous) {
-		const int global_max_steps = kernel_data.integrator.volume_max_steps;
-		step_size = kernel_data.integrator.volume_step_size;
-		/* compute exact steps in advance for malloc */
-		if(ray->t > global_max_steps*step_size) {
-			max_steps = global_max_steps;
-			step_size = ray->t / (float)max_steps;
-		}
-		else {
-			max_steps = max((int)ceilf(ray->t/step_size), 1);
-		}
+		max_steps = kernel_data.integrator.volume_max_steps;
+		kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
+
 #ifdef __KERNEL_CPU__
 		/* NOTE: For the branched path tracing it's possible to have direct
 		 * and indirect light integration both having volume segments allocated.
@@ -723,19 +737,18 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 		               sizeof(*kg->decoupled_volume_steps));
 		if(kg->decoupled_volume_steps[index] == NULL) {
 			kg->decoupled_volume_steps[index] =
-			        (VolumeStep*)malloc(sizeof(VolumeStep)*global_max_steps);
+			        (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps);
 		}
 		segment->steps = kg->decoupled_volume_steps[index];
 		++kg->decoupled_volume_steps_index;
 #else
 		segment->steps = (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps);
 #endif
-		random_jitter_offset = lcg_step_float(&state->rng_congruential) * step_size;
 	}
 	else {
 		max_steps = 1;
 		step_size = ray->t;
-		random_jitter_offset = 0.0f;
+		step_offset = 0.0f;
 		segment->steps = &segment->stack_step;
 	}
 	
@@ -757,11 +770,13 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 		float new_t = min(ray->t, (i+1) * step_size);
 		float dt = new_t - t;
 
-		/* use random position inside this segment to sample shader */
-		if(heterogeneous && new_t == ray->t)
-			random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
+		/* use random position inside this segment to sample shader,
+		* for last shorter step we remap it to fit within the segment. */
+		if(new_t == ray->t) {
+			step_offset *= (new_t - t) / step_size;
+		}
 
-		float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+		float3 new_P = ray->P + ray->D * (t + step_offset);
 		VolumeShaderCoefficients coeff;
 
 		/* compute segment */
@@ -818,7 +833,7 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 		step->accum_transmittance = accum_transmittance;
 		step->cdf_distance = cdf_distance;
 		step->t = new_t;
-		step->shade_t = t + random_jitter_offset;
+		step->shade_t = t + step_offset;
 
 		/* stop if at the end of the volume */
 		t = new_t;



More information about the Bf-blender-cvs mailing list