//  [python]
def up_sample_unbias(self, rays_o, rays_d, z_vals, udf, sample_dist,
                         n_importance, inv_s, beta, gamma, debug=False):
        """
        up sampling strategy similar with NeuS;
        only sample more points at the first possible surface intersection
        """
        device = z_vals.device
        batch_size, n_samples = z_vals.shape
        pts = rays_o[:, None, :] + rays_d[:, None, :] * z_vals[..., :, None]  # n_rays, n_samples, 3
        radius = torch.linalg.norm(pts, ord=2, dim=-1, keepdim=False)
        inside_sphere = (radius[:, :-1] < 1.0) | (radius[:, 1:] < 1.0)

        udf = udf.reshape(batch_size, n_samples)

        dists_raw = z_vals[..., 1:] - z_vals[..., :-1]
        dists_raw = torch.cat([dists_raw, torch.Tensor([sample_dist]).to(device).expand(dists_raw[..., :1].shape)], -1)

        dirs = rays_d[:, None, :].expand(pts.shape)

        prev_udf, next_udf = udf[:, :-1], udf[:, 1:]
        prev_z_vals, next_z_vals = z_vals[:, :-1], z_vals[:, 1:]
        mid_udf = (prev_udf + next_udf) * 0.5
        mid_z_vals = (prev_z_vals + next_z_vals) * 0.5

        dists = (next_z_vals - prev_z_vals)

        # !  directly use udf to approximate cos_val, otherwise, sampling will be biased
        fake_sdf = udf
        ## near the 0levelset, the cos_val approximation should be inaccurate
        prev_sdf, next_sdf = fake_sdf[:, :-1], fake_sdf[:, 1:]

        true_cos = (next_sdf - prev_sdf) / (next_z_vals - prev_z_vals + 1e-5)  # ! the cos_val will be larger than 1
        cos_val = -1 * torch.abs(true_cos)

        prev_cos_val = torch.cat([torch.zeros([batch_size, 1]).to(device), cos_val[:, :-1]], dim=-1)
        cos_val = torch.stack([prev_cos_val, cos_val], dim=-1)
        cos_val, _ = torch.min(cos_val, dim=-1, keepdim=False)

        cos_val = cos_val.clip(-1e3, 0.0) * inside_sphere

        # ! leverage the direction of gradients to alleviate the early zero of vis_prob
        vis_mask = torch.ones_like(true_cos).to(true_cos.device).to(true_cos.dtype) * (
                true_cos < 0.05).float()
        vis_mask = vis_mask.reshape(batch_size, n_samples - 1)
        vis_mask = torch.cat([torch.ones([batch_size, 1]).to(device), vis_mask], dim=-1)

        # * the probability of occlusion
        raw_occ = self.udf2logistic(udf, beta, 1.0, 1.0).reshape(batch_size, n_samples)
        # near 0levelset alpha_acc -> 1 others -> 0
        alpha_occ = 1.0 - torch.exp(-F.relu(raw_occ) * gamma * dists_raw)

        # - use the vis_mask, make upsampling more uniform and centered
        vis_prob = torch.cumprod(
            torch.cat([torch.ones([batch_size, 1]).to(device), (1. - alpha_occ + vis_mask).clip(0, 1) + 1e-7], -1), -1)[
                   :, :-1]  # before udf=0 -> 1; after udf=0 -> 0

        signs_prob = vis_prob[:, :-1]
        sdf_plus = mid_udf
        sdf_minus = mid_udf * -1
        alpha_plus = self.sdf2alpha(sdf_plus, cos_val, dists, inv_s)  # batch_size, n_samples
        alpha_minus = self.sdf2alpha(sdf_minus, cos_val, dists, inv_s)
        alpha = alpha_plus * signs_prob + alpha_minus * (1 - signs_prob)
        alpha = alpha.reshape(batch_size, n_samples - 1)

        weights = alpha * torch.cumprod(
            torch.cat([torch.ones([batch_size, 1]).to(device), 1. - alpha + 1e-7], -1), -1)[:, :-1]
        z_samples = sample_pdf(z_vals, weights, n_importance, det=True).detach()

        flag = torch.any(torch.isnan(z_samples)).cpu().numpy().item()
        if flag:
            print("z_vals", z_samples[torch.isnan(z_samples)])
            print('z_vals have nan values')
            pdb.set_trace()
            # raise Exception("gradients have nan values")

        return z_samples