Skip to content

Fix scale-dependent ulp bounds in RoundLineIntersector - #640

Open
dongclin wants to merge 1 commit into
RenderKit:masterfrom
dongclin:fix-roundline-ulp-scale
Open

dongclin wants to merge 1 commit into
RenderKit:masterfrom
dongclin:fix-roundline-ulp-scale

Conversation

@dongclin

Copy link
Copy Markdown

Summary

Fixes a scale-invariance bug in kernels/geometry/roundline_intersector.h (RTC_GEOMETRY_TYPE_ROUND_LINEAR_CURVE) where three ulp epsilon bounds in ConeGeometry::isInsideCappedCone and ConeGeometryIntersector::intersectCone were not scaled by the squared tangent slant length g = dPdP - drdr.

Root Cause

Unlike Cone (kernels/geometry/cone.h) and Cylinder (kernels/geometry/cylinder.h), which normalize the segment axis by rl = rcp_length(p1 - p0) so that axial projections are O(L) and unitless parameters lie in [0, 1], ConeGeometry in roundline_intersector.h avoids the square root by scaling the axial parameter domain by g = ||p1 - p0||^2 - (r1 - r0)^2 (O(L^2)):

  • The cone segment spans unnormalized axial coordinate y in [0, g], with normalized curve parameter u = clamp(y / g, 0.0f, 1.0f).
  • Lines 338 & 342 (ConeGeometryIntersector::intersectCone):
    • lower = select((y_cone_front > -(float)ulp) & (y_cone_front <= g) & (g > 0.0f), ...)
    • upper = select((y_cone_back > -(float)ulp) & (y_cone_back <= g) & (g > 0.0f), ...)
      Because y_cone_front and y_cone_back have units of g (O(L^2)), testing > -(float)ulp admits phantom cylinder/cone hits behind p0 over physical distance ulp / L (u in [-ulp / g, 0)). Furthermore, because t_first = select(valid_lower, t_cone_sphere_lower, t_cone_sphere_upper), an external ray hits the back wall t_cone_upper if t_cone_lower is rejected—so both front and back bounds must be scaled by g.
  • Line 203 (ConeGeometry::isInsideCappedCone):
    • cap0 = -r0dr + vfloat<M>(ulp)
    • cap1 = -r1*dr + dPdP = -r0dr + g
      Because cap1 - (-r0dr) = g, comparing against an unscaled ulp (1.1920929e-7f) causes cap0 >= cap1 whenever g <= ulp (segment length L <= sqrt(ulp) ~= 3.45e-4), collapsing (cap0, cap1) to the empty set so neighbor-cone clipping (coneL.isInsideCappedCone / coneR.isInsideCappedCone) unconditionally returns false.

Before vs. After Fix (rtcIntersect1 on L-Shaped 2-Segment Curve (-L,0,0) -> (0,0,0) -> (0,L,0), r = 0.35*L)

1. Reference (L = 1.0) 2. Before Fix (L = 1e-4) 3. After Fix (L = 1e-4, ulp * g)
image image image

At L = 1e-4 (g = 1e-8 < ulp), both segments grow a ulp / L = 11.92 * L phantom cylinder backward past their start vertices (Seg 0 extends 11.92 * L to the left of (-L,0,0), and Seg 1 extends 11.92 * L downward through the (0,0,0) corner), while isInsideCappedCone fails to clip internal overlap at the (0,0,0) joint. Scaling ulp by g in all three places restores exact scale invariance.

At L = 1e-4 (g = 1e-8 < ulp), both segments grow a ulp / L = 11.92 * L phantom cylinder backward past their start vertices (Seg 0 extends 11.92 * L to the left of (-L,0,0), and Seg 1 extends 11.92 * L downward through the (0,0,0) corner), while isInsideCappedCone fails to clip internal overlap at the (0,0,0) joint. Scaling ulp by g in all three places restores exact scale invariance.

Reproduce with embree_viewer and RoundLineSegments XML
l_curve_1.xml (L = 1.0):

<?xml version="1.0"?>
<scene>
  <Group>
    <RoundLineSegments>
      <positions>
        -1.0 0.0 0.0 0.35
         0.0 0.0 0.0 0.35
         0.0 1.0 0.0 0.35
      </positions>
      <indices>
        0 1
      </indices>
      <material>
        <code>"OBJ"</code>
        <parameters>
          <float3 name="Kd">1 1 1</float3>
        </parameters>
      </material>
    </RoundLineSegments>
  </Group>
</scene>

l_curve_1e-4.xml (L = 1e-4):

<?xml version="1.0"?>
<scene>
  <Group>
    <RoundLineSegments>
      <positions>
        -0.0001 0.0    0.0 0.000035
         0.0    0.0    0.0 0.000035
         0.0    0.0001 0.0 0.000035
      </positions>
      <indices>
        0 1
      </indices>
      <material>
        <code>"OBJ"</code>
        <parameters>
          <float3 name="Kd">1 1 1</float3>
        </parameters>
      </material>
    </RoundLineSegments>
  </Group>
</scene>

Render and compare with embree_viewer:

# 1. Reference (L = 1.0)
./embree_viewer -i l_curve_1.xml \
  --vp -6.0 -5.7 70.0 --vi -6.0 -5.7 0.0 --vu 0 1 0 --fov 12.2 --righthanded \
  --size 360 360 -o l_curve_L_1.png
# 2. Small scale (L = 1e-4)
./embree_viewer -i l_curve_1e-4.xml \
  --vp -0.0006 -0.00057 0.0070 --vi -0.0006 -0.00057 0.0 --vu 0 1 0 --fov 12.2 --righthanded \
  --size 360 360 -o l_curve_L_1e-4.png --compare l_curve_L_1.png

In ConeGeometry::isInsideCappedCone and ConeGeometryIntersector::intersectCone,
the axial coordinate y along the unnormalized tangent vector dP = p1 - p0 has
units of g = ||p1 - p0||^2 - (r1 - r0)^2 and spans [0, g]. Scale the ulp
tolerance by g so the bounds remain invariant to geometric scale.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants