コード例 #1
0
def test07_rotate_stokes_basis(variant_scalar_rgb):
    from mitsuba.core import Transform4f
    from mitsuba.render.mueller import stokes_basis, rotate_stokes_basis

    # Each Stokes vector describes light polarization only w.r.t. a certain reference
    # frame determined by unit vector perpendicular to the direction of travel.
    #
    # Here we test a duality between rotating the polarization direction (e.g. from
    # linear polarization at 0˚ to 90˚) and rotating the Stokes reference frame
    # in the opposite direction.
    # We start out with horizontally polarized light (Stokes vector [1, 1, 0, 0])
    # and perform several transformations.

    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

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

    # Switch to basis rotated by 90˚.
    b_90 = rotate_vector(b_00, w, 90.0)
    assert ek.allclose(b_90, [0, 1, 0], atol=1e-3)
    # Now polarization should be at 90˚ / vertical (with Stokes vector [1, -1, 0, 0])
    s_90 = rotate_stokes_basis(w, b_00, b_90) @ s_00
    assert ek.allclose(s_90, [1.0, -1.0, 0.0, 0.0], atol=1e-3)

    # Switch to basis rotated by +45˚.
    b_45 = rotate_vector(b_00, w, +45.0)
    assert ek.allclose(b_45, [0.70712, 0.70712, 0.0], atol=1e-3)
    # Now polarization should be at -45˚ (with Stokes vector [1, 0, -1, 0])
    s_45 = rotate_stokes_basis(w, b_00, b_45) @ s_00
    assert ek.allclose(s_45, [1.0, 0.0, -1.0, 0.0], atol=1e-3)

    # Switch to basis rotated by -45˚.
    b_45_neg = rotate_vector(b_00, w, -45.0)
    assert ek.allclose(b_45_neg, [0.70712, -0.70712, 0.0], atol=1e-3)
    # Now polarization should be at +45˚ (with Stokes vector [1, 0, +1, 0])
    s_45_neg = rotate_stokes_basis(w, b_00, b_45_neg) @ s_00
    assert ek.allclose(s_45_neg, [1.0, 0.0, +1.0, 0.0], atol=1e-3)
