Open CptPotato opened 1 year ago
Great suggestion. From what it looks like, it should be faster (since it uses no trigonometric functions). If you would like to work on it, then I will gladly help. What you would need to implement is mainly 3 functions: one to encode, another to decode, and one which will convert an angle(in radians or degrees) which would return a minimal number of bits required to save the normal with a given precision. Their signature could look something like that:
fn encode(normal:Vec3,prec_bits:u8)->(f64,f64)
fn decode(a:f64,b:f64)->Vector3
fn bits_from_angle(angle:f64)->u8
I can then take those functions and add tmf
-specific stuff around them.
However, this may take me some time(I am going on a short vacation soon and the project has some work that is has more impact). This is the ranking of size of different segments in the example mesh:
The hemioct
method on page 27 of https://jcgt.org/published/0003/02/01/paper.pdf is also worth looking into. Greater accuracy than oct32p
.
The
hemioct
method on page 27 of https://jcgt.org/published/0003/02/01/paper.pdf is also worth looking into. Greater accuracy thanoct32p
.
This seems like a good idea for normal maps, but I'm not sure about arbitrary vectors since you have to worry about the other hemisphere as well (adding a z sign bit, and at that point the accuracy/size improvement is lost). It could be faster, though.
Hey there!
Have you considered encoding normals using octahedron mapping? It's a neat way to map a direction vector into two components and it has a more uniform distribution than storing
(angle, z)
.I haven't profiled it against the current implementation, but here's some sample code:
code
```rs use glam::{Vec2, Vec3}; /// Encode a 3d direction vector to a 2d vector using octahedron mapping. /// The output vector is in the range [-1..1]. The input vector doesn't have to be normalized. pub fn encode_oct(dir: Vec3) -> Vec2 { let norm = dir.x.abs() + dir.y.abs() + dir.z.abs(); let nx = dir.x / norm; let ny = dir.y / norm; if dir.z.is_sign_positive() { Vec2::new(nx, ny) } else { // fold over negative z Vec2::new( (1.0 - ny.abs()) * nx.signum(), (1.0 - nx.abs()) * ny.signum(), ) } } /// Decode an octahedron mapped direction vector back to the original one. /// The output is normalized. pub fn decode_oct(mut oct: Vec2) -> Vec3 { let z = 1.0 - oct.x.abs() - oct.y.abs(); oct += oct.signum() * z.min(0.0); Vec3::new(oct.x, oct.y, z).normalize() } ```If you want to, I could open a PR to compare it to the current impl.