def f(self, wo: 'geo.Vector', wi: 'geo.Vector') -> 'Spectrum': diffuse = (28. / (23. * PI)) * self.Rd * \ (Spectrum(1.) - self.Rs) * \ (1. - np.power(1. - .5 * abs_cos_theta(wi), 5)) * \ (1. - np.power(1. - .5 * abs_cos_theta(wo), 5)) wh = geo.normalize(wi + wo) specular = self.distribution.D(wh) / \ (4. * wi.abs_dot(wh) * max(abs_cos_theta(wi), abs_cos_theta(wo))) * \ self.schlick(wi.dot(wh)) return diffuse + specular
def sample_f(self, wo: 'geo.Vector', u1: FLOAT, u2: FLOAT) -> [FLOAT, 'geo.Vector', 'Spectrum']: # find direction wi = geo.Vector(-wo.x, -wo.y, wo.z) return [ 1., wi, self.fresnel(cos_theta(wo)) * self.R / abs_cos_theta(wi) ] # 1. suggests no MC samples needed
def pdf(self, wo: 'geo.Vector', wi: 'geo.Vector') -> FLOAT: wh = geo.normalize(wo + wi) ct = abs_cos_theta(wh) if wo.dot(wh) <= 0.: return 0. return ( (self.e + 1.) * np.power(ct, self.e)) / (2. * PI * 4. * wo.dot(wh))
def pdf(self, wo: 'geo.Vector', wi: 'geo.Vector') -> FLOAT: """ pdf() Returns pdf of given direction """ if wo.z * wi.z > 0.: # same hemisphere return abs_cos_theta(wi) * INV_PI return 0.
def f(self, wo: 'geo.Vector', wi: 'geo.Vector') -> 'Spectrum': st_i = sin_theta(wi) st_o = sin_theta(wo) # \cos(\phi_i - \phi_o) mc = 0. if st_i > EPS and st_o > EPS: dcos = cos_phi(wi) * cos_phi(wo) + sin_phi(wi) * sin_phi(wo) mc = max(0., dcos) # \sin \alpah \tan \beta if abs_cos_theta(wi) > abs_cos_theta(wo): sa = st_o tb = st_i / abs_cos_theta(wi) else: sa = st_i tb = st_o / abs_cos_theta(wo) return self.R * INV_PI * (self.A + self.B * mc * sa * tb)
def D(self, wh: 'geo.Vector') -> FLOAT: cth = abs_cos_theta(wh) d = 1. - cth * cth if d == 0.: return 0. e = (self.ex * wh.x * wh.x + self.ey * wh.y * wh.y) / d return np.sqrt( (self.ex + 2.) * (self.ey + 2.)) * INV_2PI * np.power(cth, e)
def pdf(self, wo: 'geo.Vector', wi: 'geo.Vector') -> FLOAT: wh = geo.normalize(wo + wi) ct = abs_cos_theta(wh) ds = 1. - ct * ct if ds > 0. and wo.dot(wh) > 0.: return (np.sqrt((self.ex + 1.) * (self.ey + 1.)) * INV_2PI * np.power(ct, ( self.ex * wh.x * wh.x + self.ey * wh.y * wh.y) / ds)) / \ (4. * wo.dot(wh)) else: return 0.
def rho_hh(self, nSamples: INT, samples_1: [FLOAT], samples_2: [FLOAT]) -> 'Spectrum': """ Computs hemispherical-hemispherical reflectance function. - samples_1, samples_2 2d np array """ r = Spectrum(0.) for i in range(nSamples): wo = mc.uniform_sample_hemisphere(samples_1[i][0], samples_1[i][1]) pdf_o = INV_2PI pdf_i, wi, f = self.sample_f(wo, samples_2[i][0], samples_2[i][1]) if pdf_i > 0.: r += f * abs_cos_theta(wi) * abs_cos_theta(wo) / (pdf_o * pdf_i) r /= (PI * nSamples) return r
def rho_hd(self, w: 'geo.Vector', samples: [FLOAT]) -> 'Spectrum': """ Computes hemispherical-directional reflectance function. - w Incoming 'geo.Vector' - samples 2d np array """ r = Spectrum(0.) for smp in samples: pdf, wi, f = self.sample_f(w, smp[0], smp[1]) if pdf > 0.: r += f * abs_cos_theta(wi) / pdf r /= len(samples) return r
def sample_f(self, wo: 'geo.Vector', u1: FLOAT, u2: FLOAT) -> [FLOAT, 'geo.Vector', 'Spectrum']: # find eta pair ei, et = self.ei, self.et if cos_theta(wo) > 0.: ei, et = et, ei # compute transmited ray direction si_sq = sin_theta_sq(wo) eta = ei / et st_sq = eta * eta * si_sq if st_sq >= 1.: return [0., None, None] ct = np.sqrt(max(0., 1. - st_sq)) if cos_theta(wo) > 0.: ct = -ct wi = geo.Vector(eta * (-wo.x), eta * (-wo.y), ct) F = self.fresnel(cos_theta(wo)) return [1., wi, (et * et) / (ei * ei) * (Spectrum(1.) - F) * \ self.T / abs_cos_theta(wi)] # 1. suggests no MC samples needed
def sample_f(self, wo: 'geo.Vector', u1: FLOAT, u2: FLOAT) -> [FLOAT, 'geo.Vector']: # sample from first quadrant and remap to hemisphere to sample w_h if u1 < .25: phi, ct = self.__sample_first_quad(4. * u1, u2) elif u1 < .5: u1 = 4. * (.5 - u1) phi, ct = self.__sample_first_quad(u1, u2) phi = PI - phi elif u1 < .75: u1 = 4. * (u1 - .5) phi, ct = self.__sample_first_quad(u1, u2) phi += PI else: u1 = 4. * (1. - u1) phi, ct = self.__sample_first_quad(u1, u2) phi = 2. * PI - phi st = np.sqrt(max(0., 1. - ct * ct)) wh = geo.spherical_direction(st, ct, phi) if wo.z * wh.z <= 0.: wh *= -1. # incident direction by reflection wi = -wo + 2. * wo.dot(wh) * wh # compute pdf for w_i ct = abs_cos_theta(wh) ds = 1. - ct * ct if ds > 0. and wo.dot(wh) > 0.: return [(np.sqrt((self.ex + 1.) * (self.ey + 1.)) * INV_2PI * np.power(ct, ( self.ex * wh.x * wh.x + self.ey * wh.y * wh.y) / ds)) / \ (4. * wo.dot(wh)), wi] else: return [0., wi]
def pdf(self, wo: 'geo.Vector', wi: 'geo.Vector') -> FLOAT: if wo.z * wi.z <= 0.: return 0. return .5 * (abs_cos_theta(wi) * INV_PI + self.distribution.pdf(wo, wi))
def D(self, wh: 'geo.Vector') -> FLOAT: return (self.e + 2) * INV_2PI * np.power(abs_cos_theta(wh), self.e)
def G(self, wo: 'geo.Vector', wi: 'geo.Vector', wh: 'geo.Vector') -> FLOAT: return min( 1., min((2. * abs_cos_theta(wh) * abs_cos_theta(wo) / wo.abs_dot(wh)), (2. * abs_cos_theta(wh) * abs_cos_theta(wi) / wo.abs_dot(wh))))