more lua support, fixed scene 3

main
Codinget 3 years ago
parent 694fdf8e2b
commit fcde73c1ef
  1. 2
      README.md
  2. BIN
      prod/3.png
  3. 33
      scenes/randomspheres.lua
  4. 45
      scenes/smolgalaxy.lua
  5. 4
      src/lua/color.rs
  6. 2
      src/lua/light.rs
  7. 2
      src/lua/mat3.rs
  8. 4
      src/lua/material.rs
  9. 31
      src/lua/mod.rs
  10. 8
      src/lua/obj.rs
  11. 40
      src/lua/transform.rs
  12. 71
      src/lua/util.lua
  13. 5
      src/lua/util.rs
  14. 2
      src/lua/vec3.rs
  15. 6
      src/main.rs
  16. 2
      src/object/mod.rs
  17. 11
      src/object/transform.rs
  18. 36
      src/object/with_lights.rs
  19. 2
      test

@ -19,7 +19,6 @@ A ray marching renderer in rust
## What is planned
- Testing of more shapes
- Support for a lua-based scene representation DSL
- Support for controlling the whole application from lua
- Support for linking against a `scene.so` exporting a scene
- Support for using as a library
@ -27,6 +26,7 @@ A ray marching renderer in rust
## Examples
![1st test scene](prod/1.png)
![2nd test scene](prod/2.png)
![3rd test scene](prod/3.png)
![randomly generated spheres](prod/randomspheres.png)
![randomly generated objects](prod/smolgalaxy.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

@ -1,28 +1,20 @@
local unpack = table.unpack or _G.unpack
math.randomseed(os.time())
local function randab(a, b)
return math.random() * (b-a) + a
end
util.seed()
local function randtable(n, a, b)
local t = {}
for i=1, n do
t[i] = randab(a, b)
t[i] = util.randab(a, b)
end
return t
end
local function choice(list)
return list[math.random(1, #list)]
end
local function randomsphere()
local pos = vec3.new(unpack(randtable(3, -2, 2)))
local radius = randab(0, 0.5)
local radius = util.randab(0, 0.5)
local emission = colorvec.new(unpack(randtable(4, 0, 1)))
local reflection = colormat.new(unpack(randtable(16, 0, 1)))
local surfacetype = choice({surfacetype.DIFFUSE, surfacetype.REFLECTIVE, surfacetype.STOP})
local surfacetype = util.choice({surfacetype.DIFFUSE, surfacetype.REFLECTIVE, surfacetype.STOP})
return obj.withmaterial(
obj.sphere(pos, radius),
@ -30,23 +22,8 @@ local function randomsphere()
)
end
local function union(objs)
local n = #objs
if n == 1 then return objs[1] end
local mid = n//2
local left, right = {}, {}
for i=1, mid do
left[i] = objs[i]
end
for i=mid+1, n do
right[i-mid] = objs[i]
end
return obj.union(union(left), union(right))
end
local spheres = {}
for i=1, 100 do
spheres[i] = randomsphere()
end
return union(spheres)
return util.union(spheres)

@ -1,30 +1,22 @@
local unpack = table.unpack or _G.unpack
math.randomseed(os.time())
util.seed()
local SCALE = 0.3
local N_EACH = 25
local function randab(a, b)
return math.random() * (b-a) + a
end
local function randtable(n, a, b)
local t = {}
for i=1, n do
t[i] = randab(a, b)
t[i] = util.randab(a, b)
end
return t
end
local function choice(list)
return list[math.random(1, #list)]
end
local MATERIALS = {surfacetype.DIFFUSE, surfacetype.REFLECTIVE, surfacetype.STOP}
local function randommaterial()
local emission = colorvec.new(unpack(randtable(4, 0, 1)))
local reflection = colormat.new(unpack(randtable(16, 0, 1)))
local surfacetype = choice(MATERIALS)
local surfacetype = util.choice(MATERIALS)
return material.new(emission, reflection, surfacetype)
end
@ -62,20 +54,20 @@ local ORIENTATIONS = {
)
}
local function randomorientation()
return choice(ORIENTATIONS)
return util.choice(ORIENTATIONS)
end
local function randomsphere()
local pos = vec3.new(unpack(randtable(3, -2, 2)))
local radius = randab(0, SCALE)
local radius = util.randab(0, SCALE)
return obj.sphere(pos, radius)
end
local function randomtorus()
local pos = vec3.new(unpack(randtable(3, -2, 2)))
local radius = randab(0, SCALE)
local thickness = randab(radius/4, radius)
local radius = util.randab(0, SCALE)
local thickness = util.randab(radius/4, radius)
local orientation = randomorientation()
return obj.affinetransform(obj.torus(pos, radius, thickness), orientation, vec3.O)
@ -83,15 +75,15 @@ end
local function randomcuboid()
local pos = vec3.new(unpack(randtable(3, -2, 2)))
local radius = randab(0, SCALE)
local radius = util.randab(0, SCALE)
return obj.cuboid(pos, vec3.new(radius, radius, radius))
end
local function randomcylinder()
local pos = vec3.new(unpack(randtable(3, -2, 2)))
local radius = randab(0, SCALE)
local height = randab(0, SCALE)
local radius = util.randab(0, SCALE)
local height = util.randab(0, SCALE)
local orientation = randomorientation()
return obj.affinetransform(
@ -104,25 +96,10 @@ local function randomcylinder()
)
end
local function union(objs)
local n = #objs
if n == 1 then return objs[1] end
local mid = n//2
local left, right = {}, {}
for i=1, mid do
left[i] = objs[i]
end
for i=mid+1, n do
right[i-mid] = objs[i]
end
return obj.union(union(left), union(right))
end
local objects = {}
for i=1, N_EACH do table.insert(objects, randomsphere()) end
for i=1, N_EACH do table.insert(objects, randomtorus()) end
for i=1, N_EACH do table.insert(objects, randomcuboid()) end
for i=1, N_EACH do table.insert(objects, randomcylinder()) end
for i, object in ipairs(objects) do objects[i] = obj.withmaterial(object, randommaterial()) end
return union(objects)
return util.union(objects)

@ -4,7 +4,7 @@ use rlua::{UserData, Context, Table};
impl UserData for ColorVec {}
impl UserData for ColorMat {}
pub fn color_vec(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn color_vec<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("new", ctx.create_function(
@ -16,7 +16,7 @@ pub fn color_vec(ctx: Context, _: ()) -> rlua::Result<Table> {
Ok(module)
}
pub fn color_mat(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn color_mat<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("new", ctx.create_function(

@ -3,7 +3,7 @@ use rlua::{UserData, Context, Table};
impl UserData for Light {}
pub fn light(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn light<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("new", ctx.create_function(

@ -29,7 +29,7 @@ impl UserData for Mat3 {
}
}
pub fn mat3(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn mat3<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("new", ctx.create_function(

@ -4,7 +4,7 @@ use rlua::{UserData, Context, Table};
impl UserData for SurfaceType {}
impl UserData for Material {}
pub fn surface_type(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn surface_type<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("DIFFUSE", SurfaceType::Diffuse)?;
@ -14,7 +14,7 @@ pub fn surface_type(ctx: Context, _: ()) -> rlua::Result<Table> {
Ok(module)
}
pub fn material(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn material<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("new", ctx.create_function(

@ -6,6 +6,8 @@ mod mat3;
mod color;
mod material;
mod light;
mod transform;
mod util;
pub use obj::{LuaObject, obj};
pub use vec3::vec3;
@ -13,25 +15,28 @@ pub use mat3::mat3;
pub use color::{color_vec, color_mat};
pub use material::{surface_type, material};
pub use light::light;
pub use transform::transform;
pub use util::util;
pub fn env(ctx: Context, _: ()) -> rlua::Result<Table> {
let module = ctx.create_table()?;
pub fn add_scene_env<'lua>(ctx: Context<'lua>, env: Table<'lua>) -> rlua::Result<Table<'lua>> {
env.set("obj", obj(ctx, env.clone())?)?;
env.set("vec3", vec3(ctx, env.clone())?)?;
env.set("mat3", mat3(ctx, env.clone())?)?;
env.set("colorvec", color_vec(ctx, env.clone())?)?;
env.set("colormat", color_mat(ctx, env.clone())?)?;
env.set("surfacetype", surface_type(ctx, env.clone())?)?;
env.set("material", material(ctx, env.clone())?)?;
env.set("light", light(ctx, env.clone())?)?;
env.set("transform", transform(ctx, env.clone())?)?;
env.set("util", util(ctx, env.clone())?)?;
module.set("obj", obj(ctx, ())?)?;
module.set("vec3", vec3(ctx, ())?)?;
module.set("mat3", mat3(ctx, ())?)?;
module.set("colorvec", color_vec(ctx, ())?)?;
module.set("colormat", color_mat(ctx, ())?)?;
module.set("surfacetype", surface_type(ctx, ())?)?;
module.set("material", material(ctx, ())?)?;
module.set("light", light(ctx, ())?)?;
Ok(module)
Ok(env)
}
pub fn scene_from_file(file: String) -> rlua::Result<LuaObject> {
Lua::new().context(|ctx| {
let env = env(ctx, ())?;
let env = ctx.create_table()?;
add_scene_env(ctx, env.clone())?;
let meta = ctx.create_table()?;
meta.set("__index", ctx.globals())?;
env.set_metatable(Some(meta));

@ -27,7 +27,7 @@ impl Obj for LuaObject {
impl UserData for LuaObject {}
pub fn obj(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn obj<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("cuboid", ctx.create_function(
@ -70,10 +70,14 @@ pub fn obj(ctx: Context, _: ()) -> rlua::Result<Table> {
|ctx, ()| LuaObject::new(Waves::new())
)?)?;
module.set("withlights", ctx.create_function(
module.set("withlight", ctx.create_function(
|ctx, (obj, light): (LuaObject, Light)| LuaObject::new(WithLights::new_one(obj.get(), light))
)?)?;
module.set("withlights", ctx.create_function(
|ctx, (obj, lights): (LuaObject, Vec<Light>)| LuaObject::new(WithAnyLights::new(obj.get(), lights))
)?)?;
module.set("withmaterial", ctx.create_function(
|ctx, (obj, material): (LuaObject, Material)| LuaObject::new(WithMaterial::new(obj.get(), material))
)?)?;

@ -0,0 +1,40 @@
use rlua::{Context, Table, Error};
use crate::object::{SWAP_XY, SWAP_YZ, SWAP_XZ, scale_xyz, scale, scale_x, scale_y, scale_z};
use crate::structs::{Mat3, I3};
pub fn transform<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("SWAPXY", SWAP_XY)?;
module.set("SWAPXZ", SWAP_XZ)?;
module.set("SWAPYZ", SWAP_YZ)?;
module.set("scalexyz", ctx.create_function(
|ctx, (x, y, z)| Ok(scale_xyz(x, y, z))
)?)?;
module.set("scale", ctx.create_function(
|ctx, k| Ok(scale(k))
)?)?;
module.set("scalex", ctx.create_function(
|ctx, k| Ok(scale_x(k))
)?)?;
module.set("scaley", ctx.create_function(
|ctx, k| Ok(scale_y(k))
)?)?;
module.set("scalez", ctx.create_function(
|ctx, k| Ok(scale_z(k))
)?)?;
module.set("stack", ctx.create_function(
|ctx, transforms: Vec<Mat3>| {
let mut acc = I3;
for trans in transforms.iter().rev().cloned() {
acc = trans * I3;
}
Ok(acc)
}
)?)?;
Ok(module)
}

@ -0,0 +1,71 @@
local env = ...
local obj = env.obj
local vec3 = env.vec3
local mat3 = env.mat3
local transform = env.transform
local M = {}
-- technically a binary fold
local function binstack(objs, fn)
local n = #objs
if n == 1 then return objs[1] end
local mid = n//2
local left, right = {}, {}
for i=1, mid do
left[i] = objs[i]
end
for i=mid+1, n do
right[i-mid] = objs[i]
end
return fn(binstack(left, fn), binstack(right, fn))
end
-- technically a fold
local function linstack(objs, fn)
local head = objs[1]
for i=2, #objs do
head = fn(head, objs[i])
end
return head
end
-- random functions
local SEED = os.getenv "SEED" or os.time()
function M.seed()
math.randomseed(SEED)
end
function M.randab(a, b)
return math.random() * (b-a) + a
end
function M.choice(list)
return list[math.random(1, #list)]
end
-- multiple object composition
function M.union(objs)
return binstack(objs, obj.union)
end
function M.intersection(objs)
return binstack(objs, obj.intersection)
end
function M.smoothunion(objs, k)
return linstack(objs, function(a, b) return obj.smoothunion(a, b, k) end)
end
function M.smoothintersection(objs, k)
return linstack(objs, function(a, b) return obj.smoothintersection(a, b, k) end)
end
-- simpler transformations
function M.scale(object, k)
return obj.affinetransform(object, transform.scale(k), vec3.O)
end
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)
end
return M

@ -0,0 +1,5 @@
use rlua::{Context, Table};
pub fn util<'lua>(ctx: Context<'lua>, env: Table<'lua>) -> rlua::Result<Table<'lua>> {
ctx.load(include_str!("util.lua")).call(env)
}

@ -24,7 +24,7 @@ impl UserData for Vec3 {
}
}
pub fn vec3(ctx: Context, _: ()) -> rlua::Result<Table> {
pub fn vec3<'lua>(ctx: Context<'lua>, _env: Table<'lua>) -> rlua::Result<Table<'lua>> {
let module = ctx.create_table()?;
module.set("new", ctx.create_function(

@ -68,8 +68,7 @@ fn default_scene2() -> Scene {
}
fn default_scene3() -> Scene {
//TODO fix this scene
let s1 = WithMaterial::new(Sphere::new_xyz(4., 0., 0., 1.), WHITE);
let s1 = WithMaterial::new(Sphere::new_xyz(4., 0., 0., 1.), MIRROR);
let s2 = WithMaterial::new(Sphere::new_xyz(3., 1., 1., 0.5), GREEN);
let navion = WithMaterial::new(Plane::new_xyz(0., 1., -1., 3.), BLUE);
let backwall = WithMaterial::new(Plane::new_xyz(-1., -1., -0.5, 8.), RED);
@ -86,7 +85,8 @@ fn default_scene3() -> Scene {
fn main() {
// get scene and camera
let scene = scene_from_file("scenes/smolgalaxy.lua".to_owned()).unwrap();
//let scene = scene_from_file("scenes/randomspheres.lua".to_owned()).unwrap();
let scene = default_scene3();
let cam = default_cam();
// get stats on the scene we're about to render

@ -70,6 +70,6 @@ pub use cylinder::Cylinder;
pub use torus::Torus;
pub use waves::Waves;
pub use with_material::{WithMaterial, WithDynamicMaterial};
pub use with_lights::{WithLights, WithLight};
pub use with_lights::{WithLights, WithLight, WithAnyLights};
pub use transform::*;
pub use scene::Scene;

@ -8,18 +8,19 @@ pub struct AffineTransform<T: Obj + Clone> {
obj: T,
transform: Mat3,
transform_inv: Mat3,
translate: Vec3
translate: Vec3,
scale: f64
}
impl<T: Obj + Clone> AffineTransform<T> {
pub fn new(obj: T, transform: Mat3, translate: Vec3) -> AffineTransform<T> {
AffineTransform { obj, transform, transform_inv: transform.invert(), translate }
AffineTransform { obj, transform, transform_inv: transform.invert(), translate, scale: transform.det().cbrt() }
}
pub fn new_linear(obj: T, transform: Mat3) -> AffineTransform<T> {
AffineTransform { obj, transform, transform_inv: transform.invert(), translate: O }
AffineTransform { obj, transform, transform_inv: transform.invert(), translate: O, scale: transform.det().cbrt() }
}
pub fn new_translate(obj: T, translate: Vec3) -> AffineTransform<T> {
AffineTransform { obj, transform: I3, transform_inv: I3, translate }
AffineTransform { obj, transform: I3, transform_inv: I3, translate, scale: 1. }
}
fn apply_rev(&self, point: Vec3) -> Vec3 {
@ -60,7 +61,7 @@ pub const fn scale_z(k: f64) -> Mat3 { scale_xyz(1., 1., k) }
impl<T: Obj + Clone> Obj for AffineTransform<T> {
fn distance_to(&self, point: Vec3) -> f64 {
self.obj.distance_to(self.apply_rev(point))
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()

@ -41,4 +41,40 @@ impl<T: Obj> WithLight<T> {
pub fn new_one(obj: T, light: Light) -> WithLight<T> {
WithLight { obj, lights: [light; 1] }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WithAnyLights<T: Obj> {
obj: T,
lights: Vec<Light>
}
impl<T: Obj> WithAnyLights<T> {
pub fn new(obj: T, lights: Vec<Light>) -> WithAnyLights<T> {
WithAnyLights { obj, lights }
}
pub fn add_light(&mut self, light: Light) {
self.lights.push(light)
}
}
impl<T: Obj> Obj for WithAnyLights<T> {
fn distance_to(&self, point: Vec3) -> f64 {
self.obj.distance_to(point)
}
fn normal_at(&self, point: Vec3) -> Vec3 {
self.obj.normal_at(point)
}
fn material_at(&self, point: Vec3) -> Material {
self.obj.material_at(point)
}
fn get_lights(&self) -> Vec<Light> {
let mut l = self.obj.get_lights();
l.extend(&self.lights);
l
}
fn node_count(&self) -> u32 {
self.obj.node_count() + 1
}
}

@ -2,6 +2,6 @@
clear
cargo build --release && \
clear && \
time target/release/rmarcher && \
time SEED=0 target/release/rmarcher && \
printf '\n' && \
kitty +kitten icat --align=left a.png

Loading…
Cancel
Save