File ElectricField.h¶
File List > Intern > rayx-core > src > Shader > ElectricField.h
Go to the documentation of this file
#pragma once
#include "Complex.h"
#include "Constants.h"
namespace rayx {
using Stokes = glm::dvec4;
using ElectricField = cvec3;
using LocalElectricField = cvec2;
static_assert(std::is_default_constructible_v<ElectricField>);
static_assert(std::is_default_constructible_v<LocalElectricField>);
#ifndef GLM_FORCE_XYZW_ONLY
#error "GLM_FORCE_XYZW_ONLY should be defined, to enable default construction of ElectricField"
#endif
RAYX_FN_ACC
inline double intensity(const LocalElectricField field) {
const auto mag = complex::abs(field);
return glm::dot(mag, mag);
}
RAYX_FN_ACC
inline double intensity(const ElectricField field) {
const auto mag = complex::abs(field);
return glm::dot(mag, mag);
}
RAYX_FN_ACC
inline double intensity(const Stokes stokes) { return stokes.x; }
RAYX_FN_ACC
inline double degreeOfPolarization(const Stokes stokes) { return glm::length(glm::dvec3(stokes.y, stokes.z, stokes.w)) / stokes.x; }
RAYX_FN_ACC
inline ElectricField advanceElectricField(const ElectricField field, double waveLength, const double opticalPathLength) {
// bring wavelength from nanometers into millimeters
waveLength /= 1e6;
// compute wave number (2π / λ), since opticalPathLength already includes IOR
const double waveNumber = 2.0 * PI / waveLength;
// reduce the distance modulo wavelength to avoid large angle errors
const double reducedDistance = std::fmod(opticalPathLength, waveLength);
// compute the phase shift
const double deltaPhi = waveNumber * reducedDistance;
// apply the phase shift as a complex exponential
const auto phaseShift = complex::exp(complex::Complex(0.0, deltaPhi));
return field * phaseShift;
}
/*
* rotation of electric field
*/
struct RotationBase {
glm::dvec3 right;
glm::dvec3 up;
glm::dvec3 forward;
};
// Computes a base given a forward vector
// A convention for the up and right vectors is implemented, making this function well defined and for all forward directions
// TODO(Sven): this convention should be exchanged with one that does not branch
RAYX_FN_ACC
inline RotationBase forwardVectorToBaseConvention(const glm::dvec3 forward) {
glm::dvec3 up;
glm::dvec3 right;
// test if the forward vector is close to being vertical
const auto close_to_vertical = glm::abs(glm::dot(forward, glm::dvec3(0, 1, 0))) > .5;
// if the forward vector is not close to being vertical, we initialize the up vector to (0, 1, 0)
if (!close_to_vertical) {
up = glm::dvec3(0, 1, 0);
right = glm::normalize(glm::cross(up, forward));
up = glm::normalize(glm::cross(forward, right));
}
// otherwise initialize the right vector to (1, 0, 0)
else {
right = glm::dvec3(1, 0, 0);
up = glm::normalize(glm::cross(forward, right));
right = glm::normalize(glm::cross(up, forward));
}
return RotationBase{
.right = right,
.up = up,
.forward = forward,
};
}
// Computes a rotation matrix given a forward vector. This matrix can be used to align an object with a direction
RAYX_FN_ACC
inline glm::dmat3 rotationMatrixWithBaseConvention(const glm::dvec3 forward) {
const auto base = forwardVectorToBaseConvention(forward);
return glm::dmat3(base.right, base.up, base.forward);
}
RAYX_FN_ACC
inline glm::dmat3 rotationMatrix(const glm::dvec3 forward, const glm::dvec3 up) {
const auto right = glm::cross(up, forward);
return glm::dmat3(right, up, forward);
}
/*
* conversion between local and global electric field
*/
RAYX_FN_ACC
inline ElectricField localToGlobalElectricFieldWithBaseConvention(const LocalElectricField localField, const glm::dvec3 forward) {
return rotationMatrixWithBaseConvention(forward) * ElectricField(localField, complex::Complex{0, 0});
}
RAYX_FN_ACC
inline ElectricField localToGlobalElectricField(const LocalElectricField localField, const glm::dvec3 forward, const glm::dvec3 up) {
return rotationMatrix(forward, up) * ElectricField(localField, complex::Complex{0, 0});
}
RAYX_FN_ACC
inline ElectricField localToGlobalElectricField(const LocalElectricField localField, const glm::dmat3 rotation) {
return rotation * ElectricField(localField, complex::Complex{0, 0});
}
RAYX_FN_ACC
inline LocalElectricField globalToLocalElectricFieldWithBaseConvention(const ElectricField field, const glm::dvec3 forward) {
return glm::transpose(rotationMatrixWithBaseConvention(forward)) * field;
}
RAYX_FN_ACC
inline LocalElectricField globalToLocalElectricField(const ElectricField field, const glm::dvec3 forward, const glm::dvec3 up) {
return glm::transpose(rotationMatrix(forward, up)) * field;
}
RAYX_FN_ACC
inline LocalElectricField globalToLocalElectricField(const ElectricField field, const glm::dmat3 rotation) { return rotation * field; }
/*
* conversion between stokes and electric field
*/
RAYX_FN_ACC
inline Stokes localElectricFieldToStokes(const LocalElectricField field) {
const auto mag = complex::abs(field);
const auto theta = complex::arg(field);
return Stokes(mag.x * mag.x + mag.y * mag.y, mag.x * mag.x - mag.y * mag.y, 2.0 * mag.x * mag.y * glm::cos(theta.x - theta.y),
2.0 * mag.x * mag.y * glm::sin(theta.x - theta.y));
}
RAYX_FN_ACC
inline LocalElectricField stokesToLocalElectricField(const Stokes stokes) {
const auto x_real = glm::sqrt((stokes.x + stokes.y) / 2.0);
const auto y_mag = glm::sqrt((stokes.x - stokes.y) / 2.0);
const auto y_theta = -1.0 * glm::atan(stokes.w, stokes.z);
const auto y = complex::polar(y_mag, y_theta);
return LocalElectricField({x_real, 0}, y);
}
RAYX_FN_ACC
inline ElectricField stokesToElectricFieldWithBaseConvention(const Stokes stokes, const glm::dvec3 forward) {
return localToGlobalElectricFieldWithBaseConvention(stokesToLocalElectricField(stokes), forward);
}
RAYX_FN_ACC
inline ElectricField stokesToElectricField(const Stokes stokes, const glm::dvec3 forward, const glm::dvec3 up) {
return localToGlobalElectricField(stokesToLocalElectricField(stokes), forward, up);
}
RAYX_FN_ACC
inline ElectricField stokesToElectricField(const Stokes stokes, const glm::dmat3 rotation) {
return rotation * ElectricField(stokesToLocalElectricField(stokes), complex::Complex{0, 0});
}
RAYX_FN_ACC
inline ElectricField electricFieldToStokesWithBaseConvention(const ElectricField field, const glm::dvec3 forward) {
return localElectricFieldToStokes(globalToLocalElectricFieldWithBaseConvention(field, forward));
}
RAYX_FN_ACC
inline ElectricField electricFieldToStokes(const ElectricField field, const glm::dvec3 forward, const glm::dvec3 up) {
return localElectricFieldToStokes(globalToLocalElectricField(field, forward, up));
}
RAYX_FN_ACC
inline ElectricField electricFieldToStokes(const ElectricField field, const glm::dmat3 rotation) {
return localElectricFieldToStokes(globalToLocalElectricField(field, rotation));
}
} // namespace rayx