Source code for padvinder.material

#!/usr/bin/python
# -*- coding: latin-1 -*-
"""
Materials define the surface properties and specify how light rays get coloured and reflected. All materials are callables - they implement the __call__ method
and can be used like functions.

.. moduleauthor:: Adrian Köring
"""

import numpy as np

from padvinder.util import normalize
from padvinder.util import check_finite

[docs]class Material(object): """ An emission material consists of an emitted colour only. Without gradients, lighting or anything. Parameters ---------- color : numpy.ndarray_like of three dimensions and contains colors as (Red, Green, Blue) where (0,0,0) is black and (1,1,1) is white Raises ------ ValueError if the color contains any non-finite (inf, nan) values Examples -------- >>> Material((0.8, 0.8, 0.8)) Material(color=[.8 .8 .8]) """ def __init__(self, color = (.5, .5, .5)): check_finite(color) self._color = np.array(color).astype(np.float64) @property def color(self): """ Returns the color of the material. """ return self._color
[docs] def __call__(self, surface_normal, incoming_color, incoming_direction, outgoing_direction): """ Calculate light reflected from the material toward the outgoing direction. Keep in mind, while pathtracing starts at the camera and heads into the scene, the rays contribution is accumulated 'backwards'. Therefore the incoming direction is further down the path and outgoing_direction is closer towards the camera. Parameters ---------- surface_normal : numpy.ndarray_like normal vector at the geometries surface incoming_color : numpy.ndarray_like the color the ray has accumulated up to this point incoming_direction : numpy.ndarray_like the direction from where the 'light shines' onto the surface outgoing_direction : numpy.ndarray_like the direction into which the 'light gets reflected' from the surface Returns ------- color : numpy.ndarray_like the light color 'getting reflected' from the surface """ return self._color
[docs] def outgoing_direction(self, normal, incoming_direction): """ Given a surface normal and an incoming direction, determine the direction in which the path continues. normal : numpy.ndarray_like of shape (3, ) the surface normal at the intersection point incoming_direction : numpy.ndarray_like of shape (3, ) the direction from which light hits the surface Returns ------- outgoing direction : numpy.ndarray_like of shape (3, 0) the direction in which light is reflected from the surface """ # BaseClass randomly (not uniformly) samples the hemisphere point_on_sphere = normalize(np.random.uniform(0, 1, size=(3, ))) # ensure it is in the same hemisphere as the normal if np.dot(normal, point_on_sphere) < 0: point_on_sphere = -point_on_sphere return normalize(normal + point_on_sphere)
def __repr__(self): return "Material(color={})".format(self._color)
[docs]class Emission(Material): """ Emission is equivalent to the abstract base class Material. Due to semantics this class exists and merely inherits without modifications. Parameters ---------- color : numpy.ndarray_like of three dimensions and contains colors as (Red, Green, Blue) where (0,0,0) is black and (1,1,1) is white Raises ------ ValueError if the color contains any non-finite (inf, nan) values Examples -------- >>> Emission() Emission(color=[10.0, 10.0, 10.0]) """ def __init__(self, color = (10, 10, 10)): super().__init__(color) def __repr__(self): return "Emission(color={})".format(self._color)
[docs]class Lambert(Material): def __init__(self, color = (0.5, 0.5, 0.5), diffuse = 1): """ A lambert material consists of a colour value and a diffuse coefficient. Parameters ---------- color : numpy.ndarray_like of three dimensions and contains colors as (Red, Green, Blue) where (0,0,0) is black and (1,1,1) is white diffuse : number in [0, 1] percentage of incoming light that is reflected again Raises ------ ValueError if the color contains any non-finite (inf, nan) values Examples -------- >>> Lambert((0.8, 0.8, 0.8), 1) Lambert(color=[0.8, 0.8, 0.8], diffuse=1) """ super().__init__(color) self._diffuse = diffuse @property def diffuse(self): """ Returns the diffuse value of the material. """ return self._diffuse # def __call__(self, surface_normal, # incoming_light, # incoming_direction, # outgoing_direction): # """ # Calculate light reflected from the material toward the outgoing # direction. Keep in mind, while pathtracing starts at the camera and # heads into the scene, the rays contribution is accumulated 'backwards'. # Therefore the incoming direction is further down the path and # outgoing_direction is closer towards the camera. # # Parameters # ---------- # surface_normal : numpy.ndarray_like # normal vector at the geometries surface # incoming_color : numpy.ndarray_like # the color the ray has accumulated up to this point # incoming_direction : numpy.ndarray_like # the direction from where the 'light shines' onto the surface # outgoing_direction : numpy.ndarray_like # the direction into which the 'light gets reflected' from the surface # # Returns # ------- # color : numpy.ndarray_like # the light color 'getting reflected' from the surface # """ # raise NotImplemented() def __repr__(self): c, d = self._color, self._diffuse return "Lambert(color={0}, diffuse={1})".format(c, d)