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)
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)
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)
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)