Compare commits

..

No commits in common. "c5965ec8746ceaa57e237ac8e778446e66be0cc1" and "e0ee0d6dffcff3969ca9f1d5fad54dd4c97408f1" have entirely different histories.

5 changed files with 85 additions and 32 deletions

View File

@ -13,9 +13,7 @@ efficiently handle very large data sets, complex shading, motion blur, color
management, etc. presents a much richer and more challenging problem space to management, etc. presents a much richer and more challenging problem space to
explore than just writing a basic path tracer. explore than just writing a basic path tracer.
## Building ## Building
Psychopath is written in [Rust](https://www.rust-lang.org), and is pretty Psychopath is written in [Rust](https://www.rust-lang.org), and is pretty
straightforward to build except for its OpenEXR dependency. straightforward to build except for its OpenEXR dependency.
@ -36,7 +34,6 @@ documented in the [OpenEXR-rs readme](https://github.com/cessen/openexr-rs/blob/
Once those environment variables are set, then you should be able to build using Once those environment variables are set, then you should be able to build using
the same simple cargo command above. the same simple cargo command above.
# PsychoBlend # PsychoBlend
Included in the repository is an add-on for [Blender](http://www.blender.org) Included in the repository is an add-on for [Blender](http://www.blender.org)
@ -53,6 +50,15 @@ doesn't support them yet.
- Exports dupligroups with full hierarchical instancing - Exports dupligroups with full hierarchical instancing
- Limited auto-detection of instanced meshes - Limited auto-detection of instanced meshes
# Contributing
I'm not looking for contributions right now, and I'm likely to reject pull
requests. This is currently a solo project and I like it that way.
However, if you're looking for projects _related_ to Psychopath to contribute to,
[OpenEXR-rs](https://github.com/cessen/openexr-rs) is definitely a
collaborative project that I would love more help with! And I fully expect more
such projects to come out of Psychopath in the future.
# License # License
@ -63,13 +69,3 @@ See LICENSE.md for details. But the gist is:
* Most crates under the `sub_crates` directory are dual-licensed under MIT and Apache 2.0 (but with some exceptions--see each crate for its respective licenses). * Most crates under the `sub_crates` directory are dual-licensed under MIT and Apache 2.0 (but with some exceptions--see each crate for its respective licenses).
The intent of this scheme is to keep Psychopath itself copyleft, while allowing smaller reusable components to be licensed more liberally. The intent of this scheme is to keep Psychopath itself copyleft, while allowing smaller reusable components to be licensed more liberally.
# Contributing
This is a personal, experimental, for-fun project, and I am specifically
not looking for contributions of any kind. All PRs will be rejected
without review.
However, feel free to fork this into an entirely new project, or examine
the code for ideas for a project of your own.

View File

@ -7,7 +7,7 @@ use std::{
use crate::{ use crate::{
lerp::{lerp, lerp_slice, Lerp}, lerp::{lerp, lerp_slice, Lerp},
math::{Point, Transform, Vector}, math::{fast_minf32, Point, Transform, Vector},
}; };
const BBOX_MAXT_ADJUST: f32 = 1.000_000_24; const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;
@ -47,7 +47,7 @@ impl BBox {
// Find the far and near intersection // Find the far and near intersection
let far_t = t1.max(t2).extend(std::f32::INFINITY); let far_t = t1.max(t2).extend(std::f32::INFINITY);
let near_t = t1.min(t2).extend(0.0); let near_t = t1.min(t2).extend(0.0);
let far_hit_t = (far_t.min_element() * BBOX_MAXT_ADJUST).min(max_t); let far_hit_t = fast_minf32(far_t.min_element() * BBOX_MAXT_ADJUST, max_t);
let near_hit_t = near_t.max_element(); let near_hit_t = near_t.max_element();
// Did we hit? // Did we hit?

View File

@ -4,18 +4,75 @@ use std::f32;
pub use math3d::{cross, dot, CrossProduct, DotProduct, Normal, Point, Transform, Vector}; pub use math3d::{cross, dot, CrossProduct, DotProduct, Normal, Point, Transform, Vector};
/// Gets the log base 2 of the given integer /// Clamps a value between a min and max.
pub fn log2_64(n: u64) -> u64 { pub fn clamp<T: PartialOrd>(v: T, lower: T, upper: T) -> T {
// This works by finding the largest non-zero binary digit in the if v < lower {
// number. Its bit position is then the log2 of the integer. lower
} else if v > upper {
if n == 0 { upper
0
} else { } else {
(63 - n.leading_zeros()) as u64 v
} }
} }
// The stdlib min function is slower than a simple if statement for some reason.
pub fn fast_minf32(a: f32, b: f32) -> f32 {
if a < b {
a
} else {
b
}
}
// The stdlib max function is slower than a simple if statement for some reason.
pub fn fast_maxf32(a: f32, b: f32) -> f32 {
if a > b {
a
} else {
b
}
}
/// Rounds an integer up to the next power of two.
pub fn upper_power_of_two(mut v: u32) -> u32 {
v -= 1;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v + 1
}
/// Gets the log base 2 of the given integer
pub fn log2_64(mut value: u64) -> u64 {
// This works by doing a binary search for the largest non-zero binary
// digit in the number. Its bit position is then the log2 of the integer.
let mut log = 0;
const POWERS: [(u64, u64); 6] = [
(32, (1 << 32) - 1),
(16, (1 << 16) - 1),
(8, (1 << 8) - 1),
(4, (1 << 4) - 1),
(2, (1 << 2) - 1),
(1, (1 << 1) - 1),
];
for &(i, j) in &POWERS {
let tmp = value >> i;
if tmp != 0 {
log += i;
value = tmp;
} else {
value &= j;
}
}
log
}
/// Creates a coordinate system from a single vector. /// Creates a coordinate system from a single vector.
/// ///
/// The input vector, v, becomes the first vector of the /// The input vector, v, becomes the first vector of the

View File

@ -18,7 +18,7 @@ use crate::{
hash::hash_u32, hash::hash_u32,
hilbert, hilbert,
image::Image, image::Image,
math::probit, math::{probit, upper_power_of_two},
mis::power_heuristic, mis::power_heuristic,
ray::{Ray, RayBatch}, ray::{Ray, RayBatch},
scene::{Scene, SceneLightSample}, scene::{Scene, SceneLightSample},
@ -151,7 +151,7 @@ impl<'a> Renderer<'a> {
let bucket_count_x = ((width / bucket_w) + 1) as u32; let bucket_count_x = ((width / bucket_w) + 1) as u32;
let bucket_count_y = ((height / bucket_h) + 1) as u32; let bucket_count_y = ((height / bucket_h) + 1) as u32;
let larger = cmp::max(bucket_count_x, bucket_count_y); let larger = cmp::max(bucket_count_x, bucket_count_y);
let pow2 = larger.next_power_of_two(); let pow2 = upper_power_of_two(larger);
pow2 * pow2 pow2 * pow2
}; };
for hilbert_d in 0..bucket_n { for hilbert_d in 0..bucket_n {

View File

@ -7,7 +7,7 @@ use glam::Vec4;
use crate::{ use crate::{
color::{Color, SpectralSample}, color::{Color, SpectralSample},
lerp::{lerp, Lerp}, lerp::{lerp, Lerp},
math::{dot, zup_to_vec, Normal, Vector}, math::{clamp, dot, zup_to_vec, Normal, Vector},
sampling::cosine_sample_hemisphere, sampling::cosine_sample_hemisphere,
}; };
@ -481,11 +481,11 @@ mod ggx_closure {
} }
// Calculate needed dot products // Calculate needed dot products
let na = dot(nn, aa).clamp(-1.0, 1.0); let na = clamp(dot(nn, aa), -1.0, 1.0);
let nb = dot(nn, bb).clamp(-1.0, 1.0); let nb = clamp(dot(nn, bb), -1.0, 1.0);
let ha = dot(hh, aa).clamp(-1.0, 1.0); let ha = clamp(dot(hh, aa), -1.0, 1.0);
let hb = dot(hh, bb).clamp(-1.0, 1.0); let hb = clamp(dot(hh, bb), -1.0, 1.0);
let nh = dot(nn, hh).clamp(-1.0, 1.0); let nh = clamp(dot(nn, hh), -1.0, 1.0);
// Calculate F - Fresnel // Calculate F - Fresnel
let col_f = { let col_f = {
@ -584,7 +584,7 @@ mod ggx_closure {
// Approximate method // Approximate method
let theta = cos_theta_max.acos(); let theta = cos_theta_max.acos();
let hh = (aa + bb).normalized(); let hh = (aa + bb).normalized();
let nh = dot(nn, hh).clamp(-1.0, 1.0); let nh = clamp(dot(nn, hh), -1.0, 1.0);
let fac = ggx_d(nh, (1.0f32).min(roughness.sqrt() + (2.0 * theta / PI_32))); let fac = ggx_d(nh, (1.0f32).min(roughness.sqrt() + (2.0 * theta / PI_32)));
fac * (1.0f32).min(1.0 - cos_theta_max) * INV_PI fac * (1.0f32).min(1.0 - cos_theta_max) * INV_PI