The SKIRT project
advanced radiative transfer for astrophysics
The perspective instrument

Introduction

The PerspectiveInstrument class in SKIRT implements a full perspective view of the simulated model, with arbitrary placement of the viewport outside or inside the model. Only photon packets arriving from the front are recorded; light emitted behind the viewport is ignored. For each wavelength the instrument outputs the detected surface brightness in every pixel of a given frame as a data cube in a FITS file. The instrument does not support recording of spatially integrated flux densities.

The perspective instrument is intended mostly for making movies. Each movie frame is generated by a separate perspective instrument with the appropriate parameters.

Parameters and coordinate systems

The following table lists all parameters for the perspective instrument other than the options common to all instrument, which obviously have no effect on the perspective transformation. Positions and sizes are specified in the right-handed world coordinate system, i.e. the coordinate system in which the SKIRT model is defined, using units of length such as parsec. Usually the model is centered around the origin \(\mathbf{O}\) of the world coordinate system.

Parameter(s) Description
\(N_{x},N_{y}\) The number of pixels in the viewport in the local \(x\) and \(y\) directions
\(S_{x},S_{y}\) The size (twice the extent) of the viewport in the local \(x\) and \(y\) directions
\(V_{x},V_{y},V_{z}\) The position of the viewport origin \(\mathbf{V}\)
\(C_{x},C_{y},C_{z}\) The crosshair position \(\mathbf{C}\) that determines the orientation of the viewport's \(z\) axis
\(U_{x},U_{y},U_{z}\) The upwards position \(\mathbf{U}\) that determines the orientation of the viewport's \(y\) axis
\(F_{e}\) The focal length that determines the position of the eye

The aspect ratio of the viewport is assumed to be the same when calculated in pixels ( \(N_{y}/N_{x}\)) or in world size ( \(S_{y}/S_{x}\)). If not the perspective transformation will introduce a distortion.

The viewport is placed in the \(xy\) plane of the left-handed viewport coordinate system (left-handed so that depth increases along the \(z\)-axis). Both the viewport and the coordinate system are centered around the viewport origin \(\mathbf{V}\). The \(z\) axis is placed along the crosshair line \(\mathbf{\overline{VC}}\), i.e. the line connecting the viewport origin with the crosshair position, in the direction of \(\mathbf{C}\). Consequently the viewport is perpendicular to \(\overline{\mathbf{VC}}\). The \(y\) axis is placed such that it is parallel with the upwards line \(\overline{\mathbf{OU}}\), i.e. the line connecting the world origin with the upwards position, and in the same direction.

The eye and the origin of the left-handed eye coordinate system are placed on the crosshair line \(\overline{\mathbf{VC}}\) at a distance \(F_{e}\) from the viewport origin \(\mathbf{V}\), at the opposite side from \(\mathbf{C}\). In other words, the eye is behind the viewport and looks towards the crosshair position through the viewport. The axes of the eye coordinate system have the same orientation as those of the viewport coordinate system. The perspective transformation occurs from the eye to the viewport system.

Derived positions and directions

We denote the norm of an arbitrary point or vector \(\mathbf{A}\) as

\[ \left\Vert \mathbf{A}\right\Vert =\sqrt{A_{x}^{2}+A_{y}^{2}+A_{z}^{2}} \]

A unit vector in the direction from the crosshair position to the viewport origin can then be written as

\[ \mathbf{G}=\frac{\mathbf{V}-\mathbf{C}}{\left\Vert \mathbf{V}-\mathbf{C}\right\Vert } \]

and the eye position \(\mathbf{E}\) becomes

\[ \mathbf{E}=\mathbf{V}+F_{e}\mathbf{G} \]

When a peel-off photon packet is launched towards the instrument from position \(\mathbf{P}\), a unit vector with the appropriate direction is given by

\[ \mathbf{D}=\frac{\mathbf{E}-\mathbf{P}}{\left\Vert \mathbf{E}-\mathbf{P}\right\Vert } \]

Homogeneous coordinates

We use homogeneous coordinates \((x,y,z,w)\) with arbitrary scale factor \(w\). An arbitary point \(\mathbf{P}=(P_{x},P_{y},P_{z})\) can be represented in homogeneous coordinates as \(\mathcal{P}=(P_{x},P_{y},P_{z},1)\). An arbitrary direction \(\mathbf{D}=(D_{x},D_{y},D_{z})\) is represented as a point at infinity \(\mathcal{D}=(D_{x},D_{y},D_{z},0)\).

