BVH.pde 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*
  2. *AABB bounding box
  3. *Bouding Volume Hierarchy
  4. */
  5. class BoundingBox {
  6. float min_x, min_y, min_z, max_x, max_y, max_z;
  7. PVector center;
  8. BoundingBox() {
  9. min_x = Float.POSITIVE_INFINITY;
  10. min_y = Float.POSITIVE_INFINITY;
  11. min_z = Float.POSITIVE_INFINITY;
  12. max_x = Float.NEGATIVE_INFINITY;
  13. max_y = Float.NEGATIVE_INFINITY;
  14. max_z = Float.NEGATIVE_INFINITY;
  15. center = new PVector();
  16. }
  17. // build a bounding box for a triangle
  18. void create(Triangle t) {
  19. min_x = min(t.p1.x, min(t.p2.x, t.p3.x));
  20. max_x = max(t.p1.x, max(t.p2.x, t.p3.x));
  21. min_y = min(t.p1.y, min(t.p2.y, t.p3.y));
  22. max_y = max(t.p1.y, max(t.p2.y, t.p3.y));
  23. min_z = min(t.p1.z, min(t.p2.z, t.p3.z));
  24. max_z = max(t.p1.z, max(t.p2.z, t.p3.z));
  25. center.x = (max_x + min_x) / 2;
  26. center.y = (max_y + min_y) / 2;
  27. center.z = (max_z + min_z) / 2;
  28. }
  29. // merge two bounding boxes
  30. void add(BoundingBox bbx) {
  31. min_x = min(min_x, bbx.min_x);
  32. min_y = min(min_y, bbx.min_y);
  33. min_z = min(min_z, bbx.min_z);
  34. max_x = max(max_x, bbx.max_x);
  35. max_y = max(max_y, bbx.max_y);
  36. max_z = max(max_z, bbx.max_z);
  37. center.x = (max_x + min_x) / 2;
  38. center.y = (max_y + min_y) / 2;
  39. center.z = (max_z + min_z) / 2;
  40. }
  41. // get bounding box center axis value
  42. float getCenterAxisValue(int axis) {
  43. if (axis == 1) {
  44. return center.x;
  45. } else if (axis == 2) {
  46. return center.y;
  47. }
  48. // when axis == 3
  49. return center.z;
  50. }
  51. // check if a ray is intersected with the bounding box
  52. boolean intersect(Ray r) {
  53. float tmin, tmax;
  54. if (r.dir.x >= 0) {
  55. tmin = (min_x - r.ori.x) * (1.0f / r.dir.x);
  56. tmax = (max_x - r.ori.x) * (1.0f / r.dir.x);
  57. } else {
  58. tmin = (max_x - r.ori.x) * (1.0f / r.dir.x);
  59. tmax = (min_x - r.ori.x) * (1.0f / r.dir.x);
  60. }
  61. float tymin, tymax;
  62. if (r.dir.y >= 0) {
  63. tymin = (min_y - r.ori.y) * (1.0f / r.dir.y);
  64. tymax = (max_y - r.ori.y) * (1.0f / r.dir.y);
  65. } else {
  66. tymin = (max_y - r.ori.y) * (1.0f / r.dir.y);
  67. tymax = (min_y - r.ori.y) * (1.0f / r.dir.y);
  68. }
  69. if (tmax < tymin || tymax < tmin) {
  70. return false;
  71. }
  72. tmin = tmin < tymin ? tymin : tmin;
  73. tmax = tmax > tymax ? tymax : tmax;
  74. float tzmin, tzmax;
  75. if (r.dir.z >= 0) {
  76. tzmin = (min_z - r.ori.z) * (1.0f / r.dir.z);
  77. tzmax = (max_z - r.ori.z) * (1.0f / r.dir.z);
  78. } else {
  79. tzmin = (max_z - r.ori.z) * (1.0f / r.dir.z);
  80. tzmax = (min_z - r.ori.z) * (1.0f / r.dir.z);
  81. }
  82. if (tmax < tzmin || tmin > tzmax) {
  83. return false;
  84. }
  85. return true;
  86. }
  87. }
  88. // Bounding Volume Hierarchy
  89. class BVH {
  90. // Binary Tree
  91. BVH left, right;
  92. BoundingBox overall_bbx;
  93. ArrayList<Triangle> mesh;
  94. BVH(ArrayList<Triangle> mesh) {
  95. this.mesh = mesh;
  96. overall_bbx = new BoundingBox();
  97. left = null;
  98. right = null;
  99. int mesh_size = this.mesh.size();
  100. if (mesh_size <= 1) {
  101. return;
  102. }
  103. // random select an axis
  104. int axis = int(random(100)) % 3 + 1;
  105. // build bounding box and save the selected center component
  106. float[] axis_values = new float[mesh_size];
  107. for (int i = 0; i < mesh_size; i++) {
  108. Triangle t = this.mesh.get(i);
  109. overall_bbx.add(t.bbx);
  110. axis_values[i] = t.bbx.getCenterAxisValue(axis);
  111. }
  112. // find the median value of selected center component as pivot
  113. axis_values = sort(axis_values);
  114. float pivot;
  115. if (mesh_size % 2 == 1) {
  116. pivot = axis_values[mesh_size / 2];
  117. } else {
  118. pivot =
  119. 0.5f * (axis_values[mesh_size / 2 - 1] + axis_values[mesh_size / 2]);
  120. }
  121. // Build left node and right node by partitioning the mesh based on triangle
  122. // bounding box center component value
  123. ArrayList<Triangle> left_mesh = new ArrayList<Triangle>();
  124. ArrayList<Triangle> right_mesh = new ArrayList<Triangle>();
  125. for (int i = 0; i < mesh_size; i++) {
  126. Triangle t = this.mesh.get(i);
  127. if (t.bbx.getCenterAxisValue(axis) < pivot) {
  128. left_mesh.add(t);
  129. } else if (t.bbx.getCenterAxisValue(axis) > pivot) {
  130. right_mesh.add(t);
  131. } else if (left_mesh.size() < right_mesh.size()) {
  132. left_mesh.add(t);
  133. } else {
  134. right_mesh.add(t);
  135. }
  136. }
  137. left = new BVH(left_mesh);
  138. right = new BVH(right_mesh);
  139. }
  140. // check if a ray intersect with current volume
  141. boolean intersect(Ray r, float[] param) {
  142. if (mesh.size() == 0) {
  143. return false;
  144. }
  145. if (mesh.size() == 1) {
  146. Triangle t = mesh.get(0);
  147. return t.intersect(r, param);
  148. }
  149. if (!overall_bbx.intersect(r)) {
  150. return false;
  151. }
  152. boolean left_res = left.intersect(r, param);
  153. boolean right_res = right.intersect(r, param);
  154. return left_res || right_res;
  155. }
  156. }