From 52c2b66b52adde882679f8fd1e15ff85f4bcefa2 Mon Sep 17 00:00:00 2001
From: Mariia Soroka <mariia.soroka@epfl.ch>
Date: Fri, 2 Jun 2023 10:27:09 +0200
Subject: [PATCH 1/3] fixed ray differentials for analytical sphere

---
 src/shapes/sphere.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/shapes/sphere.cpp b/src/shapes/sphere.cpp
index ccb30d15a..0ef805a0f 100644
--- a/src/shapes/sphere.cpp
+++ b/src/shapes/sphere.cpp
@@ -539,16 +539,16 @@ class Sphere final : public Shape<Float, Spectrum> {
                       cos_phi = local.x() * inv_rd,
                       sin_phi = local.y() * inv_rd;
 
-                si.dp_dv = Vector3f(local.z() * cos_phi,
-                                    local.z() * sin_phi,
-                                    -rd);
+                si.dp_dv = Vector3f(-local.z() * cos_phi * inv_rd,
+                                    -local.z() * sin_phi * inv_rd,
+                                    1.f);
 
                 Mask singularity_mask = active && dr::eq(rd, 0.f);
                 if (unlikely(dr::any_or<true>(singularity_mask)))
                     si.dp_dv[singularity_mask] = Vector3f(1.f, 0.f, 0.f);
 
                 si.dp_du = to_world * si.dp_du * (2.f * dr::Pi<Float>);
-                si.dp_dv = to_world * si.dp_dv * dr::Pi<Float>;
+                si.dp_dv = to_world * si.dp_dv * 2.f;
             }
         }
 

From 1d5b005cb98ee5270964375092cc1bc8aa991242 Mon Sep 17 00:00:00 2001
From: Mariia Soroka <mariia.soroka@epfl.ch>
Date: Fri, 2 Jun 2023 13:38:20 +0200
Subject: [PATCH 2/3] fixed tests for analytical sphere

---
 src/shapes/sphere.cpp           | 6 +++---
 src/shapes/tests/test_sphere.py | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/shapes/sphere.cpp b/src/shapes/sphere.cpp
