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