Skip to content

Instantly share code, notes, and snippets.

@aloucks
Last active October 11, 2020 02:47
Show Gist options
  • Save aloucks/da81c25f86fc8474090262af2c586c04 to your computer and use it in GitHub Desktop.
Save aloucks/da81c25f86fc8474090262af2c586c04 to your computer and use it in GitHub Desktop.
Generate normals from a height map
// https://stackoverflow.com/questions/44120220/calculating-normals-on-terrain-mesh
fn generate_normals(
height_map: &image::ImageBuffer<image::Luma<u16>, Vec<u16>>,
normal_map: &mut image::ImageBuffer<image::Rgb<u8>, Vec<u8>>)
{
let (cols, rows) = height_map.dimensions();
let up = cgmath::vec3(0.0, 0.0, 1.0);
//let mut normals = vec![up; (cols * rows) as usize];
// the `size_factor` reduces to `width / 1024` with this `cell_size`.
let cell_size = 1.0 / (cols as f32 / 128.0);
let size_factor = 1.0 / (8.0 * cell_size);
for row in 0..rows {
for col in 0..cols {
if row == 0 || col == 0 || row == rows - 1 || col == cols - 1 {
let normal = up;
let r = ((1.0 + normal.x) * 0.5 * 255.0) as u8;
let g = ((1.0 + normal.y) * 0.5 * 255.0) as u8;
let b = ((1.0 + normal.z) * 0.5 * 255.0) as u8;
let pixel = image::Rgb([r, g, b]);
normal_map.put_pixel(col, row, pixel);
} else {
const U16_MAX: f32 = u16::MAX as f32;
let nw = height_map.get_pixel(col - 1, row - 1)[0] as f32 / U16_MAX;
let n = height_map.get_pixel(col, row - 1)[0] as f32 / U16_MAX;
let ne = height_map.get_pixel(col + 1, row - 1)[0] as f32 / U16_MAX;
let e = height_map.get_pixel(col + 1, row)[0] as f32 / U16_MAX;
let se = height_map.get_pixel(col + 1, row + 1)[0] as f32 / U16_MAX;
let s = height_map.get_pixel(col, row + 1)[0] as f32 / U16_MAX;
let sw = height_map.get_pixel(col - 1, row + 1)[0] as f32 / U16_MAX;
let w = height_map.get_pixel(col - 1, row)[0] as f32 / U16_MAX;
let dydx = ((ne + 2.0 * e + se) - (nw + 2.0 * w + sw)) * size_factor;
let dydz = ((sw + 2.0 * s + se) - (nw + 2.0 * n + ne)) * size_factor;
let normal = cgmath::vec3(dydx, dydz, 1.0).normalize();
//normals[(row * cols + col) as usize] = normal;
let r = ((1.0 + normal.x) * 0.5 * 255.0) as u8;
let g = ((1.0 + normal.y) * 0.5 * 255.0) as u8;
let b = ((1.0 + normal.z) * 0.5 * 255.0) as u8;
let pixel = image::Rgb([r, g, b]);
normal_map.put_pixel(col, row, pixel);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment