Add light attenuation
This commit is contained in:
parent
b49f171f32
commit
e2b74d4559
6 changed files with 140 additions and 17 deletions
15
assignment-1b/examples/attenuation-demo.txt
Normal file
15
assignment-1b/examples/attenuation-demo.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
imsize 600 200
|
||||
eye 0 0 15
|
||||
viewdir 0 0 -1
|
||||
hfov 90
|
||||
updir 0 1 0
|
||||
bkgcolor 0.4 0.4 0.4
|
||||
|
||||
attlight -15 10 5 1 1 1 1 0 0.25 0.03
|
||||
|
||||
mtlcolor 0.6 1 0.8 1 1 1 0.4 1 0.5 15
|
||||
sphere -10 0 0 2
|
||||
sphere -5 0 0 2
|
||||
sphere 0 0 0 2
|
||||
sphere 5 0 0 2
|
||||
sphere 10 0 0 2
|
|
@ -66,7 +66,12 @@ pub struct Material {
|
|||
#[derive(Debug)]
|
||||
pub enum LightKind {
|
||||
/// A point light source exists at a point and emits light in all directions
|
||||
Point { location: Vector3<f64> },
|
||||
Point {
|
||||
location: Vector3<f64>,
|
||||
|
||||
/// Whether light attenuation is enabled for this light
|
||||
attenuation: Option<Attenuation>,
|
||||
},
|
||||
|
||||
/// A directional light source exists at an infinitely far location but emits
|
||||
/// light in a specific direction
|
||||
|
@ -87,7 +92,7 @@ impl Light {
|
|||
/// light source
|
||||
pub fn direction_from(&self, point: Vector3<f64>) -> Vector3<f64> {
|
||||
match self.kind {
|
||||
LightKind::Point { location } => location - point,
|
||||
LightKind::Point { location, .. } => location - point,
|
||||
LightKind::Directional { direction } => -direction,
|
||||
}
|
||||
.normalize()
|
||||
|
@ -131,6 +136,28 @@ impl Default for DepthCueing {
|
|||
}
|
||||
}
|
||||
|
||||
/// Light attenuation dropoff coefficients
|
||||
#[derive(Debug)]
|
||||
pub struct Attenuation {
|
||||
pub c1: f64,
|
||||
pub c2: f64,
|
||||
pub c3: f64,
|
||||
}
|
||||
|
||||
/// A default implementation here needs to simulate what would happen if there
|
||||
/// was no light attenuation specified. In this case, c1 would just be a
|
||||
/// constant of 1 and all the coefficients for anything involving distance would
|
||||
/// be zeroed out
|
||||
impl Default for Attenuation {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
c1: 1.0,
|
||||
c2: 0.0,
|
||||
c3: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
/// Determine the boundaries of the viewing window in world coordinates
|
||||
pub fn compute_viewing_window(&self, distance: f64) -> Rect {
|
||||
|
|
|
@ -61,14 +61,37 @@ impl Scene {
|
|||
.max(0.0)
|
||||
.powf(material.exponent);
|
||||
|
||||
// Shadow coefficient between 0 and 1 to control how bright this pixel
|
||||
// should be from being in the shadow of another object (could be
|
||||
// between 0 and 1 when applying soft shadows)
|
||||
let shadow_coefficient = self.compute_shadow_coefficient(
|
||||
obj_idx,
|
||||
intersection_context.point,
|
||||
light,
|
||||
);
|
||||
|
||||
let attenuation_coefficient = match &light.kind {
|
||||
LightKind::Point {
|
||||
location,
|
||||
attenuation: Some(att),
|
||||
} => {
|
||||
let dist = (location - intersection_context.point).norm();
|
||||
let denom = att.c1 + att.c2 * dist + att.c3 * dist.powi(2);
|
||||
if denom == 0.0 {
|
||||
warn!("Light attenuation coefficients produced a denominator of 0. Check your inputs...");
|
||||
1.0 // Some kind of graceful fallback here
|
||||
} else {
|
||||
1.0 / denom
|
||||
}
|
||||
}
|
||||
_ => 1.0,
|
||||
};
|
||||
|
||||
let diffuse_and_specular = diffuse_component + specular_component;
|
||||
shadow_coefficient * light.color.component_mul(&diffuse_and_specular)
|
||||
|
||||
attenuation_coefficient
|
||||
* shadow_coefficient
|
||||
* light.color.component_mul(&diffuse_and_specular)
|
||||
})
|
||||
.sum();
|
||||
|
||||
|
@ -141,7 +164,7 @@ impl Scene {
|
|||
match light.kind {
|
||||
// In the case of point lights, we must check to see if both t > 0 and
|
||||
// t is less than the time it took to even get to the light.
|
||||
LightKind::Point { location } => {
|
||||
LightKind::Point { location, .. } => {
|
||||
let light_time = (location - ray.origin).norm();
|
||||
|
||||
if intersection_time <= 0.0 || intersection_time >= light_time {
|
||||
|
|
|
@ -5,7 +5,7 @@ use nalgebra::Vector3;
|
|||
|
||||
use crate::scene::{
|
||||
cylinder::Cylinder,
|
||||
data::{Light, LightKind, Material, Object},
|
||||
data::{Light, LightKind, Material, Object, Attenuation},
|
||||
sphere::Sphere,
|
||||
Scene,
|
||||
};
|
||||
|
@ -79,6 +79,33 @@ impl Scene {
|
|||
},
|
||||
1 => LightKind::Point {
|
||||
location: read_vec3(0)?,
|
||||
attenuation: None,
|
||||
},
|
||||
_ => bail!("Invalid w; must be either 0 or 1"),
|
||||
};
|
||||
let light = Light {
|
||||
kind,
|
||||
color: read_vec3(4)?,
|
||||
};
|
||||
scene.lights.push(light);
|
||||
}
|
||||
|
||||
// attlight x y z w r g b c1 c2 c3
|
||||
"attlight" => {
|
||||
ensure!(parts.len() == 10, "Attenuated light requires 10 params");
|
||||
|
||||
let kind = match parts[3] as usize {
|
||||
// TODO: Is this even defined? Pending TA answer
|
||||
0 => LightKind::Directional {
|
||||
direction: read_vec3(0)?,
|
||||
},
|
||||
1 => LightKind::Point {
|
||||
location: read_vec3(0)?,
|
||||
attenuation: Some(Attenuation {
|
||||
c1: parts[7],
|
||||
c2: parts[8],
|
||||
c3: parts[9],
|
||||
}),
|
||||
},
|
||||
_ => bail!("Invalid w; must be either 0 or 1"),
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ use nalgebra::Vector3;
|
|||
|
||||
use crate::image::Color;
|
||||
|
||||
use self::data::{DepthCueing, Light, Material, Object};
|
||||
use self::data::{DepthCueing, Light, Material, Object, Attenuation};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Scene {
|
||||
|
@ -26,6 +26,7 @@ pub struct Scene {
|
|||
/// Background color
|
||||
pub bkg_color: Color,
|
||||
pub depth_cueing: DepthCueing,
|
||||
pub attenuation: Attenuation,
|
||||
|
||||
pub materials: Vec<Material>,
|
||||
pub lights: Vec<Light>,
|
||||
|
|
|
@ -5,13 +5,15 @@ output: pdf_document
|
|||
|
||||
# Raytracer part B
|
||||
|
||||
This project implements a raytracer with Blinn-Phong illumination implemented.
|
||||
The primary formula that is used by this implementation is:
|
||||
This project implements a raytracer with Blinn-Phong illumination and shadows
|
||||
implemented. The primary formula that is used by this implementation is:
|
||||
|
||||
\begin{equation}
|
||||
I_{\lambda} =
|
||||
k_a O_{d\lambda} +
|
||||
\sum_{i=1}^{n_\textrm{lights}} \left(
|
||||
f_\textrm{att} \cdot
|
||||
S_i \cdot
|
||||
IL_{i\lambda} \left[
|
||||
k_d O_{d\lambda} \max ( 0, \vec{N} \cdot \vec{L_i} ) +
|
||||
k_s O_{s\lambda} \max ( 0, \vec{N} \cdot \vec{H_i} )^n
|
||||
|
@ -26,6 +28,8 @@ Where:
|
|||
- $k_d$ is the material's diffuse reflectivity
|
||||
- $k_s$ is the material's specular reflectivity
|
||||
- $n_\textrm{lights}$ is the number of lights
|
||||
- $f_\textrm{att}$ is the light attenuation factor (1.0 if attenuation is not on)
|
||||
- $S_i$ is the shadow coefficient for light $i$
|
||||
- $IL_{i\lambda}$ is the intensity of light $i$
|
||||
- $O_{d\lambda}$ is the object's diffuse color
|
||||
- $O_{s\lambda}$ is the object's specular color
|
||||
|
@ -35,6 +39,11 @@ Where:
|
|||
direction to the viewer
|
||||
- $n$ is the exponent for the specular component
|
||||
|
||||
In this report we will look through how these various factors influence the
|
||||
rendering of the scene. All the images along with their source `.txt` files,
|
||||
rendered `.ppm` files, and converted `.png` files can be found in the `examples`
|
||||
directory of this handin.
|
||||
|
||||
## Varying $k_a$
|
||||
|
||||
$k_a$ is the strength of ambient light. It's used as a coefficient for the
|
||||
|
@ -99,6 +108,28 @@ we can get some soft shadow effects like this:
|
|||
![Soft shadows](examples/soft-shadow-demo.png){width=360px}
|
||||
\
|
||||
|
||||
## Light attenuation
|
||||
|
||||
Light attenuation is when more of the light is applied for objects that are
|
||||
closer to a particular light source. The function that's applied is an inverse
|
||||
quadratic formula with respect to the distance the object is from the light:
|
||||
|
||||
\begin{equation}
|
||||
f_\textrm{att}(d) = \frac{1}{c_1 + c_2 d + c_3 d^2}
|
||||
\end{equation}
|
||||
|
||||
Where:
|
||||
|
||||
- $f_\textrm{att}$ is the attenuation factor
|
||||
- $d$ is the distance the object is from the light
|
||||
- $c_1$, $c_2$, and $c_3$ are user-supplied coefficients
|
||||
|
||||
As you can see below, the effect of the light drops off with the distance from
|
||||
the light (light coming from the left):
|
||||
|
||||
![Light attenuation](examples/attenuation-demo.png){width=360px}
|
||||
\
|
||||
|
||||
## Depth Cueing
|
||||
|
||||
Depth cueing is when the objects further from the viewer have a lower opacity to
|
||||
|
@ -117,4 +148,3 @@ The model cannot be used to represent TODO
|
|||
|
||||
![Objects in the scene](examples/objects.png){width=360px}
|
||||
\
|
||||
|
||||
|
|
Loading…
Reference in a new issue