ft (wip): texture/material overhaul
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
"image_height": 1080,
|
"image_height": 1080,
|
||||||
"max_depth": 50,
|
"max_depth": 50,
|
||||||
"camera": {
|
"camera": {
|
||||||
"anti_alias_rate": 23,
|
"anti_alias_rate": 2,
|
||||||
"fov": 70.0,
|
"fov": 70.0,
|
||||||
"look_from": [-10, 4, 15],
|
"look_from": [-10, 4, 15],
|
||||||
"look_at": [-11.0, 0.0, 0.0],
|
"look_at": [-11.0, 0.0, 0.0],
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
},
|
},
|
||||||
"materials": [
|
"materials": [
|
||||||
{ "type": "lambertian", "albedo": [0.2, 0.2, 0.2], "prob": 0.8 },
|
{ "type": "lambertian", "albedo": [0.2, 0.2, 0.2], "prob": 0.8 },
|
||||||
{"type": "texture", "source": "./textures/earthmap1k.png"},
|
{ "type": "lambertian", "albedo": { "source": "./textures/earthmap1k.png" }, "prob": 1.0 },
|
||||||
{"type": "texture", "source": "./textures/bluegrid.png"}
|
{ "type": "lambertian", "albedo": { "source": "./textures/bluegrid.png" }, "prob": 0.5 }
|
||||||
],
|
],
|
||||||
"objects": [
|
"objects": [
|
||||||
{ "type": "sphere", "center": [0.0, 0.0, -1.2], "radius": 0.9, "material": 2},
|
{ "type": "sphere", "center": [0.0, 0.0, -1.2], "radius": 0.9, "material": 2},
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ fn main() {
|
|||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
json_file = &args[1];
|
json_file = &args[1];
|
||||||
}
|
}
|
||||||
|
println!("we done serializingiaonosnl");
|
||||||
|
|
||||||
let json_str = fs::read_to_string(json_file).expect("Reading specified scene file failed!");
|
let json_str = fs::read_to_string(json_file).expect("Reading specified scene file failed!");
|
||||||
let mut scene: Scene = serde_json::from_str(&json_str).unwrap();
|
let mut scene: Scene = serde_json::from_str(&json_str).unwrap();
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ pub mod lambertian;
|
|||||||
pub mod normal;
|
pub mod normal;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
|
pub mod colour_source;
|
||||||
|
|||||||
57
src/objects/materials/colour_source.rs
Normal file
57
src/objects/materials/colour_source.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::{objects::{hit::Hit, materials::texture::Texture}, vec3::Colour};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME (high priority): we can't deserialize this enum as-is, because of the texture.
|
||||||
|
// To fix this we'll have to pull the same trick we did for materialDef/sceneDef and the texture
|
||||||
|
// pointer, but i'm not sure what that implies for all materials.. maybe theres a clever way to
|
||||||
|
// solve that somehow, but from where im looking at it we'll need to manually deserialize everything
|
||||||
|
// that uses coloursource ://
|
||||||
|
pub enum ColourSource {
|
||||||
|
Colour(Colour),
|
||||||
|
Texture(Texture),
|
||||||
|
}
|
||||||
|
|
||||||
|
// REFACTOR: not entirely happy having this serialization code here. move that.
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum RawColourSource {
|
||||||
|
Colour(Colour),
|
||||||
|
Texture(RawTexture),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct RawTexture {
|
||||||
|
pub source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ColourSource {
|
||||||
|
pub fn get_colour(&self, hit: &Hit) -> Colour {
|
||||||
|
match self {
|
||||||
|
ColourSource::Colour(vec3) => *vec3,
|
||||||
|
ColourSource::Texture(texture) => texture.colour_at(hit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawColourSource> for ColourSource {
|
||||||
|
fn from(value: RawColourSource) -> Self {
|
||||||
|
match value {
|
||||||
|
RawColourSource::Colour(c) => Self::Colour(c),
|
||||||
|
RawColourSource::Texture(t) => Self::Texture(Texture::new(&t.source)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ColourSource {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let raw = RawColourSource::deserialize(deserializer)?;
|
||||||
|
Ok(raw.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ use crate::{
|
|||||||
vec3::Colour,
|
vec3::Colour,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO (low priority): add support for coloured dielectrics (add colour to the struct, return that
|
||||||
|
// instead of 1,1,1)
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Dielectric {
|
pub struct Dielectric {
|
||||||
refraction_index: f32,
|
refraction_index: f32,
|
||||||
|
|||||||
@@ -1,31 +1,39 @@
|
|||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{hit::Hit, materials::traits::Material},
|
objects::{hit::Hit, materials::{colour_source::ColourSource, traits::Material}}, ray::Ray, vec3::{Colour, Vec3},
|
||||||
ray::Ray,
|
|
||||||
vec3::{Colour, Vec3},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Lambertian {
|
pub struct Lambertian {
|
||||||
albedo: Colour,
|
albedo: ColourSource,
|
||||||
|
// albedo: Colour,
|
||||||
prob: f32,
|
prob: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lambertian {
|
impl Lambertian {
|
||||||
pub fn new(albedo: Colour, prob: f32) -> Self {
|
pub fn new(albedo: ColourSource, prob: f32) -> Self {
|
||||||
Self { albedo, prob }
|
Self { albedo, prob }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rgb(r: f32, g: f32, b: f32, prob: f32) -> Self {
|
pub fn rgb(r: f32, g: f32, b: f32, prob: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
albedo: Colour::new(r, g, b),
|
albedo: ColourSource::Colour(Colour::new(r, g, b)),
|
||||||
prob,
|
prob,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Lambertian {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Lambertian").field("prob", &self.prob).finish()
|
||||||
|
// .field("albedo", &self.albedo)
|
||||||
|
// FIXME: readd albedo field w proper syntax ^
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Material for Lambertian {
|
impl Material for Lambertian {
|
||||||
fn scatter(&self, hit: &Hit, _ray: &Ray) -> Option<(Option<Ray>, Colour)> {
|
fn scatter(&self, hit: &Hit, _ray: &Ray) -> Option<(Option<Ray>, Colour)> {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
@@ -35,7 +43,7 @@ impl Material for Lambertian {
|
|||||||
dir = *hit.n();
|
dir = *hit.n();
|
||||||
}
|
}
|
||||||
let scattered = Ray::new(*hit.p(), dir);
|
let scattered = Ray::new(*hit.p(), dir);
|
||||||
return Some((Some(scattered), self.albedo));
|
return Some((Some(scattered), self.albedo.get_colour(hit)));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,10 @@ use image::{DynamicImage, ImageReader};
|
|||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{hit::Hit, materials::traits::Material},
|
objects::hit::Hit,
|
||||||
ray::Ray,
|
|
||||||
vec3::Vec3,
|
vec3::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: overhaul texture implementation so we can support textures for arbitrary materials.
|
|
||||||
// Basically remove this struct, and instead make lambertian/metal/dielectric/take either a texture
|
|
||||||
// or a flat colour
|
|
||||||
// I think we can use an enum ColourSource with a function to fetch a specific colour depending on
|
|
||||||
// if we have a texture/flat colour. and yeah.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
source: DynamicImage,
|
source: DynamicImage,
|
||||||
@@ -69,12 +63,9 @@ impl Texture {
|
|||||||
b[self.stride * idx + 1],
|
b[self.stride * idx + 1],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Material for Texture {
|
pub fn colour_at(&self, hit: &Hit) -> Vec3 {
|
||||||
fn scatter(&self, hit: &Hit, _ray: &Ray) -> Option<(Option<Ray>, Vec3)> {
|
|
||||||
let (u, v) = hit.uv();
|
let (u, v) = hit.uv();
|
||||||
let col = self._at(u, 1. - v);
|
self._at(u, 1. - v)
|
||||||
Some((None, col))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ pub fn render(scene: &mut Scene) {
|
|||||||
|
|
||||||
pub fn render_chunk(chunk: &mut [u8], scene: &Scene, y: u32) {
|
pub fn render_chunk(chunk: &mut [u8], scene: &Scene, y: u32) {
|
||||||
let camera = &scene.get_camera();
|
let camera = &scene.get_camera();
|
||||||
let aa_rate = &scene.get_camera().get_anti_alias_rate();
|
let aa_rate = camera.get_anti_alias_rate();
|
||||||
|
|
||||||
for i in 0..scene.get_image_width() {
|
for i in 0..scene.get_image_width() {
|
||||||
let pixel_tl = camera.get_pixel_tl(i, y);
|
let pixel_tl = camera.get_pixel_tl(i, y);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ pub(crate) enum MaterialDef {
|
|||||||
Metal(Metal),
|
Metal(Metal),
|
||||||
Dielectric(Dielectric),
|
Dielectric(Dielectric),
|
||||||
Normal(Normal),
|
Normal(Normal),
|
||||||
Texture(RawTexture),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialDef {
|
impl MaterialDef {
|
||||||
@@ -22,13 +21,6 @@ impl MaterialDef {
|
|||||||
MaterialDef::Metal(m) => Arc::new(m),
|
MaterialDef::Metal(m) => Arc::new(m),
|
||||||
MaterialDef::Dielectric(d) => Arc::new(d),
|
MaterialDef::Dielectric(d) => Arc::new(d),
|
||||||
MaterialDef::Normal(n) => Arc::new(n),
|
MaterialDef::Normal(n) => Arc::new(n),
|
||||||
MaterialDef::Texture(t) => Arc::new(Texture::new(&t.source)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub(crate) struct RawTexture {
|
|
||||||
pub source: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::objects::materials::colour_source::ColourSource;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
@@ -78,7 +79,7 @@ impl Scene {
|
|||||||
world.push(Arc::new(Sphere::new(
|
world.push(Arc::new(Sphere::new(
|
||||||
center,
|
center,
|
||||||
0.2,
|
0.2,
|
||||||
Arc::new(Lambertian::new(Vec3::random() * Vec3::random(), 1.)),
|
Arc::new(Lambertian::new(ColourSource::Colour(Vec3::random() * Vec3::random()), 1.)),
|
||||||
)));
|
)));
|
||||||
} else if mat < 0.95 {
|
} else if mat < 0.95 {
|
||||||
// metal
|
// metal
|
||||||
@@ -186,6 +187,9 @@ struct SceneDef {
|
|||||||
pub max_depth: u32,
|
pub max_depth: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (low priority): rn textures are tightly coupled to materials; what if we wnat different
|
||||||
|
// materials to share a texture? apply the same technique we used for decoupling materials/objects
|
||||||
|
// on textures/materials.
|
||||||
impl<'de> Deserialize<'de> for Scene {
|
impl<'de> Deserialize<'de> for Scene {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
|
|||||||
Reference in New Issue
Block a user