diff --git a/prod/tsm.png b/prod/tsm.png new file mode 100644 index 0000000..51f802e Binary files /dev/null and b/prod/tsm.png differ diff --git a/scenes/smolgalaxy.lua b/scenes/smolgalaxy.lua index 845a84d..57b644c 100644 --- a/scenes/smolgalaxy.lua +++ b/scenes/smolgalaxy.lua @@ -70,7 +70,7 @@ local function randomtorus() local thickness = util.randab(radius/4, radius) local orientation = randomorientation() - return obj.affinetransform(obj.torus(pos, radius, thickness), orientation, vec3.O) + return util.transform(obj.torus(pos, radius, thickness), orientation) end local function randomcuboid() @@ -86,13 +86,12 @@ local function randomcylinder() local height = util.randab(0, SCALE) local orientation = randomorientation() - return obj.affinetransform( + return util.transform( obj.intersection( obj.cylinder(pos, radius), obj.cuboid(pos, vec3.new(radius, height, radius)) ), - orientation, - vec3.O + orientation ) end diff --git a/scenes/tsm.lua b/scenes/tsm.lua new file mode 100644 index 0000000..5c51a8e --- /dev/null +++ b/scenes/tsm.lua @@ -0,0 +1,157 @@ +-- Scene developed by TSM71 +-- Also this took 30 hours to render + +local COLOR_BLACK = colorvec.new(0, 0, 0, 0) + +local GRAY = material.newfromdiagonal( + COLOR_BLACK, + colorvec.new(0.5, 0.5, 0.5, 0.5), + surfacetype.DIFFUSE +) +local CYAN = material.newfromdiagonal( + COLOR_BLACK, + colorvec.new(0, 1, 1, 0), + surfacetype.DIFFUSE +) +local PINK = material.newfromdiagonal( + COLOR_BLACK, + colorvec.new(1, .5, .5, 0), + surfacetype.DIFFUSE +) +local RED = material.newfromdiagonal( + COLOR_BLACK, + colorvec.new(1, 0, 0, 0), + surfacetype.DIFFUSE +) +local MAGENTA = material.newfromdiagonal( + COLOR_BLACK, + colorvec.new(1, 0, 1, 0), + surfacetype.DIFFUSE +) +local MIRROR = material.newfromdiagonal( + COLOR_BLACK, + colorvec.new(1, 1, 1, 1), + surfacetype.REFLECTIVE +) +local FLUORESCENT = material.new( + COLOR_BLACK, + colormat.new( + .25, 0, 0, .25, + 0, .25, 0, .75, + 0, 0, .25, 0, + 0, 0, 0, 0 + ), + surfacetype.DIFFUSE +) + +--[[ +obj.transformaround = function(a, b, c) + return util.translate( + util.transform( + util.translate( + a, + -c + ), + b + ), + c + ) +end +--]] +--obj.transformaround = function(a, _b, _c) return a end +--obj.affinetransform = function(a, _b, _c) return a end +--obj.smoothunion = function(a, b, _r) return obj.union(a, b) end +---[[ +util.stacktransforms = function(list) + --do return list[1] end + --do return list[#list] end + local n = #list + local a = mat3.I + --for i=n, 1, -1 do + for i=1, n do + a = list[i] * a + --a = a * list[i] + end + return a +end +--]] + +return obj.withlights( + obj.transformaround( + obj.withlights( + util.union({ + util.intersection({ + util.union({ + obj.withmaterial( + obj.negation( + util.union({ + obj.cuboid(vec3.new(0, 0, 0), vec3.new(.5, 1, 4)), + obj.cuboid(vec3.new(0, 0, 0), vec3.new(4, 1, .5)) + }) + ), + GRAY + ), + obj.withmaterial( + util.intersection({ + util.union({ + obj.cuboid(vec3.new(0, 1, 0), vec3.new(.4, .1, 3.9)), + obj.cuboid(vec3.new(0, 1, 0), vec3.new(3.9, .1, .4)) + }), + obj.negation( + util.union({ + obj.cuboid(vec3.new(0, 1, 0), vec3.new(.3, 1, 3.8)), + obj.cuboid(vec3.new(0, 1, 0), vec3.new(3.8, 1, .3)) + }) + ) + }), + FLUORESCENT + ), + obj.smoothunion( + obj.withmaterial( + obj.sphere(vec3.new(0, .75, 0), .25), + CYAN + ), + obj.withmaterial( -- withdynamicmaterial + obj.sphere(vec3.new(-.5, .5, 0), .25), + MAGENTA --dynamicmaterial.missingtexture(vec3.new(-.5, .5, 0)) + ), + .5 + ) + }), + obj.withmaterial( + obj.negation( + obj.cuboid(vec3.new(0, 0, 0), vec3.new(2, .25, 2)) + ), + PINK + ) + }), + obj.withmaterial( + obj.cylinder(vec3.new(1, 0, 0), .375), + RED + ), + util.transform( + obj.withmaterial( + obj.torus(vec3.new(0, 0, -1), .375, .125), + MIRROR + ), + transform.SWAPYZ + ) + }), + { + light.new(vec3.new(.45, 1-.2, -.45), colorvec.new(0, 0, 0, .1)), + --light.new(vec3.new(.45, 1-.2, -.45), colorvec.new(0, 0, 0, 1)), + } + ), + util.stacktransforms({ + transform.scalexyz(1, -1, 1), + transform.rotatez(10*math.pi/180), + }), + vec3.new(0, 0, -1.5) + ), + { + light.new(vec3.new(0, .9375, 0), colorvec.new(.5, .25, 0, 0)), + light.new(vec3.new(0, .9375, -.625), colorvec.new(0, 0, 1, 0)), + --light.new(vec3.new(0, .9375, 0), colorvec.new(5, 2.5, 0, 0)), + --light.new(vec3.new(0, .9375, -.625), colorvec.new(0, 0, 10, 0)), + } +) \ No newline at end of file diff --git a/src/consts.rs b/src/consts.rs index 082641a..0c6baed 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -20,21 +20,21 @@ pub const ANGLE_POWER: f64 = 2.; //pub const IMG_WIDTH: usize = 480; //pub const IMG_WIDTH: usize = 1280; //pub const IMG_WIDTH: usize = 1080; -//pub const IMG_WIDTH: usize = 1920; -pub const IMG_WIDTH: usize = 4961; +pub const IMG_WIDTH: usize = 1920; +//pub const IMG_WIDTH: usize = 4961; //pub const IMG_HEIGHT: usize = 480; //pub const IMG_HEIGHT: usize = 720; -//pub const IMG_HEIGHT: usize = 1080; -pub const IMG_HEIGHT: usize = 3508; +pub const IMG_HEIGHT: usize = 1080; +//pub const IMG_HEIGHT: usize = 3508; pub const IMG_DIM: usize = if IMG_HEIGHT > IMG_WIDTH { IMG_HEIGHT } else { IMG_WIDTH }; pub const IMG_SIZE: usize = IMG_WIDTH * IMG_HEIGHT; pub const IMG_BYTE_SIZE: usize = IMG_SIZE * 3; -pub const SUPERSAMPLING: usize = 1; -//pub const SUPERSAMPLING: usize = 2; -pub const RAYS_PER_PIXEL: usize = 1; +//pub const SUPERSAMPLING: usize = 1; +pub const SUPERSAMPLING: usize = 2; +//pub const RAYS_PER_PIXEL: usize = 1; //pub const RAYS_PER_PIXEL: usize = 50; -//pub const RAYS_PER_PIXEL: usize = 500; +pub const RAYS_PER_PIXEL: usize = 500; //pub const MAX_BOUNCES: u32 = 1; //pub const MAX_BOUNCES: u32 = 4; //pub const MAX_BOUNCES: u32 = 8; diff --git a/src/lua/mat3.rs b/src/lua/mat3.rs index cb763ba..10d11d7 100644 --- a/src/lua/mat3.rs +++ b/src/lua/mat3.rs @@ -7,7 +7,7 @@ impl UserData for Mat3 { methods.add_meta_method(MetaMethod::Sub, |ctx, a: &Self, b: Self| Ok(*a-b)); methods.add_meta_method(MetaMethod::Mul, |ctx, a: &Self, b: Self| Ok(*a*b)); methods.add_meta_method(MetaMethod::Unm, |ctx, a: &Self, b: ()| Ok(-*a)); - methods.add_meta_method(MetaMethod::Mul, |ctx, a: &Self, b: f64| Ok(*a*b)); + //methods.add_meta_method(MetaMethod::Mul, |ctx, a: &Self, b: f64| Ok(*a*b)); //TODO find a way to make it cohabit methods.add_meta_method(MetaMethod::Div, |ctx, a: &Self, b: f64| Ok(*a/b)); methods.add_method("a", |ctx, x, ()| Ok(x.a())); diff --git a/src/lua/obj.rs b/src/lua/obj.rs index f3eb2dc..b74369a 100644 --- a/src/lua/obj.rs +++ b/src/lua/obj.rs @@ -54,6 +54,10 @@ pub fn obj<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result> { @@ -26,11 +26,25 @@ pub fn transform<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result| { let mut acc = I3; for trans in transforms.iter().rev().cloned() { - acc = trans * I3; + acc = acc * trans; } Ok(acc) } diff --git a/src/lua/util.lua b/src/lua/util.lua index e1b4730..a3505d6 100644 --- a/src/lua/util.lua +++ b/src/lua/util.lua @@ -65,7 +65,7 @@ function M.transform(object, mat) return obj.affinetransform(object, mat, vec3.O) end function M.translate(object, vec) - return obj.affinetransform(object, mat3.O, vec) + return obj.affinetransform(object, mat3.I, vec) end return M \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c271ce3..a8ab5c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,9 +85,10 @@ fn default_scene3() -> Scene { fn main() { // get scene and camera - let scene = scene_from_file("scenes/randomspheres.lua".to_owned()).unwrap(); + let scene = scene_from_file("scenes/tsm.lua".to_owned()).unwrap(); //let scene = default_scene3(); - let cam = default_cam(); + let cam = Cam::new(Z * -1.5, Z, f64::sqrt(2.)); + //let cam = default_cam(); // get stats on the scene we're about to render let total_rpp = (RAYS_PER_PIXEL * SUPERSAMPLING * SUPERSAMPLING) as u64; diff --git a/src/object/mod.rs b/src/object/mod.rs index 10c082f..d89c77c 100644 --- a/src/object/mod.rs +++ b/src/object/mod.rs @@ -8,6 +8,7 @@ use std::vec::Vec; mod sphere; mod plane; mod union; +mod smoothunion; mod intersection; mod exclusion; mod negation; @@ -64,6 +65,7 @@ impl ObjClone for T { pub use sphere::Sphere; pub use plane::Plane; pub use union::Union; +pub use smoothunion::SmoothUnion; pub use intersection::Intersection; pub use exclusion::Exclusion; pub use negation::Negation; diff --git a/src/object/smoothunion.rs b/src/object/smoothunion.rs new file mode 100644 index 0000000..e4971e5 --- /dev/null +++ b/src/object/smoothunion.rs @@ -0,0 +1,42 @@ +use crate::object::Obj; +use crate::light::Light; +use crate::structs::Vec3; +use std::vec; +use crate::material::Material; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct SmoothUnion { + a: A, + b: B, + r: f64 +} + +impl SmoothUnion { + pub fn new(a: A, b: B, r: f64) -> SmoothUnion { + SmoothUnion { a, b, r } + } +} + +impl Obj for SmoothUnion { + fn distance_to(&self, point: Vec3) -> f64 { + let d1 = self.a.distance_to(point); + let d2 = self.b.distance_to(point); + let h = f64::max(self.r - f64::abs(d1-d2),0.); + d1.min(d2) - h*h*0.25 / self.r + } + fn material_at(&self, point: Vec3) -> Material { + if self.a.distance_to(point) < self.b.distance_to(point) { + self.a.material_at(point) + } else { + self.b.material_at(point) + } + } + fn get_lights(&self) -> vec::Vec { + let mut l = self.a.get_lights(); + l.extend(self.b.get_lights()); + l + } + fn node_count(&self) -> u32 { + self.a.node_count() + self.b.node_count() + 1 + } +} diff --git a/src/object/transform.rs b/src/object/transform.rs index 499f962..9221902 100644 --- a/src/object/transform.rs +++ b/src/object/transform.rs @@ -14,10 +14,10 @@ pub struct AffineTransform { impl AffineTransform { pub fn new(obj: T, transform: Mat3, translate: Vec3) -> AffineTransform { - AffineTransform { obj, transform, transform_inv: transform.invert(), translate, scale: transform.det().cbrt() } + AffineTransform { obj, transform, transform_inv: transform.invert(), translate, scale: transform.det().abs().cbrt() } } pub fn new_linear(obj: T, transform: Mat3) -> AffineTransform { - AffineTransform { obj, transform, transform_inv: transform.invert(), translate: O, scale: transform.det().cbrt() } + AffineTransform { obj, transform, transform_inv: transform.invert(), translate: O, scale: transform.det().abs().cbrt() } } pub fn new_translate(obj: T, translate: Vec3) -> AffineTransform { AffineTransform { obj, transform: I3, transform_inv: I3, translate, scale: 1. } @@ -59,6 +59,37 @@ pub const fn scale_x(k: f64) -> Mat3 { scale_xyz(k, 1., 1.) } pub const fn scale_y(k: f64) -> Mat3 { scale_xyz(1., k, 1.) } pub const fn scale_z(k: f64) -> Mat3 { scale_xyz(1., 1., k) } +pub fn rotate_x(a: f64) -> Mat3 { + let c = a.cos(); + let s = a.sin(); + Mat3::new( + 1., 0., 0., + 0., c, -s, + 0., s, c + ) +} +pub fn rotate_y(a: f64) -> Mat3 { + let c = a.cos(); + let s = a.sin(); + Mat3::new( + c, 0., s, + 0., 1., 0., + -s, 0., c + ) +} +pub fn rotate_z(a: f64) -> Mat3 { + let c = a.cos(); + let s = a.sin(); + Mat3::new( + c, -s, 0., + s, c, 0., + 0., 0., 1. + ) +} +pub fn rotate_xyz(x: f64, y: f64, z: f64) -> Mat3 { + rotate_z(z) * rotate_y(y) * rotate_x(x) +} + impl Obj for AffineTransform { fn distance_to(&self, point: Vec3) -> f64 { self.obj.distance_to(self.apply_rev(point)) * self.scale @@ -81,4 +112,50 @@ impl Obj for AffineTransform { fn node_count(&self) -> u32 { self.obj.node_count() + 1 } -} \ No newline at end of file +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct TransformAround { + obj: T, + transform: Mat3, + transform_inv: Mat3, + center: Vec3, + scale: f64 +} + +impl TransformAround { + pub fn new(obj: T, transform: Mat3, center: Vec3) -> TransformAround { + TransformAround { obj, transform, transform_inv: transform.invert(), center, scale: transform.det().abs().cbrt() } + } + + fn apply_rev(&self, point: Vec3) -> Vec3 { + self.transform_inv * (point + self.center) - self.center + } + fn apply_fwd(&self, point: Vec3) -> Vec3 { + self.transform * (point - self.center) + self.center + } +} + +impl Obj for TransformAround { + fn distance_to(&self, point: Vec3) -> f64 { + self.obj.distance_to(self.apply_rev(point)) * self.scale + } + fn normal_at(&self, point: Vec3) -> Vec3 { + self.apply_fwd(self.obj.normal_at(self.apply_rev(point))).unit() + } + fn material_at(&self, point: Vec3) -> Material { + self.obj.material_at(self.apply_rev(point)) + } + fn get_lights(&self) -> Vec { + self.obj + .get_lights() + .into_iter() + .map(|light| { + Light::new(self.apply_fwd(light.pos()), light.color()) + }) + .collect() + } + fn node_count(&self) -> u32 { + self.obj.node_count() + 1 + } +} diff --git a/src/structs/mat3.rs b/src/structs/mat3.rs index aa3af00..6ea1e0f 100644 --- a/src/structs/mat3.rs +++ b/src/structs/mat3.rs @@ -137,7 +137,7 @@ impl Mul for Mat3 { d: self.d*other.a + self.e*other.d + self.f*other.g, e: self.d*other.b + self.e*other.e + self.f*other.h, - f: self.d*other.c + self.e*other.f + self.e*other.i, + f: self.d*other.c + self.e*other.f + self.f*other.i, g: self.g*other.a + self.h*other.d + self.i*other.g, h: self.g*other.b + self.h*other.e + self.i*other.h, diff --git a/test b/test index 05bc9e0..bbe9dfb 100755 --- a/test +++ b/test @@ -1,7 +1,8 @@ #!/bin/bash +[ -z "$SEED" ] && export SEED=0 clear cargo build --release && \ clear && \ - time SEED=0 target/release/rmarcher && \ + time target/release/rmarcher && \ printf '\n' && \ kitty +kitten icat --align=left a.png