[Bf-blender-cvs] [c6bd7a974b3] gsoc-2018-many-light-sampling: Cycles: Updated the split heuristic

Erik Englesson noreply at git.blender.org
Fri Jul 13 13:57:43 CEST 2018


Commit: c6bd7a974b34d47f01f02666d8cdd5057bc77eba
Author: Erik Englesson
Date:   Tue Jul 10 11:13:14 2018 +0200
Branches: gsoc-2018-many-light-sampling
https://developer.blender.org/rBc6bd7a974b34d47f01f02666d8cdd5057bc77eba

Cycles: Updated the split heuristic

The split heuristic is now based on the
new paper instead of the abstract/slides
from 2017.

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

M	intern/cycles/kernel/kernel_light.h
M	intern/cycles/kernel/kernel_path_surface.h
M	intern/cycles/render/light.cpp
M	intern/cycles/render/light_tree.cpp
M	intern/cycles/render/light_tree.h

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

diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index 39d575adefa..5ded315881a 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -1128,12 +1128,12 @@ ccl_device float calc_node_importance(KernelGlobals *kg, float3 P, int node_offs
 
 ccl_device void update_parent_node(KernelGlobals *kg, int node_offset,
                                    int *childOffset, int *distribution_id,
-                                   int *nemitters)
+                                   int *num_emitters)
 {
 	float4 node        = kernel_tex_fetch(__light_tree_nodes, node_offset);
 	(*childOffset)     = __float_as_int(node[1]);
 	(*distribution_id) = __float_as_int(node[2]);
-	(*nemitters)       = __float_as_int(node[3]);
+	(*num_emitters)    = __float_as_int(node[3]);
 }
 
 /* picks one of the distant lights and computes the probability of picking it */
