Basic Perlin texture implementation.

This commit is contained in:
Bill Thiede 2018-09-23 09:33:01 -07:00
parent 4206ad7289
commit 697d056a9d
7 changed files with 167 additions and 40 deletions

1
rtiow/Cargo.lock generated
View File

@ -490,6 +490,7 @@ dependencies = [
"cpuprofiler 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -13,6 +13,7 @@ structopt = "0.2.10"
stderrlog = "0.4.1"
log = "0.4.5"
cpuprofiler = "0.0.3"
lazy_static = "1.1.0"
# For better profiling support.
[profile.release]

View File

@ -7,6 +7,7 @@ pub mod hitable_list;
pub mod kdtree;
pub mod material;
pub mod moving_sphere;
pub mod perlin;
pub mod ray;
pub mod renderer;
pub mod scenes;
@ -17,6 +18,8 @@ pub mod vec3;
extern crate crossbeam_channel;
extern crate image;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate num_cpus;
extern crate rand;

98
rtiow/src/perlin.rs Normal file
View File

@ -0,0 +1,98 @@
use rand;
use rand::Rng;
//use rand::SeedableRng;
//use rand::XorShiftRng;
use vec3::Vec3;
lazy_static! {
pub static ref GENERATOR: Perlin = Perlin::new();
}
//const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
pub struct Perlin {
ranfloat: Vec<f32>,
perm_x: Vec<usize>,
perm_y: Vec<usize>,
perm_z: Vec<usize>,
}
fn perlin_generate() -> Vec<f32> {
//let mut rng: XorShiftRng = SeedableRng::from_seed(SEED);
let mut rng = rand::thread_rng();
(0..256).map(|_| rng.gen_range::<f32>(0., 1.)).collect()
}
fn permute(p: &mut Vec<usize>, n: usize) {
//let mut rng: XorShiftRng = SeedableRng::from_seed(SEED);
let mut rng = rand::thread_rng();
let mut random = || rng.gen_range::<f32>(0., 1.);
for i in (0..n).rev() {
let target = (random() * (i + 1) as f32) as usize;
let tmp = p[i];
p[i] = p[target];
p[target] = tmp;
}
}
fn perlin_generate_perm() -> Vec<usize> {
let mut p: Vec<usize> = (0..256).map(|i| i).collect();
permute(p.as_mut(), 256);
p
}
fn trilinear_interp(c: [[[f32; 2]; 2]; 2], u: f32, v: f32, w: f32) -> f32 {
let mut accum = 0.;
for i in 0..2 {
for j in 0..2 {
for k in 0..2 {
let fi = i as f32;
let fj = j as f32;
let fk = k as f32;
accum += (fi * u + (1. - fi) * (1. - u))
* (fj * v + (1. - fj) * (1. - v))
* (fk * w + (1. - fk) * (1. - w))
* c[i][j][k];
}
}
}
accum
}
impl Perlin {
fn new() -> Perlin {
Perlin {
ranfloat: perlin_generate(),
perm_x: perlin_generate_perm(),
perm_y: perlin_generate_perm(),
perm_z: perlin_generate_perm(),
}
}
pub fn noise(&self, p: Vec3) -> f32 {
let mut u = p.x - p.x.floor();
let mut v = p.y - p.y.floor();
let mut w = p.z - p.z.floor();
// Hermite cubic to round off interpolation and attempt to address 'mach bands'.
u = u * u * (3. - 2. * u);
v = v * v * (3. - 2. * v);
w = w * w * (3. - 2. * w);
let i = p.x.floor() as usize;
let j = p.y.floor() as usize;
let k = p.z.floor() as usize;
let mut c: [[[f32; 2]; 2]; 2] = Default::default();
for di in 0..2 {
for dj in 0..2 {
for dk in 0..2 {
c[di][dj][dk] = self.ranfloat[self.perm_x[(i + di) & 255]
^ self.perm_y[(j + dj) & 255]
^ self.perm_z[(k + dk) & 255]]
}
}
}
trilinear_interp(c, u, v, w)
}
}

View File

@ -76,7 +76,7 @@ pub struct Opt {
#[structopt(short = "h", long = "height", default_value = "1024")]
pub height: usize,
/// Sub-samples per pixel
#[structopt(short = "s", long = "subsample", default_value = "1")]
#[structopt(short = "s", long = "subsample", default_value = "8")]
pub subsamples: usize,
/// Select scene to render, one of: "bench", "book", "tutorial" "cube", "bvh", "test"
#[structopt(long = "model", default_value = "test")]

View File

@ -1,24 +1,24 @@
use std::f32::consts::PI;
use camera::Camera;
use hitable::Hit;
use hitable_list::HitableList;
use kdtree::KDTree;
use material::Lambertian;
use material::Metal;
use moving_sphere::MovingSphere;
use renderer::Opt;
use renderer::Scene;
use sphere::Sphere;
use texture::CheckerTexture;
use texture::ConstantTexture;
use texture::NoiseTexture;
use vec3::Vec3;
pub fn new(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(0.001, 0.001, 20.);
let lookfrom = Vec3::new(5., 5., 5.);
let lookat = Vec3::new(0., 0., 0.);
let dist_to_focus = (lookfrom - lookat).length();
let aperture = 0.1;
let time_min = 0.;
let time_max = 0.;
let time_max = 1.;
let camera = Camera::new(
lookfrom,
lookat,
@ -30,48 +30,52 @@ pub fn new(opt: &Opt) -> Scene {
time_min,
time_max,
);
let mut objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new(
let _ground_color = if opt.use_accel {
Box::new(ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4)))
} else {
Box::new(ConstantTexture::new(Vec3::new(0.4, 1.0, 0.4)))
};
let objects: Vec<Box<Hit>> = vec![
Box::new(Sphere::new(
Vec3::new(0., 0., 0.),
5.,
Box::new(Lambertian::new(Box::new(CheckerTexture::new(
Box::new(ConstantTexture::new(Vec3::new(0., 0., 0.))),
Box::new(ConstantTexture::new(Vec3::new(1., 1., 1.))),
)))),
))];
let num_spheres = 6;
let radius = 7.;
for i in 0..num_spheres {
let c1 = radius * ((2. * PI) * (i as f32 / num_spheres as f32)).sin();
let c2 = radius * ((2. * PI) * (i as f32 / num_spheres as f32)).cos();
let center = Vec3::new(0., c1, c2);
objects.push(Box::new(Sphere::new(
center,
0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new(
1., 0., 0.,
0.1, 0.2, 0.5,
))))),
)));
let center = Vec3::new(c1, 0., c2);
objects.push(Box::new(Sphere::new(
center,
)),
// Big sphere
Box::new(Sphere::new(
Vec3::new(0., 0., -2.),
1.0,
Box::new(Lambertian::new(Box::new(NoiseTexture::with_scale(10.)))),
)),
// Earth sized sphere
Box::new(Sphere::new(
Vec3::new(0., -100.5, 0.),
100.,
// Box::new(Lambertian::new(ground_color)),
Box::new(Lambertian::new(Box::new(NoiseTexture::new()))),
)),
// Shiny sphere
Box::new(Sphere::new(
Vec3::new(1., 0., 0.),
0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new(
0., 1., 0.,
))))),
)));
let center = Vec3::new(c1, c2, 0.);
objects.push(Box::new(Sphere::new(
center,
Box::new(Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2)),
)),
Box::new(MovingSphere::new(
Vec3::new(-1., 0., -0.25),
Vec3::new(-1., 0., 0.25),
0.5,
0.,
1.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new(
0., 0., 1.,
0.2, 0.8, 0.2,
))))),
)));
}
)),
];
let world: Box<Hit> = if opt.use_accel {
let kd = KDTree::new(objects, time_min, time_max);
//println!("{}", kd);
Box::new(kd)
Box::new(KDTree::new(objects, time_min, time_max))
} else {
Box::new(HitableList::new(objects))
};

View File

@ -1,3 +1,4 @@
use perlin::GENERATOR;
use vec3::Vec3;
pub trait Texture: Send + Sync {
@ -41,3 +42,22 @@ impl Texture for CheckerTexture {
}
}
}
pub struct NoiseTexture {
scale: f32,
}
impl NoiseTexture {
pub fn new() -> NoiseTexture {
NoiseTexture { scale: 1. }
}
pub fn with_scale(scale: f32) -> NoiseTexture {
NoiseTexture { scale }
}
}
impl Texture for NoiseTexture {
fn value(&self, _u: f32, _v: f32, p: Vec3) -> Vec3 {
Vec3::new(1., 1., 1.) * GENERATOR.noise(self.scale * p)
}
}