Skip to content

Ray intersection sometimes ends up behind shape surface with inaccuracy larger than shadow epsilon #171

@leroyvn

Description

@leroyvn

Ray intersection is sometimes behind surface with inaccuracy larger than shadow epsilon. This may result in wrong occlusion.

System configuration

Platform: macOS 10.15.4
Compiler: Apple clang version 11.0.0 (clang-1100.0.33.17)
Python version: Python 3.7.6 :: Anaconda, Inc.
Mitsuba 2 version: leroyvn/mitsuba2@ed00d49 [branched off abf6938]
Compiled variants:

  • scalar_mono
  • scalar_mono_double

Problem

I sometimes get intersections located at the wrong side of the associated surface. While it's usually not an issue, the error in positioning can become larger that ShadowEpsilon if scene dimensions become too large. At this point, this become problematic, as the surface will occlude while is actually should not. I tried with the rectangle and sphere shape plugins.

Note that I encountered this issue only with the distant sensor plugin (see #143). I suspect that this is due to the fact that the ray origin moves further away from the scene as its size increases, which increases the tolerance on positioning.

Any idea about what can be done to ensure that the intersection point will end up at the correct side of the surface? (Or at least closer than ShadowEpsilon?)

Steps to reproduce

  1. Compile Mitsuba with the aforementioned variants.
  2. Run the following script (problematic configurations will be reported with a '⚠️'):
from itertools import product
import numpy as np
import mitsuba


def scene_dict(scene_size=1., shape="rectangle"):
    from mitsuba.core import ScalarTransform4f, ScalarVector3f

    if shape == "rectangle":
        to_world = ScalarTransform4f.scale(
            ScalarVector3f(scene_size, scene_size, 1.)
        )
    elif shape == "sphere":
        to_world = ScalarTransform4f(
            [[scene_size, 0, 0, 0],
             [0, scene_size, 0, 0],
             [0, 0, scene_size, -scene_size],
             [0, 0, 0, 1]]
        )
    else:
        raise ValueError(f"unsupported shape {shape}")

    return {
        "type": "scene",
        "surface": {
            "type": shape,
            "to_world": to_world,
            "bsdf": {
                "type": "diffuse",
                "reflectance": {"type": "uniform", "value": 0.5}
            }
        },
        "illumination": {
            "type": "directional",
            "direction": [0, 0, -1],
            "irradiance": {"type": "uniform", "value": 1.0}
        },
        "integrator": {"type": "volpath"},
        "measure": {
            "type": "distant",
            "direction": [-1, 0, -1],
            "target": [0, 0, 0],
            "sampler": {"type": "independent", "sample_count": 1},
            "film": {
                "type": "hdrfilm",
                "width": 1,
                "height": 1,
                "pixel_format": "luminance",
                "component_format": "float32",
                "rfilter": {"type": "box"}
            }
        }
    }


for shape, variant in product(["rectangle", "sphere"],
                              ["scalar_mono", "scalar_mono_double"]):
    mitsuba.set_variant(variant)

    from mitsuba.core.xml import load_dict
    from mitsuba.core.math import ShadowEpsilon

    from mitsuba.core import Thread
    Thread.thread().logger().clear_appenders()

    print(f"{shape}, {variant} (ShadowEpsilon = {ShadowEpsilon:.4g})")

    for scene_size in [10**i for i in range(8)]:
        print(f"  scene_size = {scene_size:.4g}")

        scene = load_dict(scene_dict(scene_size))
        sensor = scene.sensors()[0]
        sampler = sensor.sampler()

        # Cast a ray from the sensor
        ray, _ = sensor.sample_ray_differential(
            sampler.next_1d(),
            sampler.next_1d(),
            sampler.next_2d(),
            sampler.next_2d()
        )
        si = scene.ray_intersect(ray)
        print(f"    si.p.z        = {si.p.z:.4g}")
        print(f"    relative      = {si.p.z / ShadowEpsilon:.4g}")

        # Sample illumination from surface interaction point
        ds, emitter_val = scene.sample_emitter_direction(si, sampler.next_2d())
        emitter_val = np.squeeze(emitter_val)
        icon = "⚠️" if emitter_val == 0. else " "
        print(f"  {icon} emitter_val   = {emitter_val}")
        print()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions