from __future__ import annotations
import numpy as np
import numpy.random as npr
import arlunio as ar
from .data import Rays
[docs]@ar.definition
def SimpleSampler(width: int, height: int):
"""The simplest sampler."""
u = np.full((height, width), np.linspace(0, 1, width))
v = np.linspace(1, 0, height).reshape(1, height).transpose()
v = np.full((height, width), v)
return np.dstack([u, v]).reshape(width * height, 2)
[docs]@ar.definition
def SimpleCamera(
width: int,
height: int,
*,
origin=np.array([0.0, 0.0, 0.0]),
scale=2.0,
focal_length=1.0,
sampler=None
) -> Rays:
"""A simple camera that generates rays to be cast into the scene.
Attributes
----------
focal_length:
Something something focal length
origin:
Set the position of the camera in space. If not set this will default to
:math:`(0, 0, 0)`
sampler:
The sampler the camera should use in order to generate rays. If not set this
will default to an instance of :class:`arlunio.raytrace.UniformSampler`
scale:
Something something scale.
"""
# Pick sensible defaults if some attributes are not set
origin = np.array(origin)
sampler = UniformSampler() if sampler is None else sampler
ratio = width / height
# Not entirely sure how to describe the effect these have on the final image...
horizontal = np.array([scale * ratio, 0.0, 0.0])
vertical = np.array([0.0, scale, 0.0])
uv = sampler(width=width, height=height)
lower_left = (
origin - (horizontal / 2) - (vertical / 2) - np.array([0, 0, focal_length])
)
n = uv.shape[0]
hs = np.einsum("n,np->np", uv[:, 0], horizontal.reshape(1, 3))
vs = np.einsum("n,np->np", uv[:, 1], vertical.reshape(1, 3))
ll = np.full((n, 3), lower_left)
origin = np.full((n, 3), origin)
directions = ll + hs + vs
return Rays(origin, directions)