{"id":15630685,"url":"https://github.com/svenstaro/bvh","last_synced_at":"2025-05-15T02:10:19.760Z","repository":{"id":38361142,"uuid":"68921740","full_name":"svenstaro/bvh","owner":"svenstaro","description":"A fast BVH using SAH in rust","archived":false,"fork":false,"pushed_at":"2025-02-18T17:17:17.000Z","size":1601,"stargazers_count":241,"open_issues_count":12,"forks_count":38,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-15T01:09:17.689Z","etag":null,"topics":["bvh","crates","intersection-tests","pathtracing","rays","raytracing"],"latest_commit_sha":null,"homepage":"https://docs.rs/bvh","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/svenstaro.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"svenstaro"}},"created_at":"2016-09-22T13:02:14.000Z","updated_at":"2025-05-09T21:06:05.000Z","dependencies_parsed_at":"2023-10-14T17:16:52.352Z","dependency_job_id":"f5533c90-c0ea-4d8a-a176-0babbe9da94e","html_url":"https://github.com/svenstaro/bvh","commit_stats":{"total_commits":409,"total_committers":24,"mean_commits":"17.041666666666668","dds":0.7212713936430317,"last_synced_commit":"41664a963d1931f8b0f93f11ca886ff16681945e"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenstaro%2Fbvh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenstaro%2Fbvh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenstaro%2Fbvh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenstaro%2Fbvh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/svenstaro","download_url":"https://codeload.github.com/svenstaro/bvh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254259287,"owners_count":22040809,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bvh","crates","intersection-tests","pathtracing","rays","raytracing"],"created_at":"2024-10-03T10:35:14.002Z","updated_at":"2025-05-15T02:10:14.750Z","avatar_url":"https://github.com/svenstaro.png","language":"Rust","funding_links":["https://github.com/sponsors/svenstaro"],"categories":["Rust"],"sub_categories":[],"readme":"# bvh\n[![CI](https://github.com/svenstaro/bvh/workflows/CI/badge.svg)](https://github.com/svenstaro/bvh/actions)\n[![Docs Status](https://docs.rs/bvh/badge.svg)](https://docs.rs/bvh)\n[![codecov](https://codecov.io/gh/svenstaro/bvh/branch/master/graph/badge.svg)](https://codecov.io/gh/svenstaro/bvh)\n[![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/svenstaro/bvh/blob/master/LICENSE)\n[![Crates.io](https://img.shields.io/crates/v/bvh.svg)](https://crates.io/crates/bvh)\n[![Crates.io](https://img.shields.io/crates/d/bvh.svg)](https://crates.io/crates/bvh)\n\n**A crate which exports rays, axis-aligned bounding boxes, and binary bounding\nvolume hierarchies.**\n\n## About\n\nThis crate can be used for applications which contain intersection computations of rays\nwith primitives. For this purpose a binary tree BVH (Bounding Volume Hierarchy) is of great\nuse if the scene which the ray traverses contains a huge number of primitives. With a BVH the\nintersection test complexity is reduced from O(n) to O(log2(n)) at the cost of building\nthe BVH once in advance. This technique is especially useful in ray/path tracers. For\nuse in a shader this module also exports a flattening procedure, which allows for\niterative traversal of the BVH.\n\nIt uses `rayon` by default to parallelize building the BVH.\n\n## Example\n\n```rust\nuse bvh::aabb::{Aabb, Bounded};\nuse bvh::bounding_hierarchy::{BHShape, BoundingHierarchy};\nuse bvh::bvh::Bvh;\nuse bvh::ray::Ray;\nuse nalgebra::{Point3, Vector3};\n\nlet origin = Point3::new(0.0,0.0,0.0);\nlet direction = Vector3::new(1.0,0.0,0.0);\nlet ray = Ray::new(origin, direction);\n\nstruct Sphere {\n    position: Point3\u003cf32\u003e,\n    radius: f32,\n    node_index: usize,\n}\n\nimpl Bounded\u003cf32, 3\u003e for Sphere {\n    fn aabb(\u0026self) -\u003e Aabb\u003cf32, 3\u003e {\n        let half_size = Vector3::new(self.radius, self.radius, self.radius);\n        let min = self.position - half_size;\n        let max = self.position + half_size;\n        Aabb::with_bounds(min, max)\n    }\n}\n\nimpl BHShape\u003cf32, 3\u003e for Sphere {\n    fn set_bh_node_index(\u0026mut self, index: usize) {\n        self.node_index = index;\n    }\n    fn bh_node_index(\u0026self) -\u003e usize {\n        self.node_index\n    }\n}\n\nlet mut spheres = Vec::new();\nfor i in 0..1000u32 {\n    let position = Point3::new(i as f32, i as f32, i as f32);\n    let radius = (i % 10) as f32 + 1.0;\n    spheres.push(Sphere {\n        position: position,\n        radius: radius,\n        node_index: 0,\n    });\n}\n\nlet bvh = Bvh::build_par(\u0026mut spheres);\nlet hit_sphere_aabbs = bvh.traverse(\u0026ray, \u0026spheres);\n```\n\n## Explicit SIMD\n\nThis crate features some manually written SIMD instructions, currently only for the `x86_64` architecture.\nWhile nalgebra provides us with generic SIMD optimization (and it does a great job for the most part) - \nsome important functions, such as ray-aabb-intersection have been optimized by hand.\n\nThe currently optimized intersections for ray-aabb are:\nType: f32, Dimension: 2,3,4\nType: f64, Dimension: 2,3,4\n\nTo enable these optimziations, you must build with the `nightly` toolchain and enable the `simd` flag.\n\n## Optimization\n\nThis crate provides BVH updating, which is also called optimization. With BVH optimization\nyou can mutate the shapes on which the BVH is built and update the tree accordingly without rebuilding it completely.\nThis method is very useful when there are only very few changes to a huge scene. When the major part of the scene is static,\nit is faster to update the tree, instead of rebuilding it from scratch.\n\n### Drawbacks\n\nFirst of all, optimizing is not helpful if more than half of the scene is not static.\nThis is due to how optimizing takes place:\nGiven a set of indices of all shapes which have changed, the optimize procedure tries to rotate fixed constellations\nin search for a better surface area heuristic (SAH) value. This is done recursively from bottom to top while fixing the AABBs\nin the inner nodes of the BVH. Which is why it is inefficient to update the BVH in comparison to rebuilding, when a lot\nof shapes have moved.\n\nAnother problem with updated BVHs is, that the resulting BVH is not optimal. Assume that the scene is composed of two major\ngroups separated by a large gap. When one shape moves from one group to another, the updating procedure will not be able to\nfind a sequence of bottom-up rotations which inserts the shape deeply into the other branch.\n\nThe following benchmarks are run with two different datasets:\n* A randomly generated scene with unit sized cubes containing a total of (1200, 12000, and 120000 triangles).\n* Sponza, a popular scene for benchmarking graphics engines.\n\nAll benchmarks were taken on a Ryzen 3900x.\n\nAll benchmarks unless otherwise noted were captured with the `simd` feature off.\n\n### Intersection via traversal of the list of triangles\n\n```C\ntest testbase::bench_intersect_120k_triangles_list                            ... bench:     570,717 ns/iter (+/- 21,689)\ntest testbase::bench_intersect_sponza_list                                    ... bench:     510,683 ns/iter (+/- 9,525)\n\n// simd enabled\ntest testbase::bench_intersect_120k_triangles_list                            ... bench:     566,916 ns/iter (+/- 22,024)\ntest testbase::bench_intersect_sponza_list                                    ... bench:     518,821 ns/iter (+/- 12,191)\n```\n\nThis is the most naive approach to intersecting a scene with a ray. It defines the baseline.\n\n### Intersection via traversal of the list of triangles with AABB checks\n\n```C\ntest testbase::bench_intersect_120k_triangles_list_aabb                       ... bench:     687,660 ns/iter (+/- 6,850)\ntest testbase::bench_intersect_sponza_list_aabb                               ... bench:     381,037 ns/iter (+/- 1,416)\n\n// simd enabled\ntest testbase::bench_intersect_120k_triangles_list_aabb                       ... bench:     295,810 ns/iter (+/- 3,309)\ntest testbase::bench_intersect_sponza_list_aabb                               ... bench:     163,738 ns/iter (+/- 1,822)\n```\n\nAABB checks are cheap, compared to triangle-intersection algorithms. Therefore, preceeding AABB checks\nincrease intersection speed by filtering negative results a lot faster.\n\n### Build of a BVH from scratch\n\n```C\ntest flat_bvh::bench::bench_build_1200_triangles_flat_bvh                     ... bench:     242,357 ns/iter (+/- 1,882)\ntest flat_bvh::bench::bench_build_12k_triangles_flat_bvh                      ... bench:   3,681,965 ns/iter (+/- 223,716)\ntest flat_bvh::bench::bench_build_120k_triangles_flat_bvh                     ... bench:  46,415,590 ns/iter (+/- 3,226,404)\ntest bvh::bvh_impl::bench::bench_build_1200_triangles_bvh                     ... bench:     239,473 ns/iter (+/- 2,456)\ntest bvh::bvh_impl::bench::bench_build_1200_triangles_bvh_rayon               ... bench:     123,387 ns/iter (+/- 9,427)\ntest bvh::bvh_impl::bench::bench_build_12k_triangles_bvh                      ... bench:   2,903,150 ns/iter (+/- 54,318)\ntest bvh::bvh_impl::bench::bench_build_12k_triangles_bvh_rayon                ... bench:   1,073,300 ns/iter (+/- 89,530)\ntest bvh::bvh_impl::bench::bench_build_120k_triangles_bvh                     ... bench:  37,390,480 ns/iter (+/- 2,789,280)\ntest bvh::bvh_impl::bench::bench_build_120k_triangles_bvh_rayon               ... bench:   8,935,320 ns/iter (+/- 1,780,231)\ntest bvh::bvh_impl::bench::bench_build_sponza_bvh                             ... bench:  23,828,070 ns/iter (+/- 1,307,461)\ntest bvh::bvh_impl::bench::bench_build_sponza_bvh_rayon                       ... bench:   4,764,530 ns/iter (+/- 950,640)\n```\n\n### Flatten a BVH\n\n```C\ntest flat_bvh::bench::bench_flatten_120k_triangles_bvh                        ... bench:   9,806,060 ns/iter (+/- 1,771,861)\n```\n\nAs you can see, building a BVH takes a long time. Building a BVH is only useful if the number of intersections performed on the\nscene exceeds the build duration. This is the case in applications such as ray and path tracing, where the minimum\nnumber of intersections is `1280 * 720` for an HD image.\n\n### Intersection via BVH traversal\n\n```C\ntest flat_bvh::bench::bench_intersect_1200_triangles_flat_bvh                 ... bench:         144 ns/iter (+/- 1)\ntest flat_bvh::bench::bench_intersect_12k_triangles_flat_bvh                  ... bench:         370 ns/iter (+/- 4)\ntest flat_bvh::bench::bench_intersect_120k_triangles_flat_bvh                 ... bench:         866 ns/iter (+/- 16)\ntest bvh::bvh_impl::bench::bench_intersect_1200_triangles_bvh                 ... bench:         146 ns/iter (+/- 2)\ntest bvh::bvh_impl::bench::bench_intersect_12k_triangles_bvh                  ... bench:         367 ns/iter (+/- 5)\ntest bvh::bvh_impl::bench::bench_intersect_120k_triangles_bvh                 ... bench:         853 ns/iter (+/- 12)\ntest bvh::bvh_impl::bench::bench_intersect_sponza_bvh                         ... bench:       1,381 ns/iter (+/- 20)\ntest ray::ray_impl::bench::bench_intersects_aabb                              ... bench:       4,404 ns/iter (+/- 17)\n\n// simd enabled\ntest bvh::bvh_impl::bench::bench_intersect_1200_triangles_bvh                 ... bench:         121 ns/iter (+/- 2)\ntest bvh::bvh_impl::bench::bench_intersect_12k_triangles_bvh                  ... bench:         309 ns/iter (+/- 3)\ntest bvh::bvh_impl::bench::bench_intersect_120k_triangles_bvh                 ... bench:         749 ns/iter (+/- 15)\ntest bvh::bvh_impl::bench::bench_intersect_sponza_bvh                         ... bench:       1,216 ns/iter (+/- 16)\ntest ray::ray_impl::bench::bench_intersects_aabb                              ... bench:       2,447 ns/iter (+/- 18)\n```\n\nThese performance measurements show that traversing a BVH is much faster than traversing a list.\n\n### Optimization\n\nThe benchmarks for how long it takes to update the scene also contain a randomization process which takes some time.\n\n```C\ntest bvh::optimization::bench::bench_update_shapes_bvh_120k_00p               ... bench:   1,057,965 ns/iter (+/- 76,098)\ntest bvh::optimization::bench::bench_update_shapes_bvh_120k_01p               ... bench:   2,535,682 ns/iter (+/- 80,231)\ntest bvh::optimization::bench::bench_update_shapes_bvh_120k_10p               ... bench:  18,840,970 ns/iter (+/- 3,432,867)\ntest bvh::optimization::bench::bench_update_shapes_bvh_120k_50p               ... bench:  76,025,200 ns/iter (+/- 3,899,770)\ntest bvh::optimization::bench::bench_randomize_120k_50p                       ... bench:   5,361,645 ns/iter (+/- 436,340)\n```\n\nThis is the place where you have to differentiate between rebuilding the tree from scratch or trying to optimize the old one.\nThese tests show the impact of moving around a particular percentage of shapes (`10p` =\u003e `10%`).\nIt is important to note that the randomization process here moves triangles around indiscriminately.\nThis will also lead to cases where the BVH would have to be restructured completely.\n\n### Intersection after the optimization\n\nThese intersection tests are grouped by dataset and by the BVH generation method.\n* `_after_optimize` uses a BVH which was kept up to date with calls to `optimize`, while\n* `_with_rebuild` uses the same triangle data as `_after_optimize`, but constructs a BVH from scratch.\n\n*120K Triangles*\n```C\ntest bvh::optimization::bench::bench_intersect_120k_after_update_shapes_00p   ... bench:         855 ns/iter (+/- 15)\ntest bvh::optimization::bench::bench_intersect_120k_after_update_shapes_01p   ... bench:         921 ns/iter (+/- 13)\ntest bvh::optimization::bench::bench_intersect_120k_after_update_shapes_10p   ... bench:       2,677 ns/iter (+/- 191)\ntest bvh::optimization::bench::bench_intersect_120k_after_update_shapes_50p   ... bench:       2,992 ns/iter (+/- 103)\n\ntest bvh::optimization::bench::bench_intersect_120k_with_rebuild_00p          ... bench:         852 ns/iter (+/- 13)\ntest bvh::optimization::bench::bench_intersect_120k_with_rebuild_01p          ... bench:         918 ns/iter (+/- 13)\ntest bvh::optimization::bench::bench_intersect_120k_with_rebuild_10p          ... bench:       1,920 ns/iter (+/- 266)\ntest bvh::optimization::bench::bench_intersect_120k_with_rebuild_50p          ... bench:       2,075 ns/iter (+/- 318)\n```\n\n*Sponza*\n```C\ntest bvh::optimization::bench::bench_intersect_sponza_after_update_shapes_00p ... bench:       2,023 ns/iter (+/- 52)\ntest bvh::optimization::bench::bench_intersect_sponza_after_update_shapes_01p ... bench:       3,444 ns/iter (+/- 120)\ntest bvh::optimization::bench::bench_intersect_sponza_after_update_shapes_10p ... bench:      16,873 ns/iter (+/- 2,123)\ntest bvh::optimization::bench::bench_intersect_sponza_after_update_shapes_50p ... bench:       9,075 ns/iter (+/- 486)\n\ntest bvh::optimization::bench::bench_intersect_sponza_with_rebuild_00p        ... bench:       1,388 ns/iter (+/- 46)\ntest bvh::optimization::bench::bench_intersect_sponza_with_rebuild_01p        ... bench:       1,529 ns/iter (+/- 102)\ntest bvh::optimization::bench::bench_intersect_sponza_with_rebuild_10p        ... bench:       1,920 ns/iter (+/- 120)\ntest bvh::optimization::bench::bench_intersect_sponza_with_rebuild_50p        ... bench:       2,505 ns/iter (+/- 88)\n```\n\nThis set of tests shows the impact of randomly moving triangles around and producing degenerated trees.\nThe *120K Triangles* dataset has been updated randomly. The *Sponza* scene was updated using a method\nwhich has a maximum offset distance for shapes. This simulates a more realistic scenario.\n\nWe also see that the *Sponza* scene by itself contains some structures which can be tightly wrapped in a BVH.\nBy mowing those structures around we destroy the locality of the triangle groups which leads to more branches in the\nBVH requiring a check, thus leading to a higher intersection duration.\n\n### Running the benchmark suite\n\nThe benchmark suite uses features from the [test crate](https://doc.rust-lang.org/unstable-book/library-features/test.html) and therefore cannot be run on stable rust.\nUsing a nightly toolchain, run `cargo bench --features bench`.\n\n## Testing\n\n### Unit tests\n\nThis project aspires to be fully tested, and that starts with a unit test suite. Use\n`cargo test` to run all the tests. The tests automatically run in CI, too. Contributors\nare expected to add tests for all new functionality.\n\n### Proptest\n\nThis project uses [`proptest`](https://altsysrq.github.io/proptest-book/) as a second\nline of defense against bugs, allowing random instances of certain tests to be tested.\nThese tests run alongside unit-tests.\n\n### Fuzzer\n\nThis project uses [`cargo fuzz`](https://rust-fuzz.github.io/book/cargo-fuzz.html) as\na third line of defense against bugs, meaning that the `fuzz/` directory was generated\nusing `cargo fuzz init`. At the moment, there is a single fuzz target, and running\n`cargo fuzz run fuzz` will fuzz until an assertion is violated. The fuzzer automatically\nruns in CI, too.\n\n## Contributing\n\nWe recommend you install the [`just`](https://github.com/casey/just) command runner, to\nrun [commands](/justfile) including:\n- `just lint` - checks style, syntax, etc.\n- `just bench` - runs the benchmarks\n- `just test` - runs the unit tests and prop tests\n- `just fuzz` - runs the fuzzer","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvenstaro%2Fbvh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsvenstaro%2Fbvh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvenstaro%2Fbvh/lists"}