Moved lode/perlin code to noise module.
Changed how NoiseTexture is created to allow noise function and parameters to be passed in as options. Allowed setting of noise source from URL parameters.
This commit is contained in:
parent
accfd09ce4
commit
5faba9cf26
@ -22,3 +22,4 @@ serde_derive = "1.0.79"
|
||||
# For better profiling support.
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#[macro_use]
|
||||
extern crate askama;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate actix_web;
|
||||
extern crate image;
|
||||
extern crate rand;
|
||||
@ -15,22 +13,29 @@ extern crate structopt;
|
||||
|
||||
extern crate rtiow;
|
||||
|
||||
use actix_web::Path as WebPath;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use actix_web::http;
|
||||
use actix_web::middleware;
|
||||
use actix_web::server;
|
||||
use actix_web::App;
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web::Path as WebPath;
|
||||
use actix_web::Result;
|
||||
use askama::Template;
|
||||
use rand::SeedableRng;
|
||||
use rand::XorShiftRng;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use rtiow::lode::Noise;
|
||||
use rtiow::noise;
|
||||
use rtiow::noise::lode::Lode;
|
||||
use rtiow::noise::perlin::Perlin;
|
||||
use rtiow::noise::NoiseType;
|
||||
use rtiow::texture::NoiseTexture;
|
||||
use rtiow::texture::Texture;
|
||||
use rtiow::vec3::Vec3;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
@ -55,62 +60,41 @@ struct Opt {
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
enum NoiseType {
|
||||
Scale(f32),
|
||||
Turbulence(usize),
|
||||
Marble {
|
||||
period: Vec3,
|
||||
power: f32,
|
||||
size: usize,
|
||||
},
|
||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq)]
|
||||
enum NoiseSource {
|
||||
#[serde(rename = "perlin")]
|
||||
Perlin,
|
||||
#[serde(rename = "lode")]
|
||||
Lode,
|
||||
}
|
||||
|
||||
impl NoiseType {
|
||||
fn to_url(&self) -> String {
|
||||
match &self {
|
||||
NoiseType::Scale(scale) => format!("scale/{}", scale),
|
||||
NoiseType::Turbulence(turbulence) => format!("turbulence/{}", turbulence),
|
||||
NoiseType::Marble {
|
||||
period,
|
||||
power,
|
||||
size,
|
||||
} => format!(
|
||||
"marble/period/{},{},{}/power/{}/size/{}",
|
||||
period.x, period.y, period.z, power, size,
|
||||
),
|
||||
impl fmt::Display for NoiseSource {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
NoiseSource::Perlin => write!(f, "perlin"),
|
||||
NoiseSource::Lode => write!(f, "lode"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parameters(&self) -> Vec<(&str, String)> {
|
||||
match &self {
|
||||
NoiseType::Scale(scale) => vec![("Scale", scale.to_string())],
|
||||
NoiseType::Turbulence(turbulence) => vec![("Turbulence", turbulence.to_string())],
|
||||
NoiseType::Marble {
|
||||
period,
|
||||
power,
|
||||
size,
|
||||
} => vec![
|
||||
("Period", period.to_string()),
|
||||
("Power", power.to_string()),
|
||||
("Size", size.to_string()),
|
||||
],
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct NoiseSourceParam {
|
||||
noise_source: NoiseSource,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct NoiseParams {
|
||||
width: u32,
|
||||
height: u32,
|
||||
noise: NoiseType,
|
||||
noise_source: NoiseSource,
|
||||
noise_type: NoiseType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct NoiseParamsScale {
|
||||
width: u32,
|
||||
height: u32,
|
||||
noise_source: NoiseSource,
|
||||
scale: f32,
|
||||
}
|
||||
|
||||
@ -118,6 +102,7 @@ struct NoiseParamsScale {
|
||||
struct NoiseParamsTurbulence {
|
||||
width: u32,
|
||||
height: u32,
|
||||
noise_source: NoiseSource,
|
||||
turbulence: usize,
|
||||
}
|
||||
|
||||
@ -125,6 +110,7 @@ struct NoiseParamsTurbulence {
|
||||
struct NoiseParamsMarble {
|
||||
width: u32,
|
||||
height: u32,
|
||||
noise_source: NoiseSource,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
@ -135,15 +121,20 @@ struct NoiseParamsMarble {
|
||||
impl NoiseParams {
|
||||
fn url(&self) -> String {
|
||||
format!(
|
||||
"/noise/{}/{}/{}",
|
||||
"/noise/{}/{}/{}/{}",
|
||||
self.noise_source,
|
||||
self.width,
|
||||
self.height,
|
||||
self.noise.to_url(),
|
||||
self.noise_type.to_url(),
|
||||
)
|
||||
}
|
||||
|
||||
fn big_url(&self) -> String {
|
||||
format!("/noise/1024/1024/{}", self.noise.to_url())
|
||||
format!(
|
||||
"/noise/{}/1024/1024/{}",
|
||||
self.noise_source,
|
||||
self.noise_type.to_url()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,30 +142,19 @@ fn render_noise(noise_params: &NoiseParams) -> image::GrayImage {
|
||||
const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
let rng: &mut XorShiftRng = &mut SeedableRng::from_seed(SEED);
|
||||
let mut img = image::GrayImage::new(noise_params.width, noise_params.height);
|
||||
let tex = Noise::new(rng);
|
||||
let tex: NoiseTexture<Box<noise::NoiseSource>> = match noise_params.noise_source {
|
||||
NoiseSource::Perlin => {
|
||||
NoiseTexture::new(Box::new(Perlin::new(rng)), noise_params.noise_type)
|
||||
}
|
||||
NoiseSource::Lode => NoiseTexture::new(Box::new(Lode::new(rng)), noise_params.noise_type),
|
||||
};
|
||||
|
||||
for x in 0..img.width() {
|
||||
for y in 0..img.height() {
|
||||
let u = x as f32 / 2.;
|
||||
let v = y as f32 / 2.;
|
||||
let p = Vec3::new(u, v, 1.);
|
||||
let luma = match noise_params.noise {
|
||||
NoiseType::Scale(scale) => tex.value(p, scale),
|
||||
|
||||
NoiseType::Turbulence(turbulence) => tex.turbulence(p, turbulence),
|
||||
NoiseType::Marble {
|
||||
period,
|
||||
power,
|
||||
size,
|
||||
} => tex.marble(p, period, power, size),
|
||||
};
|
||||
|
||||
if luma > 1. {
|
||||
info!("Hot pixel @ {}x{}: {}", x, y, luma);
|
||||
}
|
||||
if luma < 0. {
|
||||
info!("Cold pixel @ {}x{}: {}", x, y, luma);
|
||||
}
|
||||
let luma = tex.value(u, v, p).x;
|
||||
let luma = (luma * 255.) as u8;
|
||||
img.put_pixel(x, y, image::Luma([luma]));
|
||||
}
|
||||
@ -192,21 +172,26 @@ struct Specimen {
|
||||
#[template(path = "index.html")]
|
||||
struct IndexTemplate {
|
||||
render_time: u64,
|
||||
noise_source: NoiseSource,
|
||||
specimens: Vec<Specimen>,
|
||||
}
|
||||
|
||||
fn render_html(specimens: Vec<Specimen>) -> Result<String, askama::Error> {
|
||||
fn render_html(
|
||||
noise_source: NoiseSource,
|
||||
specimens: Vec<Specimen>,
|
||||
) -> Result<String, askama::Error> {
|
||||
let index = IndexTemplate {
|
||||
render_time: SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs(),
|
||||
noise_source,
|
||||
specimens,
|
||||
};
|
||||
index.render()
|
||||
}
|
||||
|
||||
fn build_specimens(width: u32, height: u32) -> Vec<Specimen> {
|
||||
fn build_specimens(noise_source: NoiseSource, width: u32, height: u32) -> Vec<Specimen> {
|
||||
let mut specimens = Vec::new();
|
||||
let mut specimen = Specimen {
|
||||
title: "Unperturbed".into(),
|
||||
@ -216,7 +201,8 @@ fn build_specimens(width: u32, height: u32) -> Vec<Specimen> {
|
||||
let params = NoiseParams {
|
||||
width,
|
||||
height,
|
||||
noise: NoiseType::Scale(1.),
|
||||
noise_source,
|
||||
noise_type: NoiseType::Scale(1.),
|
||||
};
|
||||
specimen.params.push(params);
|
||||
}
|
||||
@ -232,7 +218,8 @@ fn build_specimens(width: u32, height: u32) -> Vec<Specimen> {
|
||||
let params = NoiseParams {
|
||||
width,
|
||||
height,
|
||||
noise: NoiseType::Scale(scale as f32 / 10.),
|
||||
noise_source,
|
||||
noise_type: NoiseType::Scale(scale as f32 / 10.),
|
||||
};
|
||||
specimen.params.push(params);
|
||||
}
|
||||
@ -246,7 +233,8 @@ fn build_specimens(width: u32, height: u32) -> Vec<Specimen> {
|
||||
let params = NoiseParams {
|
||||
width,
|
||||
height,
|
||||
noise: NoiseType::Scale(scale as f32),
|
||||
noise_source,
|
||||
noise_type: NoiseType::Scale(scale as f32),
|
||||
};
|
||||
specimen.params.push(params);
|
||||
}
|
||||
@ -260,7 +248,8 @@ fn build_specimens(width: u32, height: u32) -> Vec<Specimen> {
|
||||
let params = NoiseParams {
|
||||
width,
|
||||
height,
|
||||
noise: NoiseType::Turbulence(1 << turbulence),
|
||||
noise_source,
|
||||
noise_type: NoiseType::Turbulence(1 << turbulence),
|
||||
};
|
||||
specimen.params.push(params);
|
||||
}
|
||||
@ -280,7 +269,8 @@ fn build_specimens(width: u32, height: u32) -> Vec<Specimen> {
|
||||
let params = NoiseParams {
|
||||
width,
|
||||
height,
|
||||
noise: NoiseType::Marble {
|
||||
noise_source,
|
||||
noise_type: NoiseType::Marble {
|
||||
period: Vec3::new(5., 10., 0.),
|
||||
power: power as f32,
|
||||
size: 1 << size,
|
||||
@ -299,7 +289,8 @@ fn build_specimens(width: u32, height: u32) -> Vec<Specimen> {
|
||||
let params = NoiseParams {
|
||||
width,
|
||||
height,
|
||||
noise: NoiseType::Marble {
|
||||
noise_source,
|
||||
noise_type: NoiseType::Marble {
|
||||
period: Vec3::new(0., 1., 0.),
|
||||
power: power as f32,
|
||||
size: 32,
|
||||
@ -317,14 +308,23 @@ fn style(_req: &HttpRequest) -> Result<HttpResponse> {
|
||||
Ok(HttpResponse::Ok().content_type("text/css").body(&bytes[..]))
|
||||
}
|
||||
|
||||
fn index(_req: &HttpRequest) -> Result<HttpResponse> {
|
||||
// TODO(wathiede): get these from app state?
|
||||
let width = 128;
|
||||
let height = 128;
|
||||
let specimens = build_specimens(width, height);
|
||||
fn index(source: NoiseSource, width: u32, height: u32) -> Result<HttpResponse> {
|
||||
let specimens = build_specimens(source, width, height);
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("text/html")
|
||||
.body(render_html(specimens).unwrap()))
|
||||
.body(render_html(source, specimens).unwrap()))
|
||||
}
|
||||
|
||||
fn index_perlin(_req: &HttpRequest) -> Result<HttpResponse> {
|
||||
let width = 128;
|
||||
let height = 128;
|
||||
index(NoiseSource::Perlin, width, height)
|
||||
}
|
||||
|
||||
fn index_lode(_req: &HttpRequest) -> Result<HttpResponse> {
|
||||
let width = 128;
|
||||
let height = 128;
|
||||
index(NoiseSource::Lode, width, height)
|
||||
}
|
||||
|
||||
fn noise(np: &NoiseParams) -> Result<HttpResponse> {
|
||||
@ -343,7 +343,8 @@ fn noise_scale(np: WebPath<NoiseParamsScale>) -> Result<HttpResponse> {
|
||||
noise(&NoiseParams {
|
||||
width: np.width,
|
||||
height: np.height,
|
||||
noise: NoiseType::Scale(np.scale),
|
||||
noise_source: np.noise_source,
|
||||
noise_type: NoiseType::Scale(np.scale),
|
||||
})
|
||||
}
|
||||
|
||||
@ -352,7 +353,8 @@ fn noise_turbulence(np: WebPath<NoiseParamsTurbulence>) -> Result<HttpResponse>
|
||||
noise(&NoiseParams {
|
||||
width: np.width,
|
||||
height: np.height,
|
||||
noise: NoiseType::Turbulence(np.turbulence),
|
||||
noise_source: np.noise_source,
|
||||
noise_type: NoiseType::Turbulence(np.turbulence),
|
||||
})
|
||||
}
|
||||
|
||||
@ -361,7 +363,8 @@ fn noise_marble(np: WebPath<NoiseParamsMarble>) -> Result<HttpResponse> {
|
||||
noise(&NoiseParams {
|
||||
width: np.width,
|
||||
height: np.height,
|
||||
noise: NoiseType::Marble {
|
||||
noise_source: np.noise_source,
|
||||
noise_type: NoiseType::Marble {
|
||||
period: Vec3::new(np.x, np.y, np.z),
|
||||
power: np.power,
|
||||
size: np.size,
|
||||
@ -379,17 +382,19 @@ fn main() -> Result<(), std::io::Error> {
|
||||
|
||||
server::new(|| {
|
||||
App::new()
|
||||
.resource("/noise/{width}/{height}/scale/{scale}", |r| {
|
||||
.resource("/noise/{noise_source}/{width}/{height}/scale/{scale}", |r| {
|
||||
r.with(noise_scale);
|
||||
}).resource("/noise/{width}/{height}/turbulence/{turbulence}", |r| {
|
||||
}).resource("/noise/{noise_source}/{width}/{height}/turbulence/{turbulence}", |r| {
|
||||
r.with(noise_turbulence);
|
||||
}).resource(
|
||||
"/noise/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}",
|
||||
"/noise/{noise_source}/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}",
|
||||
|r| {
|
||||
r.with(noise_marble);
|
||||
},
|
||||
).resource("/style.css", |r| r.f(style))
|
||||
.resource("/", |r| r.f(index))
|
||||
.resource("/lode", |r| r.f(index_lode))
|
||||
.resource("/perlin", |r| r.f(index_perlin))
|
||||
.resource("/", |r| r.f(|_| HttpResponse::Found().header(http::header::LOCATION, "/lode").finish()))
|
||||
.middleware(middleware::Logger::default())
|
||||
}).bind(opt.addr)
|
||||
.unwrap()
|
||||
@ -413,14 +418,14 @@ mod tests {
|
||||
#[test]
|
||||
fn noise_param_from_req() {
|
||||
let mut srv = TestServer::build().start(|app| {
|
||||
app.resource("/noise/{width}/{height}/scale/{scale}", |r| {
|
||||
app.resource("/noise/{noise_source}/{width}/{height}/scale/{scale}", |r| {
|
||||
r.with(|p: Path<NoiseParamsScale>| format!("{:?}", p));
|
||||
});
|
||||
app.resource("/noise/{width}/{height}/turbulence/{turbulence}", |r| {
|
||||
app.resource("/noise/{noise_source}/{width}/{height}/turbulence/{turbulence}", |r| {
|
||||
r.with(|p: Path<NoiseParamsTurbulence>| format!("{:?}", p));
|
||||
});
|
||||
app.resource(
|
||||
"/noise/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}",
|
||||
"/noise/{noise_source}/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}",
|
||||
|r| {
|
||||
r.with(|p: Path<NoiseParamsMarble>| format!("{:?}", p));
|
||||
},
|
||||
@ -428,7 +433,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let request = srv
|
||||
.client(Method::GET, "/noise/32/32/scale/1")
|
||||
.client(Method::GET, "/noise/perlin/32/32/scale/1")
|
||||
.finish()
|
||||
.unwrap();
|
||||
let response = srv.execute(request.send()).unwrap();
|
||||
@ -438,11 +443,11 @@ mod tests {
|
||||
let body = str::from_utf8(&bytes).unwrap();
|
||||
assert_eq!(
|
||||
body,
|
||||
"NoiseParamsScale { width: 32, height: 32, scale: 1.0 }"
|
||||
"NoiseParamsScale { width: 32, height: 32, noise_source: Perlin, scale: 1.0 }"
|
||||
);
|
||||
|
||||
let request = srv
|
||||
.client(Method::GET, "/noise/32/32/turbulence/5")
|
||||
.client(Method::GET, "/noise/lode/32/32/scale/1")
|
||||
.finish()
|
||||
.unwrap();
|
||||
let response = srv.execute(request.send()).unwrap();
|
||||
@ -452,14 +457,28 @@ mod tests {
|
||||
let body = str::from_utf8(&bytes).unwrap();
|
||||
assert_eq!(
|
||||
body,
|
||||
"NoiseParamsTurbulence { width: 32, height: 32, turbulence: 5 }"
|
||||
"NoiseParamsScale { width: 32, height: 32, noise_source: Lode, scale: 1.0 }"
|
||||
);
|
||||
|
||||
let request = srv
|
||||
.client(Method::GET, "/noise/lode/32/32/turbulence/5")
|
||||
.finish()
|
||||
.unwrap();
|
||||
let response = srv.execute(request.send()).unwrap();
|
||||
assert!(response.status().is_success(), "Response {:?}", response);
|
||||
|
||||
let bytes = srv.execute(response.body()).unwrap();
|
||||
let body = str::from_utf8(&bytes).unwrap();
|
||||
assert_eq!(
|
||||
body,
|
||||
"NoiseParamsTurbulence { width: 32, height: 32, noise_source: Lode, turbulence: 5 }"
|
||||
);
|
||||
|
||||
// TODO(wathiede): this isn't working, probably because Vec3 Deserialize is not working.
|
||||
let request = srv
|
||||
.client(
|
||||
Method::GET,
|
||||
"/noise/32/32/marble/period/1.,2.,3./power/32./size/4",
|
||||
"/noise/lode/32/32/marble/period/1.,2.,3./power/32./size/4",
|
||||
).finish()
|
||||
.unwrap();
|
||||
let response = srv.execute(request.send()).unwrap();
|
||||
@ -469,7 +488,7 @@ mod tests {
|
||||
let body = str::from_utf8(&bytes).unwrap();
|
||||
assert_eq!(
|
||||
body,
|
||||
"NoiseParamsMarble { width: 32, height: 32, x: 1.0, y: 2.0, z: 3.0, power: 32.0, size: 4 }"
|
||||
"NoiseParamsMarble { width: 32, height: 32, noise_source: Lode, x: 1.0, y: 2.0, z: 3.0, power: 32.0, size: 4 }"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,9 @@ pub mod flip_normals;
|
||||
pub mod hitable;
|
||||
pub mod hitable_list;
|
||||
pub mod kdtree;
|
||||
pub mod lode;
|
||||
pub mod material;
|
||||
pub mod moving_sphere;
|
||||
pub mod perlin;
|
||||
pub mod noise;
|
||||
pub mod ray;
|
||||
pub mod rect;
|
||||
pub mod renderer;
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
/// Implements the concepts from https://lodev.org/cgtutor/randomnoise.html
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use rand;
|
||||
|
||||
use noise::NoiseSource;
|
||||
use vec3::Vec3;
|
||||
|
||||
const NOISE_SIZE: usize = 128;
|
||||
pub struct Noise {
|
||||
pub struct Lode {
|
||||
// Using fixed array causes stack overflow.
|
||||
noise: Vec<Vec<Vec<f32>>>, //[[[f32; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE],
|
||||
}
|
||||
|
||||
impl Noise {
|
||||
pub fn new<R>(rng: &mut R) -> Noise
|
||||
impl Lode {
|
||||
pub fn new<R>(rng: &mut R) -> Lode
|
||||
where
|
||||
R: rand::Rng,
|
||||
{
|
||||
@ -26,32 +25,12 @@ impl Noise {
|
||||
}
|
||||
}
|
||||
}
|
||||
Noise { noise }
|
||||
Lode { noise }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn marble(&self, p: Vec3, period: Vec3, power: f32, size: usize) -> f32 {
|
||||
let xyz_value = p.x * period.x / NOISE_SIZE as f32
|
||||
+ p.y * period.y / NOISE_SIZE as f32
|
||||
+ p.z * period.z / NOISE_SIZE as f32
|
||||
+ power * self.turbulence(p, size);
|
||||
//info!("p {} xyz_value {}", p, xyz_value);
|
||||
(xyz_value * PI).sin().abs()
|
||||
}
|
||||
|
||||
pub fn turbulence(&self, p: Vec3, factor: usize) -> f32 {
|
||||
let mut value = 0.;
|
||||
let initial_factor = factor;
|
||||
let mut factor = factor;
|
||||
|
||||
while factor >= 1 {
|
||||
value += self.value(p / factor as f32, 1.) * factor as f32;
|
||||
factor /= 2;
|
||||
}
|
||||
0.5 * value / initial_factor as f32
|
||||
}
|
||||
|
||||
pub fn value(&self, p: Vec3, scale: f32) -> f32 {
|
||||
let p = p / scale;
|
||||
impl NoiseSource for Lode {
|
||||
fn value(&self, p: Vec3) -> f32 {
|
||||
// Fractional part of vector components
|
||||
let x = p.x - p.x.floor();
|
||||
let y = p.y - p.y.floor();
|
||||
89
rtiow/src/noise/mod.rs
Normal file
89
rtiow/src/noise/mod.rs
Normal file
@ -0,0 +1,89 @@
|
||||
pub mod lode;
|
||||
pub mod perlin;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use vec3::Vec3;
|
||||
|
||||
pub trait NoiseSource: Send + Sync {
|
||||
/// value returns noise on the interval [0., 1.).
|
||||
fn value(&self, p: Vec3) -> f32;
|
||||
|
||||
fn marble(&self, p: Vec3, period: Vec3, power: f32, size: usize) -> f32 {
|
||||
// TODO(wathiede): can't understand why 255 works for perlin and lode, maybe it's near 360
|
||||
// degrees and interacts with the sine function?
|
||||
let xyz_value = p.x * period.x / 255.
|
||||
+ p.y * period.y / 255.
|
||||
+ p.z * period.z / 255.
|
||||
+ power * self.turbulence(p, size);
|
||||
(xyz_value * PI).sin().abs()
|
||||
}
|
||||
|
||||
fn turbulence(&self, p: Vec3, factor: usize) -> f32 {
|
||||
let mut value = 0.;
|
||||
let initial_factor = factor;
|
||||
let mut factor = factor;
|
||||
|
||||
while factor >= 1 {
|
||||
value += self.value(p / factor as f32) * factor as f32;
|
||||
factor /= 2;
|
||||
}
|
||||
0.5 * value / initial_factor as f32
|
||||
}
|
||||
|
||||
fn scaled(&self, p: Vec3, scale: f32) -> f32 {
|
||||
let p = p / scale;
|
||||
self.value(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseSource for Box<NoiseSource> {
|
||||
fn value(&self, p: Vec3) -> f32 {
|
||||
(**self).value(p)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum NoiseType {
|
||||
Scale(f32),
|
||||
Turbulence(usize),
|
||||
Marble {
|
||||
period: Vec3,
|
||||
power: f32,
|
||||
size: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl NoiseType {
|
||||
pub fn to_url(&self) -> String {
|
||||
match &self {
|
||||
NoiseType::Scale(scale) => format!("scale/{}", scale),
|
||||
NoiseType::Turbulence(turbulence) => format!("turbulence/{}", turbulence),
|
||||
NoiseType::Marble {
|
||||
period,
|
||||
power,
|
||||
size,
|
||||
} => format!(
|
||||
"marble/period/{},{},{}/power/{}/size/{}",
|
||||
period.x, period.y, period.z, power, size,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> Vec<(&str, String)> {
|
||||
match &self {
|
||||
NoiseType::Scale(scale) => vec![("Scale", scale.to_string())],
|
||||
NoiseType::Turbulence(turbulence) => vec![("Turbulence", turbulence.to_string())],
|
||||
NoiseType::Marble {
|
||||
period,
|
||||
power,
|
||||
size,
|
||||
} => vec![
|
||||
("Period", period.to_string()),
|
||||
("Power", power.to_string()),
|
||||
("Size", size.to_string()),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))]
|
||||
use rand::Rng;
|
||||
|
||||
use noise::NoiseSource;
|
||||
use vec3::dot;
|
||||
use vec3::Vec3;
|
||||
|
||||
@ -112,18 +113,16 @@ impl Perlin {
|
||||
let mut temp_p = p;
|
||||
let mut weight = 1.;
|
||||
for _ in 0..depth {
|
||||
accum += weight * self.noise(temp_p);
|
||||
accum += weight * self.value(temp_p);
|
||||
weight *= 0.5;
|
||||
temp_p = temp_p * 0.2;
|
||||
}
|
||||
accum.abs()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn noise<V>(&self, p: V) -> f32
|
||||
where
|
||||
V: Into<Vec3>,
|
||||
{
|
||||
let p = p.into();
|
||||
impl NoiseSource for Perlin {
|
||||
fn value(&self, p: Vec3) -> f32 {
|
||||
let u = p.x - p.x.floor();
|
||||
let v = p.y - p.y.floor();
|
||||
let w = p.z - p.z.floor();
|
||||
@ -145,6 +144,6 @@ impl Perlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
perlin_interp(c, u, v, w)
|
||||
(1. + perlin_interp(c, u, v, w)) * 0.5
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,8 @@ use material::Lambertian;
|
||||
use material::Material;
|
||||
use material::Metal;
|
||||
use moving_sphere::MovingSphere;
|
||||
use noise::perlin::Perlin;
|
||||
use noise::NoiseType;
|
||||
use rect::XZRect;
|
||||
use renderer::Opt;
|
||||
use renderer::Scene;
|
||||
@ -127,7 +129,9 @@ pub fn new(opt: &Opt) -> Scene {
|
||||
)));
|
||||
|
||||
// Perlin noise sphere
|
||||
let pertext = NoiseTexture::with_scale(rng, 0.1);
|
||||
let noise_source = Perlin::new(rng);
|
||||
let noise_type = NoiseType::Scale(0.1);
|
||||
let pertext = NoiseTexture::new(noise_source, noise_type);
|
||||
list.push(Box::new(Sphere::new(
|
||||
[220., 280., 300.],
|
||||
80.,
|
||||
|
||||
@ -7,6 +7,8 @@ use hitable::Hit;
|
||||
use hitable_list::HitableList;
|
||||
use kdtree::KDTree;
|
||||
use material::Lambertian;
|
||||
use noise::perlin::Perlin;
|
||||
use noise::NoiseType;
|
||||
use renderer::Opt;
|
||||
use renderer::Scene;
|
||||
use sphere::Sphere;
|
||||
@ -34,7 +36,9 @@ pub fn new(opt: &Opt) -> Scene {
|
||||
);
|
||||
// TODO(wathiede): Use XOR rng for predictability?
|
||||
let rng = &mut rand::thread_rng();
|
||||
let pertext: Arc<Texture> = Arc::new(NoiseTexture::with_scale(rng, 10.));
|
||||
let noise_source = Perlin::new(rng);
|
||||
let noise_type = NoiseType::Scale(10.);
|
||||
let pertext: Arc<Texture> = Arc::new(NoiseTexture::new(noise_source, noise_type));
|
||||
|
||||
let objects: Vec<Box<Hit>> = vec![
|
||||
Box::new(Sphere::new(
|
||||
|
||||
@ -7,6 +7,8 @@ use hitable_list::HitableList;
|
||||
use kdtree::KDTree;
|
||||
use material::DiffuseLight;
|
||||
use material::Lambertian;
|
||||
use noise::perlin::Perlin;
|
||||
use noise::NoiseType;
|
||||
use rect::XYRect;
|
||||
use rect::XZRect;
|
||||
use rect::YZRect;
|
||||
@ -45,6 +47,8 @@ pub fn new(opt: &Opt) -> Scene {
|
||||
|
||||
let world_image_bytes = include_bytes!("../../images/world.jpg");
|
||||
let it = ImageTexture::new(image::load_from_memory(world_image_bytes).unwrap().to_rgb());
|
||||
let noise_source = Perlin::new(rng);
|
||||
let noise_type = NoiseType::Scale(10.);
|
||||
let objects: Vec<Box<Hit>> = vec![
|
||||
// Big sphere
|
||||
Box::new(Sphere::new(Vec3::new(0., 2., 0.), 2.0, Lambertian::new(it))),
|
||||
@ -53,7 +57,7 @@ pub fn new(opt: &Opt) -> Scene {
|
||||
Vec3::new(0., -1000., 0.),
|
||||
1000.,
|
||||
// Box::new(Lambertian::new(ground_color)),
|
||||
Lambertian::new(NoiseTexture::with_scale(rng, 10.)),
|
||||
Lambertian::new(NoiseTexture::new(noise_source, noise_type)),
|
||||
)),
|
||||
Box::new(XZRect::new(
|
||||
-100.,
|
||||
|
||||
@ -1,39 +1,45 @@
|
||||
use rand;
|
||||
|
||||
use perlin::Perlin;
|
||||
use noise::NoiseSource;
|
||||
use noise::NoiseType;
|
||||
use texture::Texture;
|
||||
use vec3::Vec3;
|
||||
|
||||
pub struct NoiseTexture {
|
||||
scale: f32,
|
||||
perlin: Perlin,
|
||||
pub struct NoiseTexture<N>
|
||||
where
|
||||
N: NoiseSource,
|
||||
{
|
||||
noise_source: N,
|
||||
noise_type: NoiseType,
|
||||
}
|
||||
|
||||
impl NoiseTexture {
|
||||
pub fn new<R>(rng: &mut R) -> NoiseTexture
|
||||
where
|
||||
R: rand::Rng,
|
||||
{
|
||||
NoiseTexture::with_scale(rng, 1.)
|
||||
}
|
||||
pub fn with_scale<R>(rng: &mut R, scale: f32) -> NoiseTexture
|
||||
where
|
||||
R: rand::Rng,
|
||||
{
|
||||
impl<N> NoiseTexture<N>
|
||||
where
|
||||
N: NoiseSource,
|
||||
{
|
||||
pub fn new(noise_source: N, noise_type: NoiseType) -> NoiseTexture<N> {
|
||||
NoiseTexture {
|
||||
scale,
|
||||
perlin: Perlin::new(rng),
|
||||
noise_source,
|
||||
noise_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Texture for NoiseTexture {
|
||||
impl<N> Texture for NoiseTexture<N>
|
||||
where
|
||||
N: NoiseSource,
|
||||
{
|
||||
fn value(&self, _u: f32, _v: f32, p: Vec3) -> Vec3 {
|
||||
Vec3::new(1., 1., 1.) * 0.5 * (1. + self.perlin.noise(self.scale * p))
|
||||
let v = match self.noise_type {
|
||||
NoiseType::Scale(scale) => self.noise_source.scaled(p, scale),
|
||||
|
||||
//Vec3::new(1., 1., 1.) * turb(self.scale * p, 7)
|
||||
//Vec3::new(1., 1., 1.) * 0.5 * (1. + turb(self.scale * p, 7))
|
||||
//Vec3::new(1., 1., 1.) * 0.5 * (1. + (self.scale * p.z + 10. * self.perlin.turb(p, 7)).sin())
|
||||
//Vec3::new(1., 1., 1.) * 0.5 * (1. + GENERATOR.noise(self.scale * p))
|
||||
NoiseType::Turbulence(turbulence) => self.noise_source.turbulence(p, turbulence),
|
||||
NoiseType::Marble {
|
||||
period,
|
||||
power,
|
||||
size,
|
||||
} => self.noise_source.marble(p, period, power, size),
|
||||
};
|
||||
debug_assert!(v >= 0., "Cold pixel @ {}: {}", p, v);
|
||||
debug_assert!(v <= 1., "Hot pixel @ {}: {}", p, v);
|
||||
Vec3::new(1., 1., 1.) * v
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,14 @@ impl Vec3 {
|
||||
Vec3 { x, y, z }
|
||||
}
|
||||
|
||||
pub fn min(self) -> f32 {
|
||||
self.x.min(self.y).min(self.z)
|
||||
}
|
||||
|
||||
pub fn max(self) -> f32 {
|
||||
self.x.max(self.y).max(self.z)
|
||||
}
|
||||
|
||||
pub fn length(self) -> f32 {
|
||||
self.squared_length().sqrt()
|
||||
}
|
||||
|
||||
@ -6,6 +6,19 @@
|
||||
<link rel="stylesheet" type="text/css" href="style.css" media="screen" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
{% if noise_source == NoiseSource::Perlin %}
|
||||
Perlin
|
||||
{% else %}
|
||||
<a href="/perlin">Perlin</a>
|
||||
{% endif %}
|
||||
/
|
||||
{% if noise_source == NoiseSource::Lode %}
|
||||
Lode
|
||||
{% else %}
|
||||
<a href="/lode">Lode</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% for s in specimens %}
|
||||
<h2>{{ s.title }}</h2>
|
||||
{% for np in s.params %}
|
||||
@ -16,7 +29,7 @@
|
||||
</a>
|
||||
<figcaption>
|
||||
<table>
|
||||
{% for p in np.noise.parameters() %}
|
||||
{% for p in np.noise_type.parameters() %}
|
||||
<tr><th>{{ p.0 }}</th><td>{{ p.1 }}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user