コード例 #2
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)
コード例 #3
0
def test07_path_tracer_half_wave(variant_scalar_mono_polarized):
    from mitsuba.core import Spectrum
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode
    from mitsuba.render.mueller import stokes_basis, rotate_stokes_basis_m

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i, 0] = v[i]
        return res

    # Another test involving a half-wave plate, this time inside an actual
    # polarized path tracer.
    # (Serves also as test case for the polarized path tracer itself.)
    #
    # Following "Polarized Light - Fundamentals and Applications" by Edward Collett
    # Chapter 5.3:
    #
    # Case 1 & 2) Switch between diagonal linear polarization states (-45˚ & + 45˚)
    #
    # In this test, a polarizer and half-wave plate are placed between light
    # source and camera.
    # The polarizer is used to convert emitted light into a +-45˚ linearly
    # polarized state. The subsequent retarder will flip the polarization
    # axis to the opposite one.

    linear_pos = spectrum_from_stokes([1, 0, +1, 0])
    linear_neg = spectrum_from_stokes([1, 0, -1, 0])

    angles = [+45.0, -45.0]
    expected = [linear_neg, linear_pos]
    observed = []

    for angle in angles:
        scene_str = """<scene version='2.0.0'>
                           <integrator type="path"/>

                           <sensor type="perspective">
                               <float name="fov" value="0.00001"/>
                               <transform name="to_world">
                                   <lookat origin="0, 10, 0"
                                           target="0, 0, 0"
                                           up    ="0, 0, 1"/>
                               </transform>
                               <film type="hdrfilm">
                                   <integer name="width" value="1"/>
                                   <integer name="height" value="1"/>
                                   <rfilter type="gaussian"/>
                                   <string name="pixel_format" value="rgb"/>
                               </film>
                           </sensor>

                           <!-- Light source -->
                           <shape type="rectangle">
                               <transform name="to_world">
                                   <rotate x="1" y="0" z="0" angle="-90"/>
                                   <translate y="-10"/>
                               </transform>

                               <emitter type="area">
                                   <spectrum name="radiance" value="1"/>
                               </emitter>
                           </shape>

                           <!-- Polarizer at given angle -->
                           <shape type="rectangle">
                               <bsdf type="polarizer">
                               </bsdf>

                               <transform name="to_world">
                                   <rotate x="1" y="0" z="0" angle="-90"/>
                                   <rotate y="1" angle="{}"/>   <!-- Rotate around optical axis -->
                                   <translate y="-5"/>
                               </transform>
                           </shape>

                           <!-- Half-wave plate. -->
                           <shape type="rectangle">
                               <bsdf type="retarder">
                                   <spectrum name="delta" value="180"/>
                               </bsdf>

                                <transform name="to_world">
                                   <rotate x="1" y="0" z="0" angle="-90"/>
                                   <translate y="0"/>
                               </transform>
                           </shape>
                       </scene>""".format(angle)

        scene = load_string(scene_str)
        integrator = scene.integrator()
        sensor = scene.sensors()[0]
        sampler = sensor.sampler()

        # Sample ray from sensor
        ray, _ = sensor.sample_ray_differential(0.0, 0.5, [0.5, 0.5],
                                                [0.5, 0.5])

        # Call integrator
        value, _, _ = integrator.sample(scene, sampler, ray)

        # Normalize Stokes vector
        value /= value[0, 0]

        # Align output stokes vector (based on ray.d) with optical table. (In this configuration, this is a no-op.)
        forward = -ray.d
        basis_cur = stokes_basis(forward)
        basis_tar = [-1, 0, 0]

        R = rotate_stokes_basis_m(forward, basis_cur, basis_tar)
        value = R @ value

        observed.append(value)

    # Make sure observations match expectations
    for k in range(len(expected)):
        assert ek.allclose(observed[k], expected[k], atol=1e-3)
コード例 #4
0
def test05_sample_half_wave_world(variant_scalar_mono_polarized):
    from mitsuba.core import Ray3f, Spectrum
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode
    from mitsuba.render.mueller import stokes_basis, rotate_mueller_basis_collinear

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i, 0] = v[i]
        return res

    # Test polarized implementation. Special case of delta = 180˚, also known
    # as a half-wave plate. (In world coordinates.)
    #
    # Following "Polarized Light - Fundamentals and Applications" by Edward Collett
    # Chapter 5.3:
    #
    # Case 1 & 2) Switch between diagonal linear polarization states (-45˚ & + 45˚)
    # Case 3 & 4) Switch circular polarization direction

    linear_pos = spectrum_from_stokes([1, 0, +1, 0])
    linear_neg = spectrum_from_stokes([1, 0, -1, 0])
    circular_right = spectrum_from_stokes([1, 0, 0, +1])
    circular_left = spectrum_from_stokes([1, 0, 0, -1])

    # Incident direction
    forward = [0, -1, 0]

    ctx = BSDFContext()
    ctx.mode = TransportMode.Importance
    ray = Ray3f([0, 100, 0], forward, 0.0, 0.0)

    # Build scene with given polarizer rotation angle
    scene_str = """<scene version='2.0.0'>
                       <shape type="rectangle">
                           <bsdf type="retarder">
                               <spectrum name="delta" value="180"/>
                           </bsdf>

                           <transform name="to_world">
                               <rotate x="1" y="0" z="0" angle="-90"/>  <!-- Rotate s.t. it is no longer aligned with local coordinates -->
                           </transform>
                       </shape>
                   </scene>"""
    scene = load_string(scene_str)

    # Intersect ray against geometry
    si = scene.ray_intersect(ray)

    bs, M_local = si.bsdf().sample(ctx, si, 0.0, [0.0, 0.0])
    M_world = si.to_world_mueller(M_local, -si.wi, bs.wo)

    # Make sure we are measuring w.r.t. to the optical table
    M = rotate_mueller_basis_collinear(M_world, forward, stokes_basis(forward),
                                       [-1, 0, 0])

    # Case 1)
    assert ek.allclose(M @ linear_pos, linear_neg, atol=1e-3)
    # Case 2)
    assert ek.allclose(M @ linear_neg, linear_pos, atol=1e-3)
    # Case 3)
    assert ek.allclose(M @ circular_right, circular_left, atol=1e-3)
    # Case 4)
    assert ek.allclose(M @ circular_left, circular_right, atol=1e-3)
