def test03_mueller_to_world_to_local(variant_scalar_mono_polarized):
    """
    At a few places, coordinate changes between local BSDF reference frame and
    world coordinates need to take place. This change also needs to be applied
    to Mueller matrices used in computations involving polarization state.

    In practice, this is always a simple rotation of reference Stokes vectors
    (for incident & outgoing directions) of the Mueller matrix.

    To test this behavior we take any Mueller matrix (e.g. linear polarizer)
    for some arbitrary incident/outgoing directions in world coordinates and
    compute the round trip going to local frame and back again.
    """
    from mitsuba.core import Frame3f, UnpolarizedSpectrum
    from mitsuba.render import SurfaceInteraction3f
    from mitsuba.render.mueller import linear_polarizer
    import numpy as np

    si = SurfaceInteraction3f()
    si.sh_frame = Frame3f(ek.normalize([1.0, 1.0, 1.0]))

    M = linear_polarizer(UnpolarizedSpectrum(1.0))

    # Random incident and outgoing directions
    wi_world = ek.normalize([0.2, 0.0, 1.0])
    wo_world = ek.normalize([0.0, -0.8, 1.0])

    wi_local = si.to_local(wi_world)
    wo_local = si.to_local(wo_world)

    M_local = si.to_local_mueller(M, wi_world, wo_world)
    M_world = si.to_world_mueller(M_local, wi_local, wo_local)

    assert ek.allclose(M, M_world, atol=1e-5)
Exemple #2
0
def test03_linear_polarizer(variant_scalar_rgb):
    from mitsuba.render.mueller import rotated_element, linear_polarizer

    # Malus' law
    angle = 30 * ek.pi/180
    value_malus = ek.cos(angle)**2
    polarizer = rotated_element(angle, linear_polarizer(1.0))

    stokes_in  = [1, 1, 0, 0]
    stokes_out = polarizer @ stokes_in
    intensity  = stokes_out[0]
    assert ek.allclose(intensity, value_malus)
Exemple #3
0
def test04_linear_polarizer_rotated(variant_scalar_rgb):
    from mitsuba.render.mueller import rotated_element, linear_polarizer, rotator

    # The closed-form expression for a rotated linear polarizer is available
    # in many textbooks and should match the behavior of manually rotating the
    # optical element.
    angle = 33 * ek.pi/180
    s, c = ek.sin(2*angle), ek.cos(2*angle)
    M_ref = ek.scalar.Matrix4f([[1, c, s, 0],
                                [c, c*c, s*c, 0],
                                [s, s*c, s*s, 0],
                                [0, 0, 0, 0]]) * 0.5
    R = rotator(angle)
    L = linear_polarizer()
    M = rotated_element(angle, L)
    assert ek.allclose(M, M_ref)
Exemple #4
0
def test08_rotate_mueller_basis(variant_scalar_rgb):
    from mitsuba.core import Transform4f
    from mitsuba.render.mueller import stokes_basis, rotated_element, rotate_mueller_basis, \
                                       rotate_mueller_basis_collinear, linear_polarizer

    # If we have an optical element such as a linear polarizer, its rotation around
    # the optical axis can also be interpreted as a change of both incident and
    # outgoing Stokes reference frames:

    w = [0, 0, 1] # Optical axis / forward direction

    s_00 = [1, 1, 0, 0] # Horizontally polarized light
    b_00 = stokes_basis(w)  # Corresponding Stokes basis

    M = linear_polarizer()

    def rotate_vector(v, axis, angle):
        return Transform4f.rotate(axis, angle).transform_vector(v)

    # As reference, rotate the element directly by -45˚
    M_rotated_element = rotated_element(-45 * ek.pi/180, M)

    # Now rotate the two reference bases by 45˚ instead and make sure results are
    # equivalent.
    # More thorough explanation of what's going on here:
    # Mueller matrix 'M' works in b_00 = [1, 0, 0], and we want to construct
    # another matrix that works in b_45 =[0.707, 0.707, 0] instead.
    # 'rotate_mueller_basis' does exactly that:
    # R(b_00 -> b_45) * M * R(b_45 -> b_00) = R(+45˚) * M * R(-45˚) which is
    # equivalent to rotating the elemnt by -45˚.
    b_45 = rotate_vector(b_00, w, +45.0)
    M_rotated_bases = rotate_mueller_basis(M,
                                           w, b_00, b_45,
                                           w, b_00, b_45)
    assert ek.allclose(M_rotated_element, M_rotated_bases, atol=1e-5)

    # Also test alternative rotation method that assumes collinear incident and
    # outgoing directions.
    M_rotated_bases_aligned = rotate_mueller_basis_collinear(M, w, b_00, b_45)
    assert ek.allclose(M_rotated_element, M_rotated_bases_aligned, atol=1e-5)