index 0ef805a0f..bfaf8e8f1 100644
--- a/src/shapes/sphere.cpp
+++ b/src/shapes/sphere.cpp
@@ -539,9 +539,9 @@ class Sphere final : public Shape<Float, Spectrum> {
                       cos_phi = local.x() * inv_rd,
                       sin_phi = local.y() * inv_rd;
 
-                si.dp_dv = Vector3f(-local.z() * cos_phi * inv_rd,
-                                    -local.z() * sin_phi * inv_rd,
-                                    1.f);
+                si.dp_dv = Vector3f(local.z() * cos_phi * inv_rd,
+                                    local.z() * sin_phi * inv_rd,
+                                    -1.f);
 
                 Mask singularity_mask = active && dr::eq(rd, 0.f);
                 if (unlikely(dr::any_or<true>(singularity_mask)))
diff --git a/src/shapes/tests/test_sphere.py b/src/shapes/tests/test_sphere.py
index f30ec5037..3ac2f8254 100644
--- a/src/shapes/tests/test_sphere.py
+++ b/src/shapes/tests/test_sphere.py
@@ -318,7 +318,7 @@ def test09_si_singularity(variants_all_rgb):
     si = scene.ray_intersect(ray)
 
     assert dr.allclose(si.dp_du, [0, 0, 0])
-    assert dr.allclose(si.dp_dv, [dr.pi, 0, 0])
+    assert dr.allclose(si.dp_dv, [2, 0, 0])
     assert dr.allclose(si.sh_frame.s, [1, 0, 0])
     assert dr.allclose(si.sh_frame.t, [0, -1, 0])
     assert dr.allclose(si.sh_frame.n, [0, 0, -1])
@@ -331,7 +331,7 @@ def test10_si_singularity_centered(variants_all_rgb):
     si = scene.ray_intersect(ray)
 
     assert dr.allclose(si.dp_du, [0, 0, 0])
-    assert dr.allclose(si.dp_dv, [dr.pi, 0, 0])
+    assert dr.allclose(si.dp_dv, [2, 0, 0])
     assert dr.allclose(si.sh_frame.s, [1, 0, 0])
     assert dr.allclose(si.sh_frame.t, [0, 1, 0])
     assert dr.allclose(si.sh_frame.n, [0, 0, 1])

From f8487782a7e1ffed88218109663343e08276efcd Mon Sep 17 00:00:00 2001
From: Mariia Soroka <mariia.soroka@epfl.ch>
Date: Fri, 2 Jun 2023 16:48:37 +0200
Subject: [PATCH 3/3] fixed uv coordinates for the sphere

---
 src/shapes/sphere.cpp           |  3 +--
 src/shapes/tests/test_sphere.py | 10 +++++-----
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/shapes/sphere.cpp b/src/shapes/sphere.cpp
index bfaf8e8f1..e21757428 100644
--- a/src/shapes/sphere.cpp
+++ b/src/shapes/sphere.cpp
@@ -525,12 +525,11 @@ class Sphere final : public Shape<Float, Spectrum> {
 
         if (likely(need_uv)) {
             Float rd_2  = dr::sqr(local.x()) + dr::sqr(local.y()),
-                  theta = unit_angle_z(local),
                   phi   = dr::atan2(local.y(), local.x());
 
             dr::masked(phi, phi < 0.f) += 2.f * dr::Pi<Float>;
 
-            si.uv = Point2f(phi * dr::InvTwoPi<Float>, theta * dr::InvPi<Float>);
+            si.uv = Point2f(phi * dr::InvTwoPi<Float>, dr::fnmadd(0.5f, local.z(), 0.5f));
             if (likely(need_dp_duv)) {
                 si.dp_du = Vector3f(-local.y(), local.x(), 0.f);
 
diff --git a/src/shapes/tests/test_sphere.py b/src/shapes/tests/test_sphere.py
index 3ac2f8254..7308b9d4e 100644
--- a/src/shapes/tests/test_sphere.py
+++ b/src/shapes/tests/test_sphere.py
@@ -74,17 +74,17 @@ def test03_ray_intersect_transform(variant_scalar_rgb):
                     si = s.ray_intersect(ray)
                     ray_u = mi.Ray3f(ray)
                     ray_v = mi.Ray3f(ray)
-                    eps = 1e-4
+                    eps = 1e-5
                     ray_u.o += si.dp_du * eps
                     ray_v.o += si.dp_dv * eps
                     si_u = s.ray_intersect(ray_u)
                     si_v = s.ray_intersect(ray_v)
                     if si_u.is_valid():
                         du = (si_u.uv - si.uv) / eps
-                        assert dr.allclose(du, [1, 0], atol=2e-2)
+                        assert dr.allclose(du, [1, 0], atol=6e-2)
                     if si_v.is_valid():
                         dv = (si_v.uv - si.uv) / eps
-                        assert dr.allclose(dv, [0, 1], atol=2e-2)
+                        assert dr.allclose(dv, [0, 1], atol=6e-2)
 
 
 def test04_ray_intersect_vec(variant_scalar_rgb):
@@ -180,7 +180,7 @@ def test06_differentiable_surface_interaction_ray_forward(variants_all_ad_rgb):
     si = shape.ray_intersect(ray)
     si.uv *= 1.0
     dr.forward(ray.o.z)
-    assert dr.allclose(dr.grad(si.uv), [0, -2 / (2.0 * dr.pi)])
+    assert dr.allclose(dr.grad(si.uv), [0, -1 / 2.0])
 
     # # If the ray origin is shifted along the x-axis, so does si.n
     dr.enable_grad(ray.o)
@@ -308,7 +308,7 @@ def test08_differentiable_surface_interaction_ray_forward_follow_shape(variants_
 
     assert dr.allclose(dr.grad(si.p), 0.0)
     assert dr.allclose(dr.grad(si.n), 0.0)
-    assert dr.allclose(dr.grad(si.uv), [0.0, -0.5])
+    assert dr.allclose(dr.grad(si.uv), [0.0, -0.25 * dr.pi])
 
 
 def test09_si_singularity(variants_all_rgb):