コード例 #5
0
def test03_sample_world(variant_scalar_mono_polarized):
    from mitsuba.core import Ray3f, Spectrum
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode
    from mitsuba.render.mueller import stokes_basis, rotate_mueller_basis_collinear

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i,0] = v[i]
        return res

    # Test polarized implementation, version in world coordinates. This involves
    # additional coordinate changes to the local reference frame and back.
    #
    # The polarizer is rotated to different angles and hit with fully
    # unpolarized light (Stokes vector [1, 0, 0, 0]).
    # We then test if the outgoing Stokes vector corresponds to the expected
    # rotation of linearly polarized light.

    # Incident direction
    forward = [0, 1, 0]
    stokes_in = spectrum_from_stokes([1, 0, 0, 0])

    ctx = BSDFContext()
    ctx.mode = TransportMode.Importance
    ray = Ray3f([0, -100, 0], forward, 0.0, 0.0)

    # Polarizer rotation angles
    angles = [0, 90, +45, -45]
    # Expected outgoing Stokes vector
    expected_states = [spectrum_from_stokes([0.5, 0.5, 0, 0]),
                       spectrum_from_stokes([0.5, -0.5, 0, 0]),
                       spectrum_from_stokes([0.5, 0, +0.5, 0]),
                       spectrum_from_stokes([0.5, 0, -0.5, 0])]

    for k in range(len(angles)):
        angle = angles[k]
        expected = expected_states[k]

        # Build scene with given polarizer rotation angle
        scene_str = """<scene version='2.0.0'>
                           <shape type="rectangle">
                               <bsdf type="polarizer"/>

                               <transform name="to_world">
                                   <rotate x="0" y="0" z="1" angle="{}"/>   <!-- Rotate around optical axis -->
                                   <rotate x="1" y="0" z="0" angle="-90"/>  <!-- Rotate s.t. it is no longer aligned with local coordinates -->
                               </transform>
                           </shape>
                       </scene>""".format(angle)
        scene = load_string(scene_str)

        # Intersect ray against geometry
        si = scene.ray_intersect(ray)

        bs, M_local = si.bsdf().sample(ctx, si, 0.0, [0.0, 0.0])
        M_world = si.to_world_mueller(M_local, -si.wi, bs.wo)

        # Make sure we are measuring w.r.t. to the optical table
        M = rotate_mueller_basis_collinear(M_world,
                                           forward,
                                           stokes_basis(forward), [-1, 0, 0])

        stokes_out = M @ stokes_in
        assert ek.allclose(stokes_out, expected, atol=1e-3)
