|
| 1 | +//! Procedural noise generation. |
| 2 | +
|
| 3 | +use super::{ |
| 4 | + spline::smoothstep, |
| 5 | + vary::lerp, |
| 6 | + vec::{splat, vec2, vec3, Vec2, Vec3, Vector}, |
| 7 | +}; |
| 8 | + |
| 9 | +/// Returns the Perlin noise value corresponding to the 2D point. |
| 10 | +pub fn perlin2(pt: Vec2) -> f32 { |
| 11 | + use super::float::f32; |
| 12 | + let f = pt - pt.map(f32::floor); |
| 13 | + let [fx, fy] = f.0; |
| 14 | + |
| 15 | + let [g00, g01, g10, g11] = grads2(pt); |
| 16 | + |
| 17 | + let d00 = g00.dot(&vec2(fx, fy)); |
| 18 | + let d01 = g01.dot(&vec2(fx, fy - 1.0)); |
| 19 | + let d10 = g10.dot(&vec2(fx - 1.0, fy)); |
| 20 | + let d11 = g11.dot(&vec2(fx - 1.0, fy - 1.0)); |
| 21 | + |
| 22 | + let t = f.map(smoothstep); |
| 23 | + let (a, b) = lerp(t.x(), (d00, d01), (d10, d11)); |
| 24 | + lerp(t.y(), a, b) |
| 25 | +} |
| 26 | + |
| 27 | +/// Returns the Perlin gradient vector corresponding to the 2D point. |
| 28 | +/// |
| 29 | +/// The vector is computed by taking the gradient vectors of all four |
| 30 | +/// surrounding grid points and smoothly interpolating between them. |
| 31 | +pub fn perlin2v(pt: Vec2) -> Vec2 { |
| 32 | + use super::float::f32; |
| 33 | + let fract = pt - pt.map(f32::floor); |
| 34 | + |
| 35 | + let [g00, g01, g10, g11] = grads2(pt); |
| 36 | + |
| 37 | + let t = fract.map(smoothstep); |
| 38 | + let (a, b) = lerp(t.x(), (g00, g01), (g10, g11)); |
| 39 | + lerp(t.y(), a, b) |
| 40 | +} |
| 41 | + |
| 42 | +/// Returns the Perlin noise value corresponding to the 3D point. |
| 43 | +pub fn perlin3(pt: Vec3) -> f32 { |
| 44 | + use super::float::f32; |
| 45 | + let pt0 = pt.map(f32::floor); |
| 46 | + let f = pt - pt0; |
| 47 | + let i_f: Vec3 = f - splat(1.0); |
| 48 | + |
| 49 | + let [fx, fy, fz] = f.0; |
| 50 | + let [ifx, ify, ifz] = i_f.0; |
| 51 | + |
| 52 | + let [g000, g001, g010, g011, g100, g101, g110, g111] = grads3(pt0); |
| 53 | + |
| 54 | + let d000 = g000.dot(&vec3(fx, fy, fz)); |
| 55 | + let d001 = g001.dot(&vec3(fx, fy, ifz)); |
| 56 | + let d010 = g010.dot(&vec3(fx, ify, fz)); |
| 57 | + let d011 = g011.dot(&vec3(fx, ify, ifz)); |
| 58 | + let d100 = g100.dot(&vec3(ifx, fy, fz)); |
| 59 | + let d101 = g101.dot(&vec3(ifx, fy, ifz)); |
| 60 | + let d110 = g110.dot(&vec3(ifx, ify, fz)); |
| 61 | + let d111 = g111.dot(&vec3(ifx, ify, ifz)); |
| 62 | + |
| 63 | + let t: Vec3 = f.map(smoothstep); |
| 64 | + |
| 65 | + let x0 = Vector::<_>::new([d000, d010, d001, d011]); |
| 66 | + let x1 = Vector::<_>::new([d100, d110, d101, d111]); |
| 67 | + let [y0, y1, y2, y3] = lerp(t.x(), x0, x1).0; |
| 68 | + let (z0, z1) = lerp(t.y(), (y0, y2), (y1, y3)); |
| 69 | + lerp(t.z(), z0, z1) |
| 70 | +} |
| 71 | + |
| 72 | +/// Returns the Perlin gradient vector corresponding to the 3D point. |
| 73 | +/// |
| 74 | +/// The vector is computed by taking the gradient vectors of all four |
| 75 | +/// surrounding grid points and smoothly interpolating between them. |
| 76 | +pub fn perlin3v(pt: Vec3) -> Vec3 { |
| 77 | + use super::float::f32; |
| 78 | + let fract = pt - pt.map(f32::floor); |
| 79 | + |
| 80 | + let [g000, g001, g010, g011, g100, g101, g110, g111] = grads3(pt); |
| 81 | + |
| 82 | + let t: Vec3 = fract.map(smoothstep); |
| 83 | + |
| 84 | + let x0 = ((g000, g010), (g001, g011)); |
| 85 | + let x1 = ((g100, g110), (g101, g111)); |
| 86 | + let ((y0, y1), (y2, y3)) = lerp(t.x(), x0, x1); |
| 87 | + let (z0, z1) = lerp(t.y(), (y0, y2), (y1, y3)); |
| 88 | + lerp(t.z(), z0, z1) |
| 89 | +} |
| 90 | + |
| 91 | +fn perm(x: i32) -> i32 { |
| 92 | + PERM[(x & 0xFF) as usize] as i32 |
| 93 | +} |
| 94 | + |
| 95 | +fn grad2(x: f32, y: f32) -> Vec2 { |
| 96 | + let perm = perm(x as i32 + perm(y as i32)); |
| 97 | + GRADS_2[perm as usize & 0x7] |
| 98 | +} |
| 99 | + |
| 100 | +fn grads2(pt: Vec2) -> [Vec2; 4] { |
| 101 | + use super::float::f32; |
| 102 | + let [x0, y0] = pt.map(f32::floor).0; |
| 103 | + |
| 104 | + let g00 = grad2(x0, y0); |
| 105 | + let g01 = grad2(x0, y0 + 1.0); |
| 106 | + let g10 = grad2(x0 + 1.0, y0); |
| 107 | + let g11 = grad2(x0 + 1.0, y0 + 1.0); |
| 108 | + |
| 109 | + [g00, g01, g10, g11] |
| 110 | +} |
| 111 | + |
| 112 | +fn grad3(x: f32, y: f32, z: f32) -> Vec3 { |
| 113 | + let perm = perm(x as i32 + perm(y as i32 + perm(z as i32))); |
| 114 | + GRADS_3[perm as usize & 0xF] |
| 115 | +} |
| 116 | + |
| 117 | +fn grads3(pt0: Vec3) -> [Vec3; 8] { |
| 118 | + let pt1 = pt0 + splat(1.0); |
| 119 | + let [x0, y0, z0] = pt0.0; |
| 120 | + let [x1, y1, z1] = pt1.0; |
| 121 | + |
| 122 | + // |
| 123 | + // 011 +--------------+ 111 |
| 124 | + // / | / | |
| 125 | + // / | / | |
| 126 | + // 010 +--------------+ 110 | |
| 127 | + // | | | | |
| 128 | + // | +--------|-----+ 101 |
| 129 | + // | / | / |
| 130 | + // | / | / |
| 131 | + // 000 +--------------+ 100 |
| 132 | + // |
| 133 | + |
| 134 | + let g000 = grad3(x0, y0, z0); |
| 135 | + let g001 = grad3(x0, y0, z1); |
| 136 | + let g010 = grad3(x0, y1, z0); |
| 137 | + let g011 = grad3(x0, y1, z1); |
| 138 | + let g100 = grad3(x1, y0, z0); |
| 139 | + let g101 = grad3(x1, y0, z1); |
| 140 | + let g110 = grad3(x1, y1, z0); |
| 141 | + let g111 = grad3(x1, y1, z1); |
| 142 | + |
| 143 | + [g000, g001, g010, g011, g100, g101, g110, g111] |
| 144 | +} |
| 145 | + |
| 146 | +const A: f32 = 1.237; |
| 147 | +const B: f32 = 0.513; |
| 148 | +/// Gradient vectors for 2D noise. |
| 149 | +static GRADS_2: [Vec2; 8] = [ |
| 150 | + vec2(A, B), |
| 151 | + vec2(B, A), |
| 152 | + vec2(-B, A), |
| 153 | + vec2(-A, B), |
| 154 | + vec2(-A, -B), |
| 155 | + vec2(-B, -A), |
| 156 | + vec2(B, -A), |
| 157 | + vec2(A, -B), |
| 158 | +]; |
| 159 | + |
| 160 | +/// Gradient vectors for 3D noise. |
| 161 | +static GRADS_3: [Vec3; 16] = [ |
| 162 | + // |
| 163 | + vec3(0.0, -1.0, -1.0), |
| 164 | + vec3(0.0, -1.0, 1.0), |
| 165 | + vec3(0.0, 1.0, -1.0), |
| 166 | + vec3(0.0, 1.0, 1.0), |
| 167 | + // |
| 168 | + vec3(-1.0, 0.0, -1.0), |
| 169 | + vec3(-1.0, 0.0, 1.0), |
| 170 | + vec3(1.0, 0.0, -1.0), |
| 171 | + vec3(1.0, 0.0, 1.0), |
| 172 | + // |
| 173 | + vec3(-1.0, -1.0, 0.0), |
| 174 | + vec3(-1.0, 1.0, 0.0), |
| 175 | + vec3(1.0, -1.0, 0.0), |
| 176 | + vec3(1.0, 1.0, 0.0), |
| 177 | + // |
| 178 | + vec3(1.0, 1.0, 0.0), |
| 179 | + vec3(-1.0, 1.0, 0.0), |
| 180 | + vec3(0.0, -1.0, 1.0), |
| 181 | + vec3(0.0, -1.0, -1.0), |
| 182 | +]; |
| 183 | + |
| 184 | +/// Permutation table for calculating a pseudo-random index for each grid point. |
| 185 | +#[rustfmt::skip] |
| 186 | +static PERM: [u8; 256] = [ |
| 187 | + 156, 2, 157, 90, 75, 199, 55, 167, 62, 92, 101, 253, 66, 134, 113, 83, |
| 188 | + 1, 136, 78, 106, 254, 105, 248, 176, 234, 5, 195, 226, 49, 71, 87, 44, |
| 189 | + 122, 94, 219, 140, 72, 159, 237, 212, 8, 162, 200, 124, 125, 69, 165, 74, |
| 190 | + 245, 42, 89, 216, 158, 108, 238, 184, 217, 73, 126, 210, 14, 111, 19, 188, |
| 191 | + 186, 45, 38, 223, 35, 112, 214, 26, 145, 95, 99, 193, 250, 189, 152, 182, |
| 192 | + 166, 247, 148, 213, 168, 70, 96, 249, 127, 132, 4, 137, 41, 60, 102, 28, |
| 193 | + 27, 240, 227, 155, 211, 230, 9, 80, 178, 3, 68, 153, 143, 84, 179, 181, |
| 194 | + 12, 97, 103, 16, 225, 146, 63, 82, 203, 175, 163, 147, 11, 116, 185, 215, |
| 195 | + 57, 120, 208, 129, 115, 198, 37, 201, 39, 98, 20, 183, 56, 118, 109, 142, |
| 196 | + 138, 65, 117, 114, 160, 25, 43, 191, 204, 161, 22, 251, 139, 79, 131, 231, |
| 197 | + 76, 0, 205, 206, 244, 51, 174, 13, 110, 85, 209, 77, 64, 53, 48, 221, |
| 198 | + 133, 93, 224, 24, 33, 164, 23, 47, 171, 128, 243, 18, 52, 119, 149, 100, |
| 199 | + 246, 233, 31, 192, 252, 190, 15, 172, 91, 229, 144, 54, 61, 58, 220, 36, |
| 200 | + 222, 29, 50, 88, 121, 173, 232, 194, 239, 197, 32, 180, 107, 46, 7, 130, |
| 201 | + 169, 81, 218, 67, 21, 170, 187, 59, 86, 235, 154, 123, 150, 177, 135, 228, |
| 202 | + 104, 242, 6, 151, 255, 34, 30, 141, 202, 196, 236, 207, 241, 40, 17, 10 |
| 203 | +]; |
| 204 | + |
| 205 | +#[cfg(test)] |
| 206 | +mod tests { |
| 207 | + use super::*; |
| 208 | + use std::eprintln; |
| 209 | + |
| 210 | + #[test] |
| 211 | + fn perlin2_statistics() { |
| 212 | + let count = 1000u32; |
| 213 | + let div = 10.0; |
| 214 | + let [mut avg, mut min, mut max] = [0.0f32, 1.0, -1.0]; |
| 215 | + for i in 0..count { |
| 216 | + for j in 0..count { |
| 217 | + let v = perlin2(vec2(i as f32, j as f32) / div); |
| 218 | + avg += v; |
| 219 | + min = min.min(v); |
| 220 | + max = max.max(v); |
| 221 | + } |
| 222 | + } |
| 223 | + avg /= count.pow(2) as f32; |
| 224 | + |
| 225 | + assert_eq!(avg, -0.0005586566); |
| 226 | + assert_eq!(min, -0.8853002); |
| 227 | + assert_eq!(max, 0.6612066); |
| 228 | + } |
| 229 | + #[test] |
| 230 | + fn perlin3_statistics() { |
| 231 | + let count = 100u32; |
| 232 | + let div = 10.0; |
| 233 | + let [mut avg, mut min, mut max] = [0.0f32, 1.0, -1.0]; |
| 234 | + for i in 0..count { |
| 235 | + for j in 0..count { |
| 236 | + for k in 0..count { |
| 237 | + let pt = vec3(i, j, k).map(|c| c as f32) / div; |
| 238 | + let v = perlin3(pt); |
| 239 | + |
| 240 | + avg += v; |
| 241 | + min = min.min(v); |
| 242 | + max = max.max(v); |
| 243 | + } |
| 244 | + } |
| 245 | + } |
| 246 | + avg /= count.pow(3) as f32; |
| 247 | + |
| 248 | + assert_eq!(avg, 0.00039595732); |
| 249 | + assert_eq!(min, -0.8853568); |
| 250 | + assert_eq!(max, 0.8853002); |
| 251 | + } |
| 252 | +} |
0 commit comments