1 #include "nanort.h"
2 
3 #define STB_IMAGE_WRITE_IMPLEMENTATION
4 #include "../common/stb_image_write.h"
5 
6 #include <stdint.h>
7 
8 namespace {
9 
10 // PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
11 // Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
12 // http://www.pcg-random.org/
13 typedef struct {
14   unsigned long long state;
15   unsigned long long inc;  // not used?
16 } pcg32_state_t;
17 
18 #define PCG32_INITIALIZER \
19   { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }
20 
pcg32_random(pcg32_state_t * rng)21 float pcg32_random(pcg32_state_t *rng) {
22   unsigned long long oldstate = rng->state;
23   rng->state = oldstate * 6364136223846793005ULL + rng->inc;
24   unsigned int xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
25   unsigned int rot = oldstate >> 59u;
26   unsigned int ret = (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
27 
28   return (float)((double)ret / (double)4294967296.0);
29 }
30 
pcg32_srandom(pcg32_state_t * rng,uint64_t initstate,uint64_t initseq)31 void pcg32_srandom(pcg32_state_t *rng, uint64_t initstate, uint64_t initseq) {
32   rng->state = 0U;
33   rng->inc = (initseq << 1U) | 1U;
34   pcg32_random(rng);
35   rng->state += initstate;
36   pcg32_random(rng);
37 }
38 
fclamp(float x)39 unsigned char fclamp(float x) {
40   int i = (int)(powf(x, 1.0 / 2.2) * 256.0f);
41   if (i > 255) i = 255;
42   if (i < 0) i = 0;
43 
44   return (unsigned char)i;
45 }
46 
SaveImagePNG(const char * filename,const float * rgb,int width,int height)47 void SaveImagePNG(const char *filename, const float *rgb, int width,
48                   int height) {
49   std::vector<unsigned char> ldr(width * height * 3);
50   for (size_t i = 0; i < (size_t)(width * height * 3); i++) {
51     ldr[i] = fclamp(rgb[i]);
52   }
53 
54   int len = stbi_write_png(filename, width, height, 3, &ldr.at(0), width * 3);
55   if (len < 1) {
56     printf("Failed to save image\n");
57     exit(-1);
58   }
59 }
60 
solve2e(float root[],float A,float B,float C)61 int solve2e(float root[], float A, float B, float C) {
62   if (fabsf(A) <= 1.0e-6f) {
63     float x = -C / B;
64     root[0] = x;
65     return 1;
66   } else {
67     float D = B * B - A * C;
68     if (D < 0) {
69       return 0;
70     } else if (D == 0) {
71       float x = -B / A;
72       root[0] = x;
73       return 1;
74     } else {
75       float x1 = (fabsf(B) + sqrtf(D)) / A;
76       if (B >= 0.0) {
77         x1 = -x1;
78       }
79       float x2 = C / (A * x1);
80       if (x1 > x2) {
81         float tmp = x1;
82         x1 = x2;
83         x2 = tmp;
84       }
85 
86       root[0] = x1;
87       root[1] = x2;
88       return 2;
89     }
90   }
91 }
92 
93 // Predefined SAH predicator for cylinder.
94 class CylinderPred {
95  public:
CylinderPred(const float * vertices)96   CylinderPred(const float *vertices)
97       : axis_(0), pos_(0.0f), vertices_(vertices) {}
98 
Set(int axis,float pos) const99   void Set(int axis, float pos) const {
100     axis_ = axis;
101     pos_ = pos;
102   }
103 
operator ()(unsigned int i) const104   bool operator()(unsigned int i) const {
105     int axis = axis_;
106     float pos = pos_;
107 
108     nanort::real3<float> p0(&vertices_[3 * (2 * i + 0)]);
109     nanort::real3<float> p1(&vertices_[3 * (2 * i + 1)]);
110 
111     float center = (p0[axis] + p1[axis]) / 2.0f;
112 
113     return (center < pos);
114   }
115 
116  private:
117   mutable int axis_;
118   mutable float pos_;
119   const float *vertices_;
120 };
121 
122 // -----------------------------------------------------
123 
124 class CylinderGeometry {
125  public:
CylinderGeometry(const float * vertices,const float * radiuss)126   CylinderGeometry(const float *vertices, const float *radiuss)
127       : vertices_(vertices), radiuss_(radiuss) {}
128 
129   /// Compute bounding box for `prim_index`th cylinder.
130   /// This function is called for each primitive in BVH build.
BoundingBox(nanort::real3<float> * bmin,nanort::real3<float> * bmax,unsigned int prim_index) const131   void BoundingBox(nanort::real3<float> *bmin, nanort::real3<float> *bmax,
132                    unsigned int prim_index) const {
133     (*bmin)[0] =
134         vertices_[3 * (2 * prim_index + 0) + 0] - radiuss_[2 * prim_index + 0];
135     (*bmin)[1] =
136         vertices_[3 * (2 * prim_index + 0) + 1] - radiuss_[2 * prim_index + 0];
137     (*bmin)[2] =
138         vertices_[3 * (2 * prim_index + 0) + 2] - radiuss_[2 * prim_index + 0];
139     (*bmax)[0] =
140         vertices_[3 * (2 * prim_index + 0) + 0] + radiuss_[2 * prim_index + 0];
141     (*bmax)[1] =
142         vertices_[3 * (2 * prim_index + 0) + 1] + radiuss_[2 * prim_index + 0];
143     (*bmax)[2] =
144         vertices_[3 * (2 * prim_index + 0) + 2] + radiuss_[2 * prim_index + 0];
145 
146     (*bmin)[0] = std::min(
147         vertices_[3 * (2 * prim_index + 1) + 0] - radiuss_[2 * prim_index + 1],
148         (*bmin)[0]);
149     (*bmin)[1] = std::min(
150         vertices_[3 * (2 * prim_index + 1) + 1] - radiuss_[2 * prim_index + 1],
151         (*bmin)[1]);
152     (*bmin)[2] = std::min(
153         vertices_[3 * (2 * prim_index + 1) + 2] - radiuss_[2 * prim_index + 1],
154         (*bmin)[2]);
155     (*bmax)[0] = std::max(
156         vertices_[3 * (2 * prim_index + 1) + 0] + radiuss_[2 * prim_index + 1],
157         (*bmax)[0]);
158     (*bmax)[1] = std::max(
159         vertices_[3 * (2 * prim_index + 1) + 1] + radiuss_[2 * prim_index + 1],
160         (*bmax)[1]);
161     (*bmax)[2] = std::max(
162         vertices_[3 * (2 * prim_index + 1) + 2] + radiuss_[2 * prim_index + 1],
163         (*bmax)[2]);
164   }
165 
166   const float *vertices_;
167   const float *radiuss_;
168   mutable nanort::real3<float> ray_org_;
169   mutable nanort::real3<float> ray_dir_;
170   mutable nanort::BVHTraceOptions trace_options_;
171 };
172 
173 class CylinderIntersection {
174  public:
CylinderIntersection()175   CylinderIntersection() {}
176 
177   float u;
178   float v;
179   nanort::real3<float> normal;
180 
181   // Required member variables.
182   float t;
183   unsigned int prim_id;
184 };
185 
186 template <class I>
187 class CylinderIntersector {
188  public:
CylinderIntersector(const float * vertices,const float * radiuss,const bool test_cap=true)189   CylinderIntersector(const float *vertices, const float *radiuss,
190                       const bool test_cap = true)
191       : vertices_(vertices), radiuss_(radiuss), test_cap_(test_cap) {}
192 
193   /// Do ray interesection stuff for `prim_index` th primitive and return hit
194   /// distance `t`,
195   /// varycentric coordinate `u` and `v`.
196   /// Returns true if there's intersection.
Intersect(float * t_inout,unsigned int prim_index) const197   bool Intersect(float *t_inout, unsigned int prim_index) const {
198     if ((prim_index < trace_options_.prim_ids_range[0]) ||
199         (prim_index >= trace_options_.prim_ids_range[1])) {
200       return false;
201     }
202     const float kEPS = 1.0e-6f;
203 
204     nanort::real3<float> p0, p1;
205     p0[0] = vertices_[3 * (2 * prim_index + 0) + 0];
206     p0[1] = vertices_[3 * (2 * prim_index + 0) + 1];
207     p0[2] = vertices_[3 * (2 * prim_index + 0) + 2];
208     p1[0] = vertices_[3 * (2 * prim_index + 1) + 0];
209     p1[1] = vertices_[3 * (2 * prim_index + 1) + 1];
210     p1[2] = vertices_[3 * (2 * prim_index + 1) + 2];
211 
212     float r0 = radiuss_[2 * prim_index + 0];
213     float r1 = radiuss_[2 * prim_index + 1];
214 
215     float tmax = (*t_inout);
216     float rr = std::max<float>(r0, r1);
217     nanort::real3<float> ORG = ray_org_;
218     nanort::real3<float> n = ray_dir_;
219     nanort::real3<float> d = p1 - p0;
220     nanort::real3<float> m = ORG - p0;
221 
222     float md = vdot(m, d);
223     float nd = vdot(n, d);
224     float dd = vdot(d, d);
225 
226     bool hitCap = false;
227     float capT = std::numeric_limits<float>::max();  // far
228 
229     if (test_cap_) {
230       nanort::real3<float> dN0 = vnormalize(p0 - p1);
231       nanort::real3<float> dN1 = vneg(dN0);
232       nanort::real3<float> rd = vnormalize(ray_dir_);
233 
234       if (fabs(vdot(ray_dir_, dN0)) > kEPS) {
235         // test with 2 planes
236         float p0D = -vdot(p0, dN0);  // plane D
237         float p1D = -vdot(p1, dN1);  // plane D
238 
239         float p0T = -(vdot(ray_org_, dN0) + p0D) / vdot(rd, dN0);
240         float p1T = -(vdot(ray_org_, dN1) + p1D) / vdot(rd, dN1);
241 
242         nanort::real3<float> q0 = ray_org_ + p0T * rd;
243         nanort::real3<float> q1 = ray_org_ + p1T * rd;
244 
245         float qp0Sqr = vdot(q0 - p0, q0 - p0);
246         float qp1Sqr = vdot(q1 - p1, q1 - p1);
247         // printf("p0T = %f, p1T = %f, q0Sqr = %f, rr^2 = %f\n", p0T, p1T,
248         // q0Sqr,
249         // rr*rr);
250 
251         if (p0T > 0.0 && p0T < tmax && (qp0Sqr < rr * rr)) {
252           // hit p0's plane
253           hit_cap_ = hitCap = true;
254           capT = p0T;
255           (*t_inout) = capT;
256           u_param_ = sqrt(qp0Sqr);
257           v_param_ = 0;
258         }
259 
260         if (p1T > 0.0 && p1T < tmax && p1T < capT && (qp1Sqr < rr * rr)) {
261           hit_cap_ = hitCap = true;
262           capT = p1T;
263           (*t_inout) = capT;
264           u_param_ = sqrt(qp1Sqr);
265           v_param_ = 1.0;
266         }
267       }
268     }
269 
270     if (md <= 0.0 && nd <= 0.0) return hitCap;
271     if (md >= dd && nd >= 0.0) return hitCap;
272 
273     float nn = vdot(n, n);
274     float mn = vdot(m, n);
275     float A = dd * nn - nd * nd;
276     float k = vdot(m, m) - rr * rr;
277     float C = dd * k - md * md;
278     float B = dd * mn - nd * md;
279 
280     float root[2] = {};
281     int nRet = solve2e(root, A, B, C);
282     if (nRet) {
283       float t = root[0];
284       if (0 <= t && t <= tmax && t <= capT) {
285         float s = md + t * nd;
286         s /= dd;
287         if (0 <= s && s <= 1) {
288           hit_cap_ = hitCap = false;
289           (*t_inout) = t;
290           u_param_ = 0;
291           v_param_ = s;
292 
293           return true;
294         }
295       }
296     }
297     return hitCap;
298   }
299 
300   /// Returns the nearest hit distance.
GetT() const301   float GetT() const { return t_; }
302 
303   /// Update is called when a nearest hit is found.
Update(float t,unsigned int prim_idx) const304   void Update(float t, unsigned int prim_idx) const {
305     t_ = t;
306     prim_id_ = prim_idx;
307   }
308 
309   /// Prepare BVH traversal(e.g. compute inverse ray direction)
310   /// This function is called only once in BVH traversal.
PrepareTraversal(const nanort::Ray<float> & ray,const nanort::BVHTraceOptions & trace_options) const311   void PrepareTraversal(const nanort::Ray<float> &ray,
312                         const nanort::BVHTraceOptions &trace_options) const {
313     ray_org_[0] = ray.org[0];
314     ray_org_[1] = ray.org[1];
315     ray_org_[2] = ray.org[2];
316 
317     ray_dir_[0] = ray.dir[0];
318     ray_dir_[1] = ray.dir[1];
319     ray_dir_[2] = ray.dir[2];
320 
321     trace_options_ = trace_options;
322   }
323 
324   /// Post BVH traversal stuff(e.g. compute intersection point information)
325   /// This function is called only once in BVH traversal.
326   /// `hit` = true if there is something hit.
PostTraversal(const nanort::Ray<float> & ray,bool hit,CylinderIntersection * isect) const327   void PostTraversal(const nanort::Ray<float> &ray, bool hit,
328                      CylinderIntersection *isect) const {
329     if (hit) {
330       float v = v_param_;
331       unsigned int index = prim_id_;
332       nanort::real3<float> p0, p1;
333 
334       p0[0] = vertices_[3 * (2 * index + 0) + 0];
335       p0[1] = vertices_[3 * (2 * index + 0) + 1];
336       p0[2] = vertices_[3 * (2 * index + 0) + 2];
337       p1[0] = vertices_[3 * (2 * index + 1) + 0];
338       p1[1] = vertices_[3 * (2 * index + 1) + 1];
339       p1[2] = vertices_[3 * (2 * index + 1) + 2];
340 
341       nanort::real3<float> center =
342           p0 + nanort::real3<float>(v, v, v) * (p1 - p0);
343       nanort::real3<float> position = ray_org_ + t_ * ray_dir_;
344 
345       nanort::real3<float> n;
346       if (hit_cap_) {
347         nanort::real3<float> c = 0.5f * (p1 - p0) + p0;
348         n = vnormalize(p1 - p0);
349 
350         if (vdot((position - c), n) > 0.0) {
351           // hit p1's plane
352         } else {
353           // hit p0's plane
354           n = vneg(n);
355         }
356       } else {
357         n = position - center;
358         n = vnormalize(n);
359       }
360 
361       isect->u = u_param_;
362       isect->v = v_param_;
363       isect->prim_id = prim_id_;
364 
365       isect->normal[0] = n[0];
366       isect->normal[1] = n[1];
367       isect->normal[2] = n[2];
368     }
369   }
370 
371   const float *vertices_;
372   const float *radiuss_;
373   const bool test_cap_;
374   mutable nanort::real3<float> ray_org_;
375   mutable nanort::real3<float> ray_dir_;
376   mutable nanort::BVHTraceOptions trace_options_;
377 
378   mutable float t_;
379   mutable unsigned int prim_id_;
380 
381   mutable bool hit_cap_;
382   mutable float u_param_;
383   mutable float v_param_;
384 };
385 
386 // -----------------------------------------------------
387 
GenerateRandomCylinders(float * vertices,float * radiuss,size_t n,const float bmin[3],const float bmax[3])388 void GenerateRandomCylinders(float *vertices, float *radiuss, size_t n,
389                              const float bmin[3], const float bmax[3]) {
390   pcg32_state_t rng;
391   pcg32_srandom(&rng, 0, 1);
392 
393   float bsize = bmax[0] - bmin[0];
394   if (bsize < bmax[1] - bmin[1]) {
395     bsize = bmax[1] - bmin[1];
396   }
397   if (bsize < bmax[2] - bmin[2]) {
398     bsize = bmax[2] - bmin[2];
399   }
400 
401   for (size_t i = 0; i < n; i++) {
402     // [0, 1)
403     float x0 = pcg32_random(&rng);
404     float y0 = pcg32_random(&rng);
405     float z0 = pcg32_random(&rng);
406     float x1 = pcg32_random(&rng);
407     float y1 = pcg32_random(&rng);
408     float z1 = pcg32_random(&rng);
409 
410     vertices[3 * (2 * i + 0) + 0] = x0 * (bmax[0] - bmin[0]) + bmin[0];
411     vertices[3 * (2 * i + 0) + 1] = y0 * (bmax[1] - bmin[1]) + bmin[1];
412     vertices[3 * (2 * i + 0) + 2] = z0 * (bmax[2] - bmin[2]) + bmin[2];
413     vertices[3 * (2 * i + 1) + 0] = x1 * (bmax[0] - bmin[0]) + bmin[0];
414     vertices[3 * (2 * i + 1) + 1] = y1 * (bmax[1] - bmin[1]) + bmin[1];
415     vertices[3 * (2 * i + 1) + 2] = z1 * (bmax[2] - bmin[2]) + bmin[2];
416 
417     // Adjust radius according to # of primitives.
418     radiuss[2 * i + 0] = (0.25 * bsize) / sqrt((double)n);
419     radiuss[2 * i + 1] = (0.25 * bsize) / sqrt((double)n);
420   }
421 }
422 
423 }  // namespace
424 
main(int argc,char ** argv)425 int main(int argc, char **argv) {
426   int width = 512;
427   int height = 513;
428 
429   if (argc < 2) {
430     printf("Needs # of cylinders\n");
431     return 0;
432   }
433 
434   size_t n = atoi(argv[1]);
435 
436   nanort::BVHBuildOptions<float> options;  // Use default option
437   options.cache_bbox = false;
438 
439   printf("  BVH build option:\n");
440   printf("    # of leaf primitives: %d\n", options.min_leaf_primitives);
441   printf("    SAH binsize         : %d\n", options.bin_size);
442 
443   std::vector<float> vertices(3 * 2 * n);
444   std::vector<float> radiuss(2 * n);
445 
446   float rbmin[3] = {-1, -1, -1};
447   float rbmax[3] = {1, 1, 1};
448   GenerateRandomCylinders(&vertices.at(0), &radiuss.at(0), n, rbmin, rbmax);
449 
450   CylinderGeometry cylinder_geom(&vertices.at(0), &radiuss.at(0));
451   CylinderPred cylinder_pred(&vertices.at(0));
452 
453   nanort::BVHAccel<float> accel;
454   bool ret = accel.Build(n, cylinder_geom, cylinder_pred, options);
455   assert(ret);
456 
457   nanort::BVHBuildStatistics stats = accel.GetStatistics();
458 
459   printf("  BVH statistics:\n");
460   printf("    # of leaf   nodes: %d\n", stats.num_leaf_nodes);
461   printf("    # of branch nodes: %d\n", stats.num_branch_nodes);
462   printf("  Max tree depth     : %d\n", stats.max_tree_depth);
463   float bmin[3], bmax[3];
464   accel.BoundingBox(bmin, bmax);
465   printf("  Bmin               : %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
466   printf("  Bmax               : %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
467 
468   std::vector<float> rgb(width * height * 3, 0.0f);
469 
470   // Shoot rays.
471   for (int y = 0; y < height; y++) {
472     for (int x = 0; x < width; x++) {
473       // Simple camera. change eye pos and direction fit to your scene.
474 
475       nanort::Ray<float> ray;
476       ray.org[0] = 0.0f;
477       ray.org[1] = 0.0f;
478       ray.org[2] = 4.0f;
479 
480       nanort::real3<float> dir;
481       dir[0] = (x / (float)width) - 0.5f;
482       dir[1] = (y / (float)height) - 0.5f;
483       dir[2] = -1.0f;
484       dir = vnormalize(dir);
485       ray.dir[0] = dir[0];
486       ray.dir[1] = dir[1];
487       ray.dir[2] = dir[2];
488 
489       float kFar = 1.0e+30f;
490       ray.min_t = 0.0f;
491       ray.max_t = kFar;
492 
493       CylinderIntersector<CylinderIntersection> isector(&vertices.at(0),
494                                                         &radiuss.at(0));
495       CylinderIntersection isect;
496       bool hit = accel.Traverse(ray, isector, &isect);
497       if (hit) {
498         // Flip Y
499         rgb[3 * ((height - y - 1) * width + x) + 0] = fabsf(isect.normal[0]);
500         rgb[3 * ((height - y - 1) * width + x) + 1] = fabsf(isect.normal[1]);
501         rgb[3 * ((height - y - 1) * width + x) + 2] = fabsf(isect.normal[2]);
502       }
503     }
504   }
505 
506   SaveImagePNG("render.png", &rgb.at(0), width, height);
507 
508   return 0;
509 }
510