コード例 #6
0
def test04_path_tracer_polarizer(variant_scalar_mono_polarized):
    from mitsuba.core import Spectrum
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode
    from mitsuba.render.mueller import stokes_basis, rotate_stokes_basis_m

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i,0] = v[i]
        return res

    # Test polarizer implementation inside of an actual polarized path tracer.
    # (Serves also as test case for the polarized path tracer itself.)
    #
    # The polarizer is place in front of a light source that emits unpolarized
    # light (Stokes vector [1, 0, 0, 0]). The filter is rotated to different
    # angles and the transmitted polarization state is direclty observed with
    # a sensor.
    # We then test if the outgoing Stokes vector corresponds to the expected
    # rotation of linearly polarized light.

    horizontal_pol   = spectrum_from_stokes([1, +1, 0, 0])
    vertical_pol     = spectrum_from_stokes([1, -1, 0, 0])
    pos_diagonal_pol = spectrum_from_stokes([1, 0, +1, 0])
    neg_diagonal_pol = spectrum_from_stokes([1, 0, -1, 0])

    angles = [0, 90, +45, -45]
    expected = [horizontal_pol,
                vertical_pol,
                pos_diagonal_pol,
                neg_diagonal_pol]

    for k, angle in enumerate(angles):
        scene_str = """<scene version='2.0.0'>
                           <integrator type="path"/>

                           <sensor type="perspective">
                               <float name="fov" value="0.00001"/>
                               <transform name="to_world">
                                   <lookat origin="0, 10, 0"
                                           target="0, 0, 0"
                                           up    ="0, 0, 1"/>
                               </transform>
                               <film type="hdrfilm">
                                   <integer name="width" value="1"/>
                                   <integer name="height" value="1"/>
                                   <rfilter type="gaussian"/>
                                   <string name="pixel_format" value="rgb"/>
                               </film>
                           </sensor>

                           <!-- Light source -->
                           <shape type="rectangle">
                               <transform name="to_world">
                                   <rotate x="1" y="0" z="0" angle="-90"/>
                                   <translate y="-10"/>
                               </transform>

                               <emitter type="area">
                                   <spectrum name="radiance" value="1"/>
                               </emitter>
                           </shape>

                           <!-- Polarizer -->
                           <shape type="rectangle">
                               <bsdf type="polarizer">
                               </bsdf>

                               <transform name="to_world">
                                   <rotate x="1" y="0" z="0" angle="-90"/>
                                   <rotate y="1" angle="{}"/>
                               </transform>
                           </shape>
                       </scene>""".format(angle)

        scene = load_string(scene_str)
        integrator = scene.integrator()
        sensor     = scene.sensors()[0]
        sampler    = sensor.sampler()

        # Sample ray from sensor
        ray, _ = sensor.sample_ray_differential(0.0, 0.5, [0.5, 0.5], [0.5, 0.5])

        # Call integrator
        value, _, _ = integrator.sample(scene, sampler, ray)

        # Normalize Stokes vector
        value /= value[0, 0]

        # Align output stokes vector (based on ray.d) with optical table. (In this configuration, this is a no-op.)
        forward = -ray.d
        basis_cur = stokes_basis(forward)
        basis_tar = [-1, 0, 0]
        R = rotate_stokes_basis_m(forward, basis_cur, basis_tar)
        value = R @ value

        assert ek.allclose(value, expected[k], atol=1e-3)
