rtiow: break project into multiple workspaces.
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
33
rtiow/renderer/src/texture/checker.rs
Normal file
33
rtiow/renderer/src/texture/checker.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use crate::texture::Texture;
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
pub struct CheckerTexture<T>
|
||||
where
|
||||
T: Texture,
|
||||
{
|
||||
odd: T,
|
||||
even: T,
|
||||
}
|
||||
|
||||
impl<T> CheckerTexture<T>
|
||||
where
|
||||
T: Texture,
|
||||
{
|
||||
pub fn new(odd: T, even: T) -> CheckerTexture<T> {
|
||||
CheckerTexture { odd, even }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Texture for CheckerTexture<T>
|
||||
where
|
||||
T: Texture,
|
||||
{
|
||||
fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3 {
|
||||
let sines = (10. * p.x).sin() * (10. * p.y).sin() * (10. * p.z).sin();
|
||||
if sines < 0. {
|
||||
self.odd.value(u, v, p)
|
||||
} else {
|
||||
self.even.value(u, v, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
24
rtiow/renderer/src/texture/constant.rs
Normal file
24
rtiow/renderer/src/texture/constant.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use crate::texture::Texture;
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ConstantTexture {
|
||||
color: Vec3,
|
||||
}
|
||||
|
||||
impl ConstantTexture {
|
||||
pub fn new<V>(color: V) -> ConstantTexture
|
||||
where
|
||||
V: Into<Vec3>,
|
||||
{
|
||||
ConstantTexture {
|
||||
color: color.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Texture for ConstantTexture {
|
||||
fn value(&self, _u: f32, _v: f32, _p: Vec3) -> Vec3 {
|
||||
self.color
|
||||
}
|
||||
}
|
||||
26
rtiow/renderer/src/texture/envmap.rs
Normal file
26
rtiow/renderer/src/texture/envmap.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use std::f32;
|
||||
|
||||
use image::RgbImage;
|
||||
|
||||
use crate::texture::ImageTexture;
|
||||
use crate::texture::Texture;
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EnvMap {
|
||||
img_tex: ImageTexture,
|
||||
}
|
||||
|
||||
impl EnvMap {
|
||||
pub fn new(img: RgbImage) -> EnvMap {
|
||||
EnvMap {
|
||||
img_tex: ImageTexture::new(img),
|
||||
}
|
||||
}
|
||||
pub fn color(&self, ray: Vec3) -> Vec3 {
|
||||
let zero = Vec3::new(0., 0., 0.);
|
||||
let u = ray.x.atan2(ray.z) / f32::consts::PI /2.0 + 0.5;
|
||||
let v = ray.y / 2.0 + 0.5;
|
||||
self.img_tex.value(u, v, zero)
|
||||
}
|
||||
}
|
||||
48
rtiow/renderer/src/texture/image.rs
Normal file
48
rtiow/renderer/src/texture/image.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use image::RgbImage;
|
||||
|
||||
use crate::texture::Texture;
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ImageTexture {
|
||||
img: RgbImage,
|
||||
width: f32,
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl ImageTexture {
|
||||
pub fn new(img: RgbImage) -> ImageTexture {
|
||||
let (w, h) = img.dimensions();
|
||||
ImageTexture {
|
||||
img,
|
||||
width: f32::from(w.min(64000) as u16),
|
||||
height: f32::from(h.min(64000) as u16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Texture for ImageTexture {
|
||||
fn value(&self, u: f32, v: f32, _p: Vec3) -> Vec3 {
|
||||
// Wrap texcoords by default.
|
||||
let x = (u % 1. * (self.width - 1.)) as u32;
|
||||
let y = ((1. - v % 1.) * (self.height - 1.)) as u32;
|
||||
if x >= self.width as u32 {
|
||||
panic!(format!(
|
||||
"u {} v {} x {} y {} w {} h {}",
|
||||
u, v, x, y, self.width, self.height
|
||||
));
|
||||
}
|
||||
if y >= self.height as u32 {
|
||||
panic!(format!(
|
||||
"u {} v {} x {} y {} w {} h {}",
|
||||
u, v, x, y, self.width, self.height
|
||||
));
|
||||
}
|
||||
let pixel = self.img.get_pixel(x, y);
|
||||
Vec3::new(
|
||||
f32::from(pixel[0]) / 255.,
|
||||
f32::from(pixel[1]) / 255.,
|
||||
f32::from(pixel[2]) / 255.,
|
||||
)
|
||||
}
|
||||
}
|
||||
79
rtiow/renderer/src/texture/mandelbrot.rs
Normal file
79
rtiow/renderer/src/texture/mandelbrot.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
use rand;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::texture::Texture;
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mandelbrot {
|
||||
scale: f32,
|
||||
palette: Vec<Vec3>,
|
||||
}
|
||||
|
||||
// HSV values in [0..1]
|
||||
// returns [r, g, b] values from 0 to 255
|
||||
//From https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
|
||||
fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Vec3 {
|
||||
let h_i = (h * 6.) as i32;
|
||||
let f = h * 6. - h_i as f32;
|
||||
let p = v * (1. - s);
|
||||
let q = v * (1. - f * s);
|
||||
let t = v * (1. - (1. - f) * s);
|
||||
match h_i {
|
||||
0 => Vec3::new(v, t, p),
|
||||
1 => Vec3::new(q, v, p),
|
||||
2 => Vec3::new(p, v, t),
|
||||
3 => Vec3::new(p, q, v),
|
||||
4 => Vec3::new(t, p, v),
|
||||
5 => Vec3::new(v, p, q),
|
||||
_ => panic!(format!("Unknown H value {}", h_i)),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_palette(num: usize) -> Vec<Vec3> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut random = || rng.gen_range(0.0, 0.1);
|
||||
// use golden ratio
|
||||
let golden_ratio_conjugate = 0.618_034;
|
||||
let mut h = random();
|
||||
(0..num)
|
||||
.map(|_| {
|
||||
h += golden_ratio_conjugate;
|
||||
h %= 1.0;
|
||||
hsv_to_rgb(h, 0.99, 0.99)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Default for Mandelbrot {
|
||||
fn default() -> Self {
|
||||
Mandelbrot {
|
||||
scale: 2.0,
|
||||
palette: generate_palette(10),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Texture for Mandelbrot {
|
||||
fn value(&self, u: f32, v: f32, _p: Vec3) -> Vec3 {
|
||||
// scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1))
|
||||
let x0 = u * 3.5 - 2.5;
|
||||
// scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1))
|
||||
let y0 = v * 2.0 - 1.0;
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
let mut iteration = 0;
|
||||
let max_iteration = 1000;
|
||||
while (x * x + y * y) <= 2. * 2. && iteration < max_iteration {
|
||||
let xtemp = x * x - y * y + x0;
|
||||
y = 2. * x * y + y0;
|
||||
x = xtemp;
|
||||
iteration += 1;
|
||||
}
|
||||
if iteration == max_iteration {
|
||||
return Vec3::default();
|
||||
}
|
||||
self.palette[iteration % self.palette.len()]
|
||||
}
|
||||
}
|
||||
45
rtiow/renderer/src/texture/mod.rs
Normal file
45
rtiow/renderer/src/texture/mod.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
mod checker;
|
||||
mod constant;
|
||||
mod envmap;
|
||||
mod image;
|
||||
mod mandelbrot;
|
||||
mod noise;
|
||||
pub use crate::texture::checker::CheckerTexture;
|
||||
pub use crate::texture::constant::ConstantTexture;
|
||||
pub use crate::texture::envmap::EnvMap;
|
||||
pub use crate::texture::image::ImageTexture;
|
||||
pub use crate::texture::mandelbrot::Mandelbrot;
|
||||
pub use crate::texture::noise::NoiseTexture;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
pub trait Texture: Send + Sync {
|
||||
fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3;
|
||||
}
|
||||
|
||||
impl Texture for Arc<dyn Texture> {
|
||||
fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3 {
|
||||
(**self).value(u, v, p)
|
||||
}
|
||||
}
|
||||
|
||||
impl Texture for Box<dyn Texture> {
|
||||
fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3 {
|
||||
(**self).value(u, v, p)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn constant_texture_from_array() {
|
||||
assert_eq!(
|
||||
ConstantTexture::new(Vec3::new(1., 2., 3.)),
|
||||
ConstantTexture::new([1., 2., 3.])
|
||||
);
|
||||
}
|
||||
}
|
||||
46
rtiow/renderer/src/texture/noise.rs
Normal file
46
rtiow/renderer/src/texture/noise.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use crate::noise::NoiseSource;
|
||||
use crate::noise::NoiseType;
|
||||
use crate::texture::Texture;
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
pub struct NoiseTexture<N>
|
||||
where
|
||||
N: NoiseSource,
|
||||
{
|
||||
noise_source: N,
|
||||
noise_type: NoiseType,
|
||||
}
|
||||
|
||||
impl<N> NoiseTexture<N>
|
||||
where
|
||||
N: NoiseSource,
|
||||
{
|
||||
pub fn new(noise_source: N, noise_type: NoiseType) -> NoiseTexture<N> {
|
||||
NoiseTexture {
|
||||
noise_source,
|
||||
noise_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> Texture for NoiseTexture<N>
|
||||
where
|
||||
N: NoiseSource,
|
||||
{
|
||||
fn value(&self, _u: f32, _v: f32, p: Vec3) -> Vec3 {
|
||||
let v = match self.noise_type {
|
||||
NoiseType::Scale(scale) => self.noise_source.scaled(p, scale),
|
||||
|
||||
NoiseType::Turbulence(turbulence) => self.noise_source.turbulence(p, turbulence),
|
||||
NoiseType::Marble {
|
||||
period,
|
||||
power,
|
||||
size,
|
||||
scale,
|
||||
} => self.noise_source.marble(p, period, power, size, scale),
|
||||
};
|
||||
debug_assert!(v >= 0., "Cold pixel @ {}: {}", p, v);
|
||||
debug_assert!(v <= 1., "Hot pixel @ {}: {}", p, v);
|
||||
Vec3::new(1., 1., 1.) * v
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user