Surface closures and light sampling now take both shading and geometric normals.

This will allow them to appropriately handle weirdness that comes
from the shading normal and geometric normal being different.
This commit is contained in:
Nathan Vegdahl 2017-07-30 19:17:32 -07:00
parent 05578a1240
commit 0481d931b9
6 changed files with 125 additions and 58 deletions

View File

@ -45,11 +45,12 @@ impl<'a> LightAccel for LightArray<'a> {
inc: Vector,
pos: Point,
nor: Normal,
nor_g: Normal,
sc: &SurfaceClosure,
time: f32,
n: f32,
) -> Option<(usize, f32, f32)> {
let _ = (inc, pos, nor, sc, time); // Not using these, silence warnings
let _ = (inc, pos, nor, nor_g, sc, time); // Not using these, silence warnings
assert!(n >= 0.0 && n <= 1.0);

View File

@ -134,6 +134,7 @@ impl<'a> LightAccel for LightTree<'a> {
inc: Vector,
pos: Point,
nor: Normal,
nor_g: Normal,
sc: &SurfaceClosure,
time: f32,
n: f32,
@ -160,7 +161,7 @@ impl<'a> LightAccel for LightTree<'a> {
let sin_theta_max2 = (r2 / dist2).min(1.0);
(1.0 - sin_theta_max2).sqrt()
};
sc.estimate_eval_over_solid_angle(inc, d, nor, cos_theta_max)
sc.estimate_eval_over_solid_angle(inc, d, nor, nor_g, cos_theta_max)
};
node_ref.energy() * inv_surface_area * approx_contrib

View File

@ -28,6 +28,7 @@ pub trait LightAccel {
inc: Vector,
pos: Point,
nor: Normal,
nor_g: Normal,
sc: &SurfaceClosure,
time: f32,
n: f32,

View File

@ -467,19 +467,21 @@ impl LightPath {
{
// Check if pdf is zero, to avoid NaN's.
if light_pdf > 0.0 {
// Calculate and store the light that will be contributed
// to the film plane if the light is not in shadow.
self.pending_color_addition = {
let material = closure.as_surface_closure();
let la = material.evaluate(
let attenuation = material.evaluate(
ray.dir,
shadow_vec,
idata.nor,
idata.nor_g,
self.wavelength,
);
light_color.e * la.e * self.light_attenuation /
(light_pdf * light_sel_pdf)
};
if attenuation.e.h_max() > 0.0 {
// Calculate and store the light that will be contributed
// to the film plane if the light is not in shadow.
self.pending_color_addition = light_color.e * attenuation.e *
self.light_attenuation /
(light_pdf * light_sel_pdf);
// Calculate the shadow ray for testing if the light is
// in shadow or not.
@ -502,6 +504,9 @@ impl LightPath {
}
} else {
false
}
} else {
false
};
// Prepare bounce ray
@ -513,11 +518,17 @@ impl LightPath {
let material = closure.as_surface_closure();
let u = self.next_lds_samp();
let v = self.next_lds_samp();
material.sample(idata.incoming, idata.nor, (u, v), self.wavelength)
material.sample(
idata.incoming,
idata.nor,
idata.nor_g,
(u, v),
self.wavelength,
)
};
// Check if pdf is zero, to avoid NaN's.
if pdf > 0.0 {
if (pdf > 0.0) && (filter.e.h_max() > 0.0) {
// Account for the additional light attenuation from
// this bounce
self.next_attentuation_fac = filter.e / pdf;

View File

@ -60,6 +60,7 @@ impl<'a> Assembly<'a> {
idata.incoming * sel_xform,
idata.pos * sel_xform,
idata.nor * sel_xform,
idata.nor_g * sel_xform,
closure.as_surface_closure(),
time,
n,

View File

@ -37,7 +37,8 @@ pub trait SurfaceClosure {
/// color filter.
///
/// inc: Incoming light direction.
/// nor: The surface normal at the surface point.
/// nor: The shading surface normal at the surface point.
/// nor_g: The geometric surface normal at the surface point.
/// uv: The sampling values.
/// wavelength: The wavelength of light to sample at.
///
@ -46,6 +47,7 @@ pub trait SurfaceClosure {
&self,
inc: Vector,
nor: Normal,
nor_g: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32);
@ -54,19 +56,28 @@ pub trait SurfaceClosure {
///
/// inc: The incoming light direction.
/// out: The outgoing light direction.
/// nor: The surface normal of the reflecting/transmitting surface point.
/// nor: The shading surface normal at the surface point.
/// nor_g: The geometric surface normal at the surface point.
/// wavelength: The wavelength of light to evaluate for.
///
/// Returns the resulting filter color.
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample;
fn evaluate(
&self,
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
wavelength: f32,
) -> SpectralSample;
/// Returns the pdf for the given 'in' direction producing the given 'out'
/// direction with the given differential geometry.
///
/// inc: The incoming light direction.
/// out: The outgoing light direction.
/// nor: The surface normal of the reflecting/transmitting surface point.
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal) -> f32;
/// nor: The shading surface normal at the surface point.
/// nor_g: The geometric surface normal at the surface point.
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> f32;
/// Returns an estimate of the sum total energy that evaluate() would return
/// when 'out' is evaluated over a circular solid angle.
@ -78,6 +89,7 @@ pub trait SurfaceClosure {
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
cos_theta: f32,
) -> f32;
}
@ -180,10 +192,11 @@ impl SurfaceClosure for EmitClosure {
&self,
inc: Vector,
nor: Normal,
nor_g: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32) {
let _ = (inc, nor, uv); // Not using these, silence warning
let _ = (inc, nor, nor_g, uv); // Not using these, silence warning
(
Vector::new(0.0, 0.0, 0.0),
@ -192,14 +205,21 @@ impl SurfaceClosure for EmitClosure {
)
}
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample {
let _ = (inc, out, nor); // Not using these, silence warning
fn evaluate(
&self,
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
wavelength: f32,
) -> SpectralSample {
let _ = (inc, out, nor, nor_g); // Not using these, silence warning
SpectralSample::new(wavelength)
}
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal) -> f32 {
let _ = (inc, out, nor); // Not using these, silence warning
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> f32 {
let _ = (inc, out, nor, nor_g); // Not using these, silence warning
1.0
}
@ -209,9 +229,10 @@ impl SurfaceClosure for EmitClosure {
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
cos_theta: f32,
) -> f32 {
let _ = (inc, out, nor, cos_theta); // Not using these, silence warning
let _ = (inc, out, nor, nor_g, cos_theta); // Not using these, silence warning
// TODO: what to do here?
unimplemented!()
@ -240,10 +261,11 @@ impl SurfaceClosure for LambertClosure {
&self,
inc: Vector,
nor: Normal,
nor_g: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32) {
let nn = if dot(nor.into_vector(), inc) <= 0.0 {
let nn = if dot(nor_g.into_vector(), inc) <= 0.0 {
nor.normalized()
} else {
-nor.normalized()
@ -254,14 +276,21 @@ impl SurfaceClosure for LambertClosure {
let dir = cosine_sample_hemisphere(uv.0, uv.1);
let pdf = dir.z() * INV_PI;
let out = zup_to_vec(dir, nn);
let filter = self.evaluate(inc, out, nor, wavelength);
let filter = self.evaluate(inc, out, nor, nor_g, wavelength);
(out, filter, pdf)
}
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample {
fn evaluate(
&self,
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
wavelength: f32,
) -> SpectralSample {
let v = out.normalized();
let nn = if dot(nor.into_vector(), inc) <= 0.0 {
let nn = if dot(nor_g.into_vector(), inc) <= 0.0 {
nor.normalized()
} else {
-nor.normalized()
@ -271,9 +300,9 @@ impl SurfaceClosure for LambertClosure {
self.col.to_spectral_sample(wavelength) * fac
}
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal) -> f32 {
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> f32 {
let v = out.normalized();
let nn = if dot(nor.into_vector(), inc) <= 0.0 {
let nn = if dot(nor_g.into_vector(), inc) <= 0.0 {
nor.normalized()
} else {
-nor.normalized()
@ -287,8 +316,11 @@ impl SurfaceClosure for LambertClosure {
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
cos_theta: f32,
) -> f32 {
let _ = nor_g; // Not using this, silence warning
assert!(cos_theta >= -1.0 && cos_theta <= 1.0);
// Analytically calculates lambert shading from a uniform light source
@ -449,11 +481,12 @@ impl SurfaceClosure for GTRClosure {
&self,
inc: Vector,
nor: Normal,
nor_g: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32) {
// Get normalized surface normal
let nn = if dot(nor.into_vector(), inc) < 0.0 {
let nn = if dot(nor_g.into_vector(), inc) < 0.0 {
nor.normalized()
} else {
-nor.normalized() // If back-facing, flip normal
@ -468,26 +501,37 @@ impl SurfaceClosure for GTRClosure {
half_dir = zup_to_vec(half_dir, nn).normalized();
let out = inc - (half_dir * 2.0 * dot(inc, half_dir));
let filter = self.evaluate(inc, out, nor, wavelength);
let pdf = self.sample_pdf(inc, out, nor);
let filter = self.evaluate(inc, out, nor, nor_g, wavelength);
let pdf = self.sample_pdf(inc, out, nor, nor_g);
(out, filter, pdf)
}
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample {
fn evaluate(
&self,
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
wavelength: f32,
) -> SpectralSample {
// Calculate needed vectors, normalized
let aa = -inc.normalized(); // Vector pointing to where "in" came from
let bb = out.normalized(); // Out
let hh = (aa + bb).normalized(); // Half-way between aa and bb
// Surface normal
let nn = if dot(nor.into_vector(), hh) < 0.0 {
let nn = if dot(nor_g.into_vector(), hh) < 0.0 {
-nor.normalized() // If back-facing, flip normal
} else {
nor.normalized()
}.into_vector();
if dot(nn, aa) < 0.0 {
return SpectralSample::from_value(0.0, 0.0);
}
// Calculate needed dot products
let na = clamp(dot(nn, aa), -1.0, 1.0);
let nb = clamp(dot(nn, bb), -1.0, 1.0);
@ -571,19 +615,23 @@ impl SurfaceClosure for GTRClosure {
}
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal) -> f32 {
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> f32 {
// Calculate needed vectors, normalized
let aa = -inc.normalized(); // Vector pointing to where "in" came from
let bb = out.normalized(); // Out
let hh = (aa + bb).normalized(); // Half-way between aa and bb
// Surface normal
let nn = if dot(nor.into_vector(), hh) < 0.0 {
let nn = if dot(nor_g.into_vector(), hh) < 0.0 {
-nor.normalized() // If back-facing, flip normal
} else {
nor.normalized()
}.into_vector();
if dot(nn, aa) < 0.0 {
return 0.0;
}
// Calculate needed dot products
let nh = clamp(dot(nn, hh), -1.0, 1.0);
@ -596,11 +644,15 @@ impl SurfaceClosure for GTRClosure {
inc: Vector,
out: Vector,
nor: Normal,
nor_g: Normal,
cos_theta: f32,
) -> f32 {
// TODO: all of the stuff in this function is horribly hacky.
// Find a proper way to approximate the light contribution from a
// solid angle.
let _ = nor_g; // Not using this, silence warning
assert!(cos_theta >= -1.0);
assert!(cos_theta <= 1.0);