コード例 #7
0
def test02_sample_pol_local(variant_scalar_mono_polarized):
    from mitsuba.core import Frame3f, Transform4f, Spectrum, UnpolarizedSpectrum, Vector3f
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode, SurfaceInteraction3f, fresnel_conductor
    from mitsuba.render.mueller import stokes_basis, rotate_mueller_basis

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i, 0] = v[i]
        return res

    # Create a Silver conductor BSDF and reflect different polarization states
    # at a 45˚ angle.
    # All tests take place directly in local BSDF coordinates. Here we only
    # want to make sure that the output of this looks like what you would
    # expect from a Mueller matrix describing specular reflection on a mirror.

    eta = 0.136125 + 4.010625j  # IoR values of Ag at 635.816284nm
    bsdf = load_string("""<bsdf version='2.0.0' type='conductor'>
                              <spectrum name="eta" value="{}"/>
                              <spectrum name="k" value="{}"/>
                          </bsdf>""".format(eta.real, eta.imag))

    theta_i = 45 * ek.pi / 180
    wi = Vector3f([-ek.sin(theta_i), 0, ek.cos(theta_i)])

    ctx = BSDFContext()
    ctx.mode = TransportMode.Importance
    si = SurfaceInteraction3f()
    si.p = [0, 0, 0]
    si.wi = wi
    n = [0, 0, 1]
    si.sh_frame = Frame3f(n)

    bs, M_local = bsdf.sample(ctx, si, 0.0, [0.0, 0.0])
    wo = bs.wo

    # Rotate into standard coordinates for specular reflection
    bi_s = ek.normalize(ek.cross(n, -wi))
    bi_p = ek.normalize(ek.cross(-wi, bi_s))
    bo_s = ek.normalize(ek.cross(n, wo))
    bo_p = ek.normalize(ek.cross(wo, bi_s))

    M_local = rotate_mueller_basis(M_local, -wi, stokes_basis(-wi), bi_p, wo,
                                   stokes_basis(wo), bo_p)

    # Apply to unpolarized light and verify that it is equivalent to normal Fresnel
    a0 = M_local @ spectrum_from_stokes([1, 0, 0, 0])
    F = fresnel_conductor(ek.cos(theta_i),
                          ek.scalar.Complex2f(eta.real, eta.imag))
    a0 = a0[0, 0]
    assert ek.allclose(a0[0], F, atol=1e-3)

    # Apply to horizontally polarized light (linear at 0˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still horizontally polarized
    a1 = M_local @ spectrum_from_stokes([1, +1, 0, 0])
    assert ek.allclose(a1[0, 0], ek.abs(a1[1, 0]), atol=1e-3)  # 1)
    a1 /= a1[0, 0]
    assert ek.allclose(a1, spectrum_from_stokes([1, 1, 0, 0]), atol=1e-3)  # 2)

    # Apply to vertically polarized light (linear at 90˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still vertically polarized
    a2 = M_local @ spectrum_from_stokes([1, -1, 0, 0])
    assert ek.allclose(a2[0, 0], ek.abs(a2[1, 0]), atol=1e-3)  # 1)
    a2 /= a2[0, 0]
    assert ek.allclose(a2, spectrum_from_stokes([1, -1, 0, 0]),
                       atol=1e-3)  # 2)

    # Apply to (positive) diagonally polarized light (linear at +45˚)
    # Test that..
    # 1) The polarization is flipped to -45˚
    # 2) There is now also some (left) circular polarization
    a3 = M_local @ spectrum_from_stokes([1, 0, +1, 0])
    assert ek.all(a3[2, 0] < UnpolarizedSpectrum(0.0))
    assert ek.all(a3[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to (negative) diagonally polarized light (linear at -45˚)
    # Test that..
    # 1) The polarization is flipped to +45˚
    # 2) There is now also some (right) circular polarization
    a4 = M_local @ spectrum_from_stokes([1, 0, -1, 0])
    assert ek.all(a4[2, 0] > UnpolarizedSpectrum(0.0))
    assert ek.all(a4[3, 0] > UnpolarizedSpectrum(0.0))

    # Apply to right circularly polarized light
    # Test that the polarization is flipped to left circular
    a5 = M_local @ spectrum_from_stokes([1, 0, 0, +1])
    assert ek.all(a5[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to left circularly polarized light
    # Test that the polarization is flipped to right circular
    a6 = M_local @ spectrum_from_stokes([1, 0, 0, -1])
    assert ek.all(a6[3, 0] > UnpolarizedSpectrum(0.0))
コード例 #8
0
def test02_sample_pol_world(variant_scalar_mono_polarized):
    from mitsuba.core import Frame3f, Spectrum, UnpolarizedSpectrum
    from mitsuba.core.xml import load_string
    from mitsuba.render import BSDFContext, TransportMode, SurfaceInteraction3f, fresnel_conductor
    from mitsuba.render.mueller import stokes_basis, rotate_mueller_basis

    def spectrum_from_stokes(v):
        res = Spectrum(0.0)
        for i in range(4):
            res[i, 0] = v[i]
        return res

    # Create a Silver conductor BSDF and reflect different polarization states
    # at a 45˚ angle.
    # This test takes place in world coordinates and thus involves additional
    # coordinate system rotations.
    #
    # The setup is as follows:
    # - The mirror is positioned at [0, 0, 0], angled s.t. the surface normal
    #   is along [1, 1, 0].
    # - Incident ray w1 = [-1, 0, 0] strikes the mirror at a 45˚ angle and
    #   reflects into direction w2 = [0, 1, 0]
    # - The corresponding Stokes bases are b1 = [0, 1, 0] and b2 = [1, 0, 0].

    # Setup
    eta = 0.136125 + 4.010625j  # IoR values of Ag at 635.816284nm
    bsdf = load_string("""<bsdf version='2.0.0' type='conductor'>
                              <spectrum name="eta" value="{}"/>
                              <spectrum name="k" value="{}"/>
                          </bsdf>""".format(eta.real, eta.imag))
    ctx = BSDFContext()
    ctx.mode = TransportMode.Importance
    si = SurfaceInteraction3f()
    si.p = [0, 0, 0]
    si.n = ek.normalize([1.0, 1.0, 0.0])
    si.sh_frame = Frame3f(si.n)

    # Incident / outgoing directions and their Stokes bases
    w1 = ek.scalar.Vector3f([-1, 0, 0])
    b1 = [0, 1, 0]
    w2 = ek.scalar.Vector3f([0, 1, 0])
    b2 = [1, 0, 0]

    # Perform actual reflection
    si.wi = si.to_local(-w1)
    bs, M_tmp = bsdf.sample(ctx, si, 0.0, [0.0, 0.0])
    M_world = si.to_world_mueller(M_tmp, -si.wi, bs.wo)

    # Test that outgoing direction is as predicted
    assert ek.allclose(si.to_world(bs.wo), w2, atol=1e-5)

    # Align reference frames s.t. polarization is expressed w.r.t. b1 & b2
    M_world = rotate_mueller_basis(M_world, w1, stokes_basis(w1), b1, w2,
                                   stokes_basis(w2), b2)

    # Apply to unpolarized light and verify that it is equivalent to normal Fresnel
    a0 = M_world @ spectrum_from_stokes([1, 0, 0, 0])
    F = fresnel_conductor(si.wi[2], ek.scalar.Complex2f(eta.real, eta.imag))
    a0 = a0[0, 0]
    assert ek.allclose(a0[0], F, atol=1e-3)

    # Apply to horizontally polarized light (linear at 0˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still horizontally polarized
    a1 = M_world @ spectrum_from_stokes([1, +1, 0, 0])
    assert ek.allclose(a1[0, 0], ek.abs(a1[1, 0]), atol=1e-3)  # 1)
    a1 /= a1[0, 0]
    assert ek.allclose(a1, spectrum_from_stokes([1, 1, 0, 0]), atol=1e-3)  # 2)

    # Apply to vertically polarized light (linear at 90˚)
    # Test that it is..
    # 1) still fully polarized (though with reduced intensity)
    # 2) still vertically polarized
    a2 = M_world @ spectrum_from_stokes([1, -1, 0, 0])
    assert ek.allclose(a2[0, 0], ek.abs(a2[1, 0]), atol=1e-3)  # 1)
    a2 /= a2[0, 0]
    assert ek.allclose(a2, spectrum_from_stokes([1, -1, 0, 0]),
                       atol=1e-3)  # 2)

    # Apply to (positive) diagonally polarized light (linear at +45˚)
    # Test that..
    # 1) The polarization is flipped to -45˚
    # 2) There is now also some (left) circular polarization
    a3 = M_world @ spectrum_from_stokes([1, 0, +1, 0])
    assert ek.all(a3[2, 0] < UnpolarizedSpectrum(0.0))
    assert ek.all(a3[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to (negative) diagonally polarized light (linear at -45˚)
    # Test that..
    # 1) The polarization is flipped to +45˚
    # 2) There is now also some (right) circular polarization
    a4 = M_world @ spectrum_from_stokes([1, 0, -1, 0])
    assert ek.all(a4[2, 0] > UnpolarizedSpectrum(0.0))
    assert ek.all(a4[3, 0] > UnpolarizedSpectrum(0.0))

    # Apply to right circularly polarized light
    # Test that the polarization is flipped to left circular
    a5 = M_world @ spectrum_from_stokes([1, 0, 0, +1])
    assert ek.all(a5[3, 0] < UnpolarizedSpectrum(0.0))

    # Apply to left circularly polarized light
    # Test that the polarization is flipped to right circular
    a6 = M_world @ spectrum_from_stokes([1, 0, 0, -1])
    assert ek.all(a6[3, 0] > UnpolarizedSpectrum(0.0))