@@ -1169,21 +1169,21 @@ ccl_device void light_bvh_sample(KernelGlobals *kg, float3 P, float randu,
 	*pdf_factor = 1.0f;
 
 	/* read in first part of root node of light BVH */
-	int secondChildOffset, distribution_id, nemitters;
-	update_parent_node(kg, 0, &secondChildOffset, &distribution_id, &nemitters);
+	int secondChildOffset, distribution_id, num_emitters;
+	update_parent_node(kg, 0, &secondChildOffset, &distribution_id, &num_emitters);
 
 	int offset = 0;
 	do{
 
 		/* Found a leaf - Choose which light to use */
-		if(nemitters > 0){ // Found a leaf
-			if(nemitters == 1){
+		if(secondChildOffset == -1){ // Found a leaf
+			if(num_emitters == 1){
 				sampled_index = distribution_id;
 			} else { // Leaf with several lights. Pick one randomly.
 				light_distribution_sample(kg, &randu); // TODO: Rescale random number in a better way
-				int light = min((int)(randu* (float)nemitters), nemitters-1);
+				int light = min((int)(randu* (float)num_emitters), num_emitters-1);
 				sampled_index = distribution_id +light;
-				*pdf_factor *= 1.0f / (float)nemitters;
+				*pdf_factor *= 1.0f / (float)num_emitters;
 			}
 			break;
 		} else { // Interior node, pick left or right randomly
@@ -1207,7 +1207,7 @@ ccl_device void light_bvh_sample(KernelGlobals *kg, float3 P, float randu,
 
 			/* update parent node info for next iteration */
 			update_parent_node(kg, offset, &secondChildOffset,
-			                   &distribution_id, &nemitters);
+			                   &distribution_id, &num_emitters);
 		}
 
 
@@ -1249,16 +1249,16 @@ ccl_device int triangle_to_distribution(KernelGlobals *kg, int triangle_id)
 ccl_device float light_bvh_pdf(KernelGlobals *kg, float3 P, int node_id){
 	float pdf = 1.0f;
 	/* read in first part of root node of light BVH */
-	int secondChildOffset, distribution_id, nemitters;
-	update_parent_node(kg, 0, &secondChildOffset, &distribution_id, &nemitters);
+	int secondChildOffset, distribution_id, num_emitters;
+	update_parent_node(kg, 0, &secondChildOffset, &distribution_id, &num_emitters);
 
 	int offset = 0;
 	do{
 
-		if(nemitters > 0){ // Found our leaf node
+		if(secondChildOffset == -1){ // Found our leaf node
 			kernel_assert(offset == node_id);
-			if(nemitters > 1){
-				pdf *= 1.0f / (float)nemitters;
+			if(num_emitters > 1){
+				pdf *= 1.0f / (float)num_emitters;
 			}
 			break;
 		} else { // Interior node, pick left or right depending on node_id
@@ -1282,7 +1282,7 @@ ccl_device float light_bvh_pdf(KernelGlobals *kg, float3 P, int node_id){
 
 			/* update parent node info for next iteration */
 			update_parent_node(kg, offset, &secondChildOffset,
-			                   &distribution_id, &nemitters);
+			                   &distribution_id, &num_emitters);
 		}
 
 	} while(true);
diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h
index 54062a1c2b7..a344f43d786 100644
--- a/intern/cycles/kernel/kernel_path_surface.h
+++ b/intern/cycles/kernel/kernel_path_surface.h
@@ -46,14 +46,13 @@ ccl_device void accum_light_contribution(KernelGlobals *kg,
 }
 
 /* Decides whether to go down both childen or only one in the tree traversal */
-ccl_device bool split(KernelGlobals *kg, ShaderData * sd, int node_offset,
-                      float randu, float randv)
+ccl_device bool split(KernelGlobals *kg, float3 P, int node_offset)
 {
 	/* early exists if never/always splitting */
-	const float threshold = 1.0f - kernel_data.integrator.splitting_threshold;
-	if(threshold == 1.0f){
+	const double threshold = (double)kernel_data.integrator.splitting_threshold;
+	if(threshold == 0.0){
 		return false;
-	} else if(threshold == 0.0f){
+	} else if(threshold == 1.0){
 		return true;
 	}
 
@@ -63,101 +62,59 @@ ccl_device bool split(KernelGlobals *kg, ShaderData * sd, int node_offset,
 	const float3 bboxMin = make_float3( node1[0], node1[1], node1[2]);
 	const float3 bboxMax = make_float3( node1[3], node2[0], node2[1]);
 
-	/* if P is inside bounding box then split */
-	const float3 P = sd->P;
-	const bool x_inside = (P[0] >= bboxMin[0] && P[0] <= bboxMax[0]);
-	const bool y_inside = (P[1] >= bboxMin[1] && P[1] <= bboxMax[1]);
-	const bool z_inside = (P[2] >= bboxMin[2] && P[2] <= bboxMax[2]);
-	if(x_inside && y_inside && z_inside){
-		return true; // Split
-	}
-
-	/* solid angle */
-
-	/* approximate solid angle of bbox with solid angle of sphere */
-	// From PBRT, todo: do for visible faces of bbox instead?
-	const float3 centroid       = 0.5f * (bboxMax + bboxMin);
-	const float  radius_squared = len_squared(bboxMax-centroid);
-	const float  dist_squared   = len_squared(centroid-P);
-
-	/* (---r---C       )
-	 *  \     /
-	 *   \ th/ <--- d
-	 *    \ /
-	 *     P
-	 * sin(th) = r/d <=> sin^2(th) = r^2 / d^2 */
-	const float sin_theta_max_squared = radius_squared / dist_squared;
-	const float cos_theta_max = safe_sqrtf(max(0.0f,1.0f-sin_theta_max_squared));
-	const float solid_angle = (dist_squared <= radius_squared)
-	                          ? M_2PI_F : M_2PI_F * (1.0f - cos_theta_max);
-
-	/* BSDF peak */
-
-	/* TODO: Instead of randomly picking a BSDF, it might be better to
-	 * loop over the BSDFs for the point and see if there are any specular ones.
-	 * If so, pick one of these, otherwise, skip BSDF peak calculations. */
-	const ShaderClosure *sc = shader_bsdf_pick(sd, &randu);
-	if(sc == NULL) {
-		return false; // TODO: handle this
-	}
-
-	float bsdf_peak = 1.0f;
-
-	/* only sample BSDF if "highly specular" */
-	/* TODO: This does not work as I expect, but it might be related to that we
-	 * currently consider non-specular BSDFs here too */
-	if(bsdf_get_roughness_squared(sc) < 0.25f) {
-		float3 eval;
-		float bsdf_pdf;
-		float3 bsdf_omega_in;
-		differential3 bsdf_domega_in;
-
-		bsdf_pdf = 0.0f;
-		bsdf_sample(kg, sd, sc, randu, randv, &eval, &bsdf_omega_in,
-		            &bsdf_domega_in, &bsdf_pdf);
-
-		/* TODO: More efficient to:
-		 *  1. Only sample direction
-		 *  2. If sampled direction points towards cluster
-		 *       - Compute conservative cosine with vector to cluster center
-		 *       - Evaluate simplified GGX for direction sampled direction or
-		 *         vector to cluster?
-		*/
-
-		if(bsdf_pdf != 0.0f && !is_zero(eval)){
-
-			/* check if sampled direction is pointing towards the cluster */
-			const float3 P_to_centroid = normalize(centroid - P);
-			const float theta     = acosf(dot(bsdf_omega_in, P_to_centroid));
-			const float theta_max = acosf(cos_theta_max);
-			if(theta <= theta_max){
-
-				eval /= bsdf_pdf;
-				const float BSDF = min(max3(eval), 1.0f);
-
-				/* conservative cosine between dir to cluster's center and N */
-				const float cosNI = dot(P_to_centroid, sd->N);
-				const float NI    = acosf(cosNI);
-				/* TODO: Do something better than clamp here.
-				 * The problem: conservative_cosNI = cos(M_PI_2_F - theta_max)
-				 * for NI > PI/2 instead of 0 */
-				const float conservative_NI = clamp(NI - theta_max,
-				                                    0.0, M_PI_2_F - theta_max);
-				const float conservative_cosNI = cosf(conservative_NI);
-
-				bsdf_peak = BSDF * conservative_cosNI;
-			}
-		}
+	/* if P is inside bounding sphere then split */
+	const float3 centroid = 0.5f * (bboxMax + bboxMin);
+	const double radius_squared = (double)len_squared(bboxMax - centroid);
+	const double dist_squared   = (double)len_squared(centroid - P);
+	if(dist_squared <= radius_squared){
+		return true;
 	}
 
-	/* TODO: how to make it so bsdf_peak makes it more probable to split? */
-	const float heuristic = solid_angle * bsdf_peak;
-
-	/* normalize heuristic */
-	const float normalized_heuristic = heuristic  * M_1_PI_F * 0.5f;
-
-	/* if heuristic is larger than the threshold then split */
-	return normalized_heuristic > threshold;
+	/* eq. 8 & 9 */
+	/* observed precision issues and issues with overflow of num_emitters_squared.
+	 * using doubles to fix this for now. */
+
+	/* Interval the distance can be in: [a,b] */
+	const double  radius = sqrt(radius_squared);
+	const double  dist   = sqrt(dist_squared);
+	const double  a      = dist - radius;
+	const double  b      = dist + radius;
+
+	const double g_mean         = 1.0 / (a * b);
+	const double g_mean_squared = g_mean * g_mean;
+	const double a3             = a * a * a;
+	const double b3             = b * b * b;
+	const double g_variance     = (b3 - a3) / (3.0 * (b - a) * a3 * b3) -
+	                              g_mean_squared;
+
+	/* eq. 10 */
+	const float4 node0   = kernel_tex_fetch(__light_tree_nodes, node_offset    );
+	const float4 node3   = kernel_tex_fetch(__light_tree_nodes, node_offset + 3);
+	const double energy       = (double)node0[0];
+	const double e_variance   = (double)node3[3];
+	const double num_emitters = (double)__float_as_int(node0[3]);
+
+	const double num_emitters_squared = num_emitters * num_emitters;
+	const double e_mean = energy / num_emitters;
+	const double e_mean_squared = e_mean * e_mean;
+	const double variance = (e_variance * (g_variance + g_mean_squared) +
+	                         e_mean_squared * g_variance) * num_emitters_squared;
+	/*
+	 * If I run into further precision issues
+	 *  sigma^2 = (V[e] * (V[g] + E[g]^2) + (E[e]^2 * V[g]) * N^2 =
+	 *          = / V[e] = E[e^2] - E[e]^2 =  ((e_1)^2 + (e_2)^2 +..+(e_N)^2)/N - E[e]^2 / =
+

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list