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:
Bill Thiede 2018-10-14 15:09:57 -07:00
parent accfd09ce4
commit 5faba9cf26
12 changed files with 286 additions and 161 deletions

View File

@ -22,3 +22,4 @@ serde_derive = "1.0.79"
# For better profiling support.
[profile.release]
debug = true

View File

@ -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 }"
);
}
}

View File

@ -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;

View File

@ -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
View 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()),
],
}
}
}

View File

@ -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
}
}

View File

@ -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.,

View File

@ -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(

View File

@ -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.,

View File

@ -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
}
}

View File

@ -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()
}

View File

@ -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>