Vice versa, given the homogeneous coordinates \(\mathcal{Q}=(Q_{x},Q_{y},Q_{z},Q_{w})\), the corresponding regular coordinates for the point can be retrieved as \(\mathbf{Q}=(Q_{x}/Q_{w},Q_{y}/Q_{w},Q_{z}/Q_{w})\) provided \(Q_{w}\neq0\).

A homogeneous coordinate transformation can be written as \(\mathcal{P}'=\mathcal{P}\,\mathcal{T}\) where \(\mathcal{T}\) is a \(4\times4\) transformation matrix which can represent a scaling, rotation, translation, perspective transformation or any combination thereof; or more explicitly

\[ \left[\begin{array}{cccc} x' & y' & z' & w'\end{array}\right]=\left[\begin{array}{cccc} x & y & z & w\end{array}\right]\left[\begin{array}{cccc} t_{11} & t_{12} & t_{13} & t_{14}\\ t_{21} & t_{22} & t_{23} & t_{24}\\ t_{31} & t_{32} & t_{33} & t_{34}\\ t_{41} & t_{42} & t_{43} & t_{44} \end{array}\right] \]

Consecutive transformations can be combined into a single transformation matrix through regular matrix multiplication.

The instrument transformation

When a peel-off photon packet launched from position \(\mathbf{P}\) arrives at the perspective instrument, a single transformation is applied to find the homogeneous coordinates of the pixel receiving the photon packet's luminosity

\[ \mathcal{J}=\mathcal{P}\,\mathcal{T}_{\mathrm{tot}} \]

where \(\mathcal{P}=(P_{x},P_{y},P_{z},1)\) and \(\mathcal{T}_{\mathrm{tot}}\) represents the composite transformation from world coordinates to pixel coordinates which will be developed below.

The incoming photon packet's luminosity is taken into account only if the packet originated in front of the viewport and if the pixel indices are both within range (from zero up to the number of pixels in the relevant direction minus one). The pixel indices can be retrieved using

\begin{eqnarray*} i & = & \mathrm{floor}(\mathcal{J}_{x}/\mathcal{J}_{w})\\ j & = & \mathrm{floor}(\mathcal{J}_{y}/\mathcal{J}_{w}) \end{eqnarray*}

based on the standard mechanism for converting homogeneous to regular coordinates. The perspective transformation developed below is such that the distance \(d\) from the launching position \(\mathbf{P}\) to the the viewport plane is given by

\[ d=\left|\mathcal{J}_{z}\right| \]

and \(\mathbf{P}\) is in front of the viewport if and only if \(\mathcal{J}_{z}>0\). In this case there is no division by \(\mathcal{J}_{w}\) to avoid distortion of the distance scale by the perspective transformation.

Developing the transformation

At the highest level we compose the instrument transformation as follows

\[ \mathcal{T}_{\mathrm{tot}}=\mathcal{T}_{we}\,\mathcal{T}_{ev}\,\mathcal{T}_{vp} \]

where

  • \(\mathcal{T}_{we}\) transforms from world coordinates to eye coordinates
  • \(\mathcal{T}_{ev}\) transforms from eye coordinates to viewport coordinates
  • \(\mathcal{T}_{vp}\) transforms from viewport coordinates to pixel coordinates

From world to eye coordinates

The transformation from world to eye coordinates can be composed as follows

\[ \mathcal{T}_{we}=\mathcal{M}_{e}\,\mathcal{R}_{xy}\,\mathcal{R}_{z}\,\mathcal{F}_{z} \]

with from left to right:

  • translate the origin to the eye position;
  • rotate around the \(x\) and \(y\) axes to align the \(z\) axis with the crosshair line \(\overline{\mathbf{CV}}\), pointing in the direction of the eye; the \(xy\) plane is now parallel with the viewport;
  • rotate around the \(z\) axis to align the \(y\) axis so that it is parallel with and in the same direction as the projection on the \(xy\) plane of the upwards line \(\overline{\mathbf{OU}}\); the \(y\) axis now points up and the \(x\) axis now points to the right when looking down from the positive \(z\) axis;
  • flip the direction of the \(z\) axis so that depth increases away from the eye.

It is easy to see that

\[ \mathcal{M}_{e}=\left[\begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ -E_{x} & -E_{y} & -E_{z} & 1 \end{array}\right]\qquad\mathrm{and}\qquad\mathcal{F}_{z}=\left[\begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & -1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right] \]

To determine \(\mathcal{R}_{xy}\) let \((a,b,c)\) be the direction cosines of the desired \(z\) axis alignment, i.e. the direction \(\mathbf{G}\) from the crosshair position to the eye position

\[ (a,b,c)=(G_{x},G_{y},G_{z}) \]

and define \(v=\sqrt{b^{2}+c^{2}}\) as the hypothenuse of the direction vector's projection in the \(yz\) plane. For the time being, assume that \(v\neq0\).

Alignment of the \(z\) axis with \((a,b,c)\) can then be accomplished by a rotation over \(\alpha\) about the \(x\) axis (figure (a)) followed by a rotation over \(\beta\) about the \(y\) axis (figure (b)) – the angles are directed clockwise when looking down from the positive rotation axis. The projection of vector \((a,b,c)\) in the \(yz\) plane has components \(b\) along the \(y\) axis and \(c\) along the \(z\) axis, so that \(\cos\alpha=c/v\) and \(\sin\alpha=-b/v\). The situation after the first rotation is shown in figure (b). The length of the hypothenuse in this figure is \(1\) since the direction cosines \((a,b,c)\) are normalized. It is then immediately clear that \(\cos\beta=v\) and \(\sin\beta=-a\). Thus we find

\[ \mathcal{R}_{xy}=\left[\begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & {c}/{v} & {b}/{v} & 0\\ 0 & -\,{b}/{v} & {c}/{v} & 0\\ 0 & 0 & 0 & 1 \end{array}\right]\left[\begin{array}{cccc} v & 0 & a & 0\\ 0 & 1 & 0 & 0\\ -a & 0 & v & 0\\ 0 & 0 & 0 & 1 \end{array}\right] \]

To determine \(\mathcal{R}_{z}\), let \((k,l,m)\) be the direction cosines of the desired \(y\) axis alignment, i.e. the direction from the world origin to the upwards position, after partial transformation to eye coordinates

\[ \left[\begin{array}{cccc} k & l & m & 1\end{array}\right]=\left[\begin{array}{cccc} U_{x} & U_{y} & U_{z} & 1\end{array}\right]\,\mathcal{R}_{xy} \]

so that the projection of the upwards direction on the eye \(xy\) plane has components \(k\) along the \(x\) axis and \(l\) along the \(y\) axis. Using the previously derived value for \(\mathcal{R}_{xy}\) simple matrix algebra leads to

\[ k=(b^{2}+c^{2})U_{x}-abU_{y}-acU_{z}\qquad\mathrm{and}\qquad l=cU_{y}-bU_{z} \]

where we dropped a proportionality factor of \(1/v\) in both values, since \(k\) and \(l\) are not normalized.

We need a rotation over \(\gamma\) about the \(z\) axis (figure (c)). Defining \(u=\sqrt{k^{2}+l^{2}}\), a reasoning similar to the one before gives \(\cos\gamma=l/u\) and \(\sin\gamma=-k/u\), and thus

\[ \mathcal{R}_{z}=\left[\begin{array}{cccc} {l}/{u} & {k}/{u} & 0 & 0\\ -\,{k}/{u} & {l}/{u} & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right] \]

In case \(v\approx0\) the matrices \(\mathcal{R}_{xy}\) and \(\mathcal{R}_{z}\) as derived above are not well defined and the rotations to align the \(z\) axis should be performed in reverse order, i.e. first about the \(y\) axis and then about the \(x\) axis. A reasoning similar to the above leads to the alternative

\[ v'=\sqrt{a^{2}+c^{2}} \]

\[ \mathcal{R}'_{xy}=\left[\begin{array}{cccc} {c}/{v'} & 0 & {a}/{v'} & 0\\ 0 & 1 & 0 & 0\\ -\,{a}/{v'} & 0 & {c}/{v'} & 0\\ 0 & 0 & 0 & 1 \end{array}\right]\left[\begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & v' & b & 0\\ 0 & -b & v' & 0\\ 0 & 0 & 0 & 1 \end{array}\right] \]

\[ k'=cU_{x}-aU_{z} \]

\[ l'=(a^{2}+c^{2})U_{y}-abU_{x}-bcU_{z} \]

\[ u'=\sqrt{k'^{2}+l'^{2}} \]

\[ \mathcal{R}'_{z}=\left[\begin{array}{cccc} {l'}/{u'} & {k'}/{u'} & 0 & 0\\ -\,{k'}/{u'} & {l'}/{u'} & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right] \]

From eye to viewport coordinates

The figure shows the \(yz\) plane of the eye and viewport coordinate systems. To develop the perspective transformation, note that the triangles \(EP'V\) and \(EPQ\) are similar so that \(\frac{y_{v}}{F_{e}}=\frac{y_{e}}{z_{e}}\). With similar reasoning in the \(xz\) plane, we get

\begin{eqnarray*} x_{v} & = & F_{e}\frac{x_{e}}{z_{e}}\\ y_{v} & = & F_{e}\frac{y_{e}}{z_{e}} \end{eqnarray*}

In the \(z\) direction we wish to preserve the distance scale and simply perform the translation

\[ z_{v}=z_{e}-F_{e} \]

This can be accomplished through the homogeneous transformation matrix

\[ \mathcal{T}_{ev}=\left[\begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 1/F_{e}\\ 0 & 0 & -F_{e} & 0 \end{array}\right] \]

keeping in mind that the \(x_{v}\) and \(y_{v}\) coordinates will be divided by \(w_{v}=z_{e}/F_{e}\) when converting from homogeneous to regular coordinates, and assuming that the \(z_{v}\) coordinate will not be divided by this factor. This `‘trick’' for the \(z\) coordinate works only because we have set \(w=1\) when converting the original point to homogeneous coordinates, and none of the other transformation matrices change the \(w\) coordinate.

From viewport to pixel coordinates

The viewport is centered on the origin and is scaled in world distances, while pixel indices range from zero to the number of pixels in each direction minus one. Thus we need to apply the following scaling and translation

\[ \mathcal{T}_{vp}=\left[\begin{array}{cccc} {N_{x}}/{S_{x}} & 0 & 0 & 0\\ 0 & {N_{y}}/{S_{y}} & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right]\left[\begin{array}{cccc} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ {N_{x}}/{2} & {N_{y}}/{2} & 0 & 1 \end{array}\right] \]

We leave the depth coordinate unchanged, which means it is still scaled in world distances, zero depth corresponds with the viewport plane, and positive depth indicates positions in front of the viewport.

Flux calibration

Each photon packet arriving at the instrument carries a certain fraction of luminosity \(L_\mathrm{pp}\) at a particular wavelength \(\lambda\). The contribution of the photon packet to the surface brightness \(f_{\lambda,_\mathrm{px}}\) of the target instrument pixel can be written as (also see the FluxRecorder class),

\[ f_{\lambda,_\mathrm{px}} = \frac{1}{\Delta\lambda}\frac{1}{4\pi d_\mathrm{pp}^{2}}\frac{1}{\Omega_\mathrm{px}} L_\mathrm{px} \]

where \(\Delta\lambda\) is the width of the wavelength bin containing the wavelength \(\lambda\), \(d_\mathrm{pp}\) is the distance from the photon packet's origin to the instrument, and \(\Omega_\mathrm{px}\) is the solid angle subtended by the target pixel as seen from the eye position.

This solid angle depends on the location of the pixel in the viewport. Pixels at the edges or in the corners of the viewport subtend a smaller solid angle than central pixels. The current implementation of the instrument, though, uses the solid angle of the central pixel for all pixels in the viewport. Let us examine the nature of the error introduced by this approximation. For simplicity, we assume a square viewport of size \(S \times S\) with \(N \times N\) square pixels and a distance from the viewport to the eye position (focal length) of \(F_\mathrm{e}\). Further assuming that \(N\) is uneven, the solid angle \(\Omega_0\) of the central viewport pixel is given by

\[ \Omega_0 = \left( 2\arctan\left(\frac{S}{2 N F_\mathrm{e}}\right) \right)^2. \]

It is straightforward to also calculate the solid angle \(\Omega_N\) of a pixel in of the corners of the viewport. The relative error \((\Omega_0 - \Omega_N)/\Omega_0\) is plotted in the figure below (as a percentage) as a function of the ratio \(S/F_\mathrm{e}\) of the viewport size and the focal length:

It is thus recommended to place the eye at a distance of at least twice the size of the viewpoint. In that case, the error in the flux calibration at the edges of the viewport is limited to about 10%. Conversely, if the focal distance becomes smaller than the viewport size, the error quickly rises to an unacceptable level.

Lastly, to avoid overly large contributions from extremely nearby objects, the instrument ignores any photon packets that originated at a distance to the viewport \(d<\frac{S}{10N}\). (Photon packets originating behind the viewport are ignored in any case).