Tachyon (current)  Current Main Branch
colorchecker.cpp
Go to the documentation of this file.
1 /*
2  * colorchecker.cpp - "ColorChecker" scene to test handling of color spaces
3  *
4  * (C) Copyright 2013-2022 John E. Stone
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * $Id: colorchecker.cpp,v 1.3 2022/04/04 05:01:24 johns Exp $
8  *
9  */
10 
20 //
21 // Emulation of the famour "ColorChecker" color rendition chart
22 // produced by Gretag-Macbeth / X-Rite / Calibrite for photographic
23 // color calibration purposes:
24 // https://home.cis.rit.edu/~cnspci/references/mccamy1976.pdf
25 // https://www.babelcolor.com/colorchecker.htm
26 // http://poynton.ca/notes/color/GretagMacbeth-ColorChecker.html
27 //
28 // This example uses a table of sRGB colors from pre-2014 charts
29 // produced by Gretag-Macbeth / X-Rite / Calibrite.
30 // Charts made after 2014 use a slightly different color formulation
31 // resulting in measurable differences.
32 //
33 // The sRGB values are as-reported by X-Rite in 2009, for a D65 illuminant:
34 // https://xritephoto.com/documents/literature/en/ColorData-1p_EN.pdf
35 //
36 // John E. Stone, Jan 2022
37 //
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <math.h>
43 #include "ProfileHooks.h"
44 
45 #define STB_IMAGE_WRITE_IMPLEMENTATION
46 #include "stb_image_write.h"
47 
48 #include "TachyonOptiX.h"
49 
50 #if defined(TACHYON_USEPINNEDMEMORY)
51 #include <cuda_runtime.h>
52 #endif
53 
54 #include "colorchecker_tables.h"
55 
56 
57 
58 void HSItoRGB(float h, float s, float i, float &r, float &g, float &b) {
59  float t=2.0f * M_PI * h;
60  float scale = i / 2.0f;
61  r=(1.0f + s*sin(t - 2.0f*M_PI/3.0f)) * scale;
62  g=(1.0f + s*sin(t)) * scale;
63  b=(1.0f + s*sin(t + 2.0f*M_PI/3.0f)) * scale;
64 }
65 
66 void int_to_normalized_float(const int *srgbi, int maxval, float *srgbf) {
67  float invmaxval = 1.0f / float(maxval);
68  for (int c=0; c<3; c++) {
69  srgbf[c] = srgbi[c] * invmaxval;
70  }
71 }
72 
73 void srgb_to_linear(const float *srgb, float *lin) {
74  for (int c=0; c<3; c++) {
75  if (srgb[c] <= 0.0404482362771082f) {
76  lin[c] = srgb[c] / 12.92f;
77  } else {
78  lin[c] = powf(((srgb[c] + 0.055f)/1.055f), 2.4f);
79  }
80  }
81 }
82 
83 void linear_to_srgb(const float *lin, float *srgb) {
84  for (int c=0; c<3; c++) {
85  if (lin[c] > 0.0031308f) {
86  srgb[c] = 1.055f * (powf(lin[c], (1.0f / 2.4f))) - 0.055f;
87  } else {
88  srgb[c] = 12.92f * lin[c];
89  }
90  }
91 }
92 
93 
94 
95 // XXX todo templatize this later
96 //
97 // Trivial 2-D box rasterization code to make a ColorChecker reference image
98 // completely from scratch, for use in a texture map, or otherwise.
99 // It is up to the caller to ensure that the correct space is being used.
100 //
101 void img_draw_box_rgba4ub(int bxs, int bys, int bxe, int bye,
102  unsigned char *rgba4ub,
103  int imgxs, int imgys, unsigned char *img) {
104  if (bxs < 0) bxs = 0;
105  if (bxs >= imgxs) bxs = imgxs - 1;
106  if (bys < 0) bys = 0;
107  if (bys >= imgys) bys = imgys - 1;
108 
109  if (bxe < 0) bxe = 0;
110  if (bxe >= imgxs) bxe = imgxs - 1;
111  if (bye < 0) bye = 0;
112  if (bye >= imgys) bye = imgys - 1;
113 
114  for (int y=bys; y<bye; y++) {
115  unsigned char *rowaddr = img + y*imgxs*4;
116  for (int x4=bxs*4; x4<bxe*4; x4+=4) {
117  unsigned char *px = rowaddr + x4;
118  px[0] = rgba4ub[0];
119  px[1] = rgba4ub[1];
120  px[2] = rgba4ub[2];
121  px[3] = rgba4ub[3];
122  }
123  }
124 }
125 
126 
127 //
128 // Trivial 2-D box rasterization code to make a ColorChecker reference image
129 // completely from scratch, for use in a texture map, or otherwise.
130 // It is up to the caller to ensure that the correct space is being used.
131 //
132 void img_draw_box_rgbaf32(int bxs, int bys, int bxe, int bye,
133  float *rgbaf32,
134  int imgxs, int imgys, float *img) {
135  if (bxs < 0) bxs = 0;
136  if (bxs >= imgxs) bxs = imgxs - 1;
137  if (bys < 0) bys = 0;
138  if (bys >= imgys) bys = imgys - 1;
139 
140  if (bxe < 0) bxe = 0;
141  if (bxe >= imgxs) bxe = imgxs - 1;
142  if (bye < 0) bye = 0;
143  if (bye >= imgys) bye = imgys - 1;
144 
145  for (int y=bys; y<bye; y++) {
146  float *rowaddr = img + y*imgxs*4;
147  for (int x4=bxs*4; x4<bxe*4; x4+=4) {
148  float *px = rowaddr + x4;
149  px[0] = rgbaf32[0];
150  px[1] = rgbaf32[1];
151  px[2] = rgbaf32[2];
152  px[3] = rgbaf32[3];
153  }
154  }
155 }
156 
157 
158 //
159 // Rasterize a simple 2-D emulation of the ColorChecker Classic
160 // color rendition chart as a 32-bit RGBA image in sRGB colorspace.
161 //
162 unsigned char *img_gen_colorchecker_4ub(int tilesz, int pad, const int *srgbi,
163  int & imgszx, int & imgszy) {
164  int bsz = tilesz + 2*pad;
165  imgszx = 6 * bsz + 2*pad;
166  imgszy = 4 * bsz + 2*pad;
167  unsigned char *img = new unsigned char[imgszx * imgszy * 4];
168 
169  unsigned char background[4] = {0, 0, 0, 255};
170  img_draw_box_rgba4ub(0, 0, imgszx-1, imgszy-1, background,
171  imgszx, imgszy, img);
172 
173  for (int y=0; y<4; y++) {
174  for (int x=0; x<6; x++) {
175  int idx3 = (y*6 + x) * 3;
176 
177  // fetch/convert colorchecker sRGB unsigned byte color values
178  // into local RGBA buffer
179  unsigned char srgba4ub[4];
180  srgba4ub[0] = srgbi[idx3 ];
181  srgba4ub[1] = srgbi[idx3 + 1];
182  srgba4ub[2] = srgbi[idx3 + 2];
183  srgba4ub[3] = 255;
184 
185  int bxs = pad + x*bsz + pad;
186  int bxe = pad + (x+1)*bsz - pad;
187  int bys = pad + y*bsz + pad;
188  int bye = pad + (y+1)*bsz - pad;
189 
190  img_draw_box_rgba4ub(bxs, bys, bxe, bye, srgba4ub, imgszx, imgszy, img);
191  }
192  }
193 
194  return img;
195 }
196 
197 
198 //
199 // Rasterize a simple 2-D emulation of the ColorChecker Classic
200 // color rendition chart as an FP RGBA image (linear colorspace)
201 //
202 float *img_gen_colorchecker_f32(int tilesz, int pad, const int *srgbi,
203  int maxval, int convertsrgbtolinear,
204  int & imgszx, int & imgszy) {
205  int bsz = tilesz + 2*pad;
206  imgszx = 6 * bsz + 2*pad;
207  imgszy = 4 * bsz + 2*pad;
208  float *img = new float[imgszx * imgszy * 4];
209 
210  float background[4] = {0.0f, 0.0f, 0.0f, 1.0f};
211  img_draw_box_rgbaf32(0, 0, imgszx-1, imgszy-1, background,
212  imgszx, imgszy, img);
213 
214  for (int y=0; y<4; y++) {
215  for (int x=0; x<6; x++) {
216  int idx3 = (y*6 + x) * 3;
217 
218  // fetch/convert colorchecker sRGB unsigned byte color values
219  // into local RGBA buffer
220  float tmp[4];
221  tmp[0] = srgbi[idx3 ] / float(maxval);
222  tmp[1] = srgbi[idx3 + 1] / float(maxval);
223  tmp[2] = srgbi[idx3 + 2] / float(maxval);
224  tmp[3] = 1.0f;
225 
226  float rgbaf32[4];
227  if (convertsrgbtolinear) {
228  srgb_to_linear(tmp, rgbaf32);
229  } else {
230  memcpy(rgbaf32, tmp, sizeof(tmp));
231  }
232 
233  int bxs = pad + x*bsz + pad;
234  int bxe = pad + (x+1)*bsz - pad;
235  int bys = pad + y*bsz + pad;
236  int bye = pad + (y+1)*bsz - pad;
237 
238  img_draw_box_rgbaf32(bxs, bys, bxe, bye, rgbaf32, imgszx, imgszy, img);
239  }
240  }
241 
242  return img;
243 }
244 
245 
246 
247 //
248 // Generate a planar grid of ColorChecker patch center coordinates
249 //
250 float * calc_colorchecker_gridpts(float tilesz, float pad, float height) {
251  const int numpts = COLORCHECKER_NUM_PATCHES;
252  float *coords = new float[numpts * 3];
253 
254  float psz = tilesz + 2.0f*pad; // patch size
255  float szx = 6.0f * psz + 2.0f*pad;
256  float szy = 4.0f * psz + 2.0f*pad;
257 
258  float szxh = szx * 0.5f;
259  float szyh = szy * 0.5f;
260  float htsz = tilesz * 0.5f;
261 
262  int idx=0;
263  for (int y=0; y<4; y++) {
264  for (int x=0; x<6; x++) {
265  float px = pad + x*psz + pad + htsz;
266  float py = pad + y*psz + pad + htsz;
267 
268  coords[idx ] = px - szxh;
269  coords[idx + 1] = height;
270  coords[idx + 2] = -py + szyh;
271  idx+=3;
272  }
273  }
274 
275  return coords;
276 }
277 
278 
279 //
280 // Generate linearized single-precision FP RGBA colors from integer sRGB.
281 // To permit later support for higher bit depth colors, the maxval
282 // parameter is used to control normalization when converting to
283 // floating point representation.
284 //
285 float * calc_colorchecker_linear_rgba(const int *srgbi, int maxval) {
286  const int numpts = COLORCHECKER_NUM_PATCHES;
287 
288  float *colors = new float[numpts * 4];
289  for (int y=0; y<4; y++) {
290  for (int x=0; x<6; x++) {
291  float srgb[3];
292 
293  int idx3 = (y*6 + x) * 3;
294  int idx4 = (y*6 + x) * 4;
295  int_to_normalized_float(&srgbi[idx3], maxval, srgb);
296  srgb_to_linear(srgb, &colors[idx4]);
297  colors[idx4 + 3] = 1.0f; // opaque
298  }
299  }
300 
301  return colors;
302 }
303 
304 
305 //
306 // Generate a planar grid of ColorChecker patch vertices with implied
307 // connectivity
308 //
309 float * calc_colorchecker_patch_verts(float tilesz, float pad, float height) {
310  const int numpts = COLORCHECKER_NUM_PATCHES;
311  float *verts = new float[numpts * 4 * 3];
312 
313  float psz = tilesz + 2.0f*pad; // patch size
314  float szx = 6.0f * psz + 2.0f*pad;
315  float szy = 4.0f * psz + 2.0f*pad;
316 
317  float szxh = szx * 0.5f;
318  float szyh = szy * 0.5f;
319 
320  int idx=0;
321  for (int y=0; y<4; y++) {
322  for (int x=0; x<6; x++) {
323 
324  float pxs = pad + x*psz + pad;
325  float pxe = pad + (x+1)*psz - pad;
326  float pys = pad + y*psz + pad;
327  float pye = pad + (y+1)*psz - pad;
328 
329  verts[idx ] = pxs - szxh;
330  verts[idx + 1] = height;
331  verts[idx + 2] = -pys + szyh;
332  idx+=3;
333 
334  verts[idx ] = pxe - szxh;
335  verts[idx + 1] = height;
336  verts[idx + 2] = -pys + szyh;
337  idx+=3;
338 
339  verts[idx ] = pxs - szxh;
340  verts[idx + 1] = height;
341  verts[idx + 2] = -pye + szyh;
342  idx+=3;
343 
344  verts[idx ] = pxe - szxh;
345  verts[idx + 1] = height;
346  verts[idx + 2] = -pye + szyh;
347  idx+=3;
348  }
349  }
350 
351  return verts;
352 }
353 
354 
355 //
356 // Generate triangle mesh vertex connectivity for patch grid vertex array
357 //
358 int * calc_colorchecker_patch_indices(int &numtris, int &meshindices) {
359  const int numpts = COLORCHECKER_NUM_PATCHES;
360  numtris = numpts * 2;
361  meshindices = numtris * 3;
362  int *indices = new int[meshindices];
363  memset(indices, 0, meshindices * sizeof(int));
364 
365  int idx4=0, idx6=0;
366  for (int y=0; y<4; y++) {
367  for (int x=0; x<6; x++,idx4+=4,idx6+=6) {
368  indices[idx6 ] = idx4;
369  indices[idx6 + 1] = idx4 + 1;
370  indices[idx6 + 2] = idx4 + 2;
371 
372  indices[idx6 + 3] = idx4 + 2;
373  indices[idx6 + 4] = idx4 + 1;
374  indices[idx6 + 5] = idx4 + 3;
375  }
376  }
377 
378  return indices;
379 }
380 
381 
382 //
383 // Generate triangle mesh vertex colors
384 //
385 float * calc_colorchecker_patch_colors(const float *colors) {
386  const int numpts = COLORCHECKER_NUM_PATCHES;
387  int numverts = numpts * 4;
388  float *patchcols = new float[numverts * 4];
389 
390  int vidx = 0;
391  for (int i=0; i<numpts; i++) {
392  int pidx = i*4;
393  // replicate color for 4 verts per patch
394  for (int v=0; v<4; v++,vidx+=4) {
395  patchcols[vidx ] = colors[pidx ];
396  patchcols[vidx + 1] = colors[pidx + 1];
397  patchcols[vidx + 2] = colors[pidx + 2];
398  patchcols[vidx + 3] = colors[pidx + 3];
399  }
400  }
401 
402  return patchcols;
403 }
404 
405 
406 //
407 // Strip off alpha channel when not needed
408 //
409 void color4f_to_color3f(float *col3f, const float *col4f, int num) {
410  for (int i=0; i<num; i++) {
411  int i3 = i*3;
412  int i4 = i*4;
413  col3f[i3 ] = col4f[i4 ];
414  col3f[i3 + 1] = col4f[i4 + 1];
415  col3f[i3 + 2] = col4f[i4 + 2];
416  }
417 }
418 
419 
420 //
421 // Draw ColorChecker patch grid using spheres
422 //
424  float *coords, float radius,
425  float *colors, int mat) {
426  SphereArray spheres;
427  spheres.center.resize(numpts);
428  spheres.radius.resize(numpts);
429  spheres.primcolors3f.resize(numpts);
430 
431  float3 *verts = spheres.center.data();
432  float *radii = spheres.radius.data();
433  float3 *cols = spheres.primcolors3f.data();
434 
435  memcpy(verts, coords, numpts * 3 * sizeof(float));
436  color4f_to_color3f((float *)cols, (float *)colors, numpts);
437  for (int i=0; i<numpts; i++)
438  radii[i]=radius;
439 
440  rt->add_spherearray(spheres, mat);
441 }
442 
443 
444 //
445 // Draw a patch grid mesh
446 //
447 void gen_chart(TachyonOptiX *rt, int numpts,
448  float *coords, int *trimesh_indices,
449  float *colors, int mat) {
450  TriangleMesh mesh;
451  mesh.vertices.resize(numpts * 4);
452  mesh.vertcolors3f.resize(numpts * 4);
453  mesh.indices.resize(numpts * 2);
454 
455  float3 *verts = mesh.vertices.data();
456  float3 *cols = mesh.vertcolors3f.data();
457  int3 *indices = mesh.indices.data();
458 
459  memcpy(verts, coords, numpts * 4 * 3 * sizeof(float));
460  color4f_to_color3f((float *)cols, (float *)colors, numpts * 4);
461  memcpy(indices, trimesh_indices, numpts * 2 * 3 * sizeof(int));
462 
463  rt->add_trimesh(mesh, mat);
464 }
465 
466 
467 //
468 // Draw textured quad or triangle mesh
469 //
470 void gen_teximg(TachyonOptiX *rt, int imszx, int imszy,
471  float width, float height, float length, int mat) {
472  float vertex[] = {
473  -100.0f, 4.0f, -100.0f,
474  -100.0f, 4.0f, 100.0f,
475  100.0f, 4.0f, -100.0f,
476  100.0f, 4.0f, 100.0f
477  };
478  float texcoord[] = {
479  0.0f, 1.0f,
480  0.0f, 0.0f,
481  1.0f, 1.0f,
482  1.0f, 0.0f,
483  };
484  int32_t index[] = {
485  0, 1, 2, // triangle-1
486  1, 2, 3 // triangle-2
487  };
488 
489  vertex[ 0] = -width / 2.0f;
490  vertex[ 3] = -width / 2.0f;
491  vertex[ 6] = width / 2.0f;
492  vertex[ 9] = width / 2.0f;
493 
494  vertex[ 1] = height;
495  vertex[ 4] = height;
496  vertex[ 7] = height;
497  vertex[10] = height;
498 
499  vertex[ 2] = -length / 2.0f;
500  vertex[ 5] = length / 2.0f;
501  vertex[ 8] = -length / 2.0f;
502  vertex[11] = length / 2.0f;
503 
504  TriangleMesh mesh;
505  mesh.vertices.resize(4);
506  mesh.tex2d.resize(4);
507  mesh.indices.resize(2);
508 
509  float3 *verts = mesh.vertices.data();
510  float2 *tex2d = mesh.tex2d.data();
511  int3 *indices = mesh.indices.data();
512 
513  memcpy(verts, vertex, 4 * 3 * sizeof(float));
514  memcpy(tex2d, texcoord, 4 * 2 * sizeof(float));
515  memcpy(indices, index, 2 * 3 * sizeof(int));
516 
517  rt->add_trimesh(mesh, mat);
518 }
519 
520 
521 //
522 // Draw a quad or triangle mesh for the floor
523 //
524 void gen_floor(TachyonOptiX *rt, float width, float height, float length, int mat) {
525  float vertex[] = {
526  -100.0f, 4.0f, -100.0f,
527  -100.0f, 4.0f, 100.0f,
528  100.0f, 4.0f, -100.0f,
529  100.0f, 4.0f, 100.0f
530  };
531  float color[] = {
532  1.0f, 1.0f, 1.0f, 1.0f,
533  1.0f, 1.0f, 1.0f, 1.0f,
534  1.0f, 1.0f, 1.0f, 1.0f,
535  1.0f, 1.0f, 1.0f, 1.0f
536  };
537  int index[] = {
538  0, 1, 2, // triangle-1
539  1, 2, 3 // triangle-2
540  };
541 
542  vertex[ 0] = -width / 2.0f;
543  vertex[ 3] = -width / 2.0f;
544  vertex[ 6] = width / 2.0f;
545  vertex[ 9] = width / 2.0f;
546 
547  vertex[ 1] = height;
548  vertex[ 4] = height;
549  vertex[ 7] = height;
550  vertex[10] = height;
551 
552  vertex[ 2] = -length / 2.0f;
553  vertex[ 5] = length / 2.0f;
554  vertex[ 8] = -length / 2.0f;
555  vertex[11] = length / 2.0f;
556 
557  TriangleMesh mesh;
558  mesh.vertices.resize(4);
559  mesh.vertcolors3f.resize(4);
560  mesh.indices.resize(2*3);
561 
562  float3 *verts = mesh.vertices.data();
563  float3 *cols = mesh.vertcolors3f.data();
564  int3 *indices = mesh.indices.data();
565 
566  memcpy(verts, vertex, 4 * 3 * sizeof(float));
567  color4f_to_color3f((float *)cols, (float *)color, 4);
568  memcpy(indices, index, 2 * 3 * sizeof(int));
569 
570  rt->add_trimesh(mesh, mat);
571 }
572 
573 
574 int main(int argc, const char **argv) {
575  // some sane defaults
576  int imgSize[2] = {1024, 1024 }; // W x H
577 // int usequads = 0;
578  int nofloor = 0;
579  int spheres = 1;
580  int drawtex4ub = 0;
581  int ambientocclusion = 1;
582 
583  //
584  // camera defaults
585  //
586 // float cam_pos[] = {0.0f, 10.0f, -7.0f}; // look at origin from -Z
587 // float cam_up[] = {0.0f, -1.0f, 0.0f}; // Y-up
588 // float cam_view[3];
589 
590 // // look at origin
591 // float invlen = 1.0f / sqrtf(cam_pos[0]*cam_pos[0] +
592 // cam_pos[1]*cam_pos[1] +
593 // cam_pos[2]*cam_pos[2]);
594 
595 // // look at origin...
596 // cam_view[0] = -cam_pos[0] * invlen;
597 // cam_view[1] = -cam_pos[1] * invlen;
598 // cam_view[2] = -cam_pos[2] * invlen;
599 
600  //
601  // parse args
602  //
603  if (argc == 1) {
604  printf("Usage help: %s [optional flags]\n", argv[0]);
605  printf(" options: -nofloor: don't draw floor\n");
606  printf(" -nospheres: don't draw spheres over patches\n");
607  printf(" -drawtex4ub: draw patches w/ sRGB uchar4 texture\n");
608 // printf(" -quads: draw surfaces using quads\n");
609  printf(" -ao: add renderer-specific AO lighting\n");
610  printf(" -res XXXX YYYY: override default image res\n");
611 // return -1;
612  }
613 
614  // parse remaining optional parameter flags
615  if (argc > 1) {
616  for (int i=1; i<argc; i++) {
617 #if 0
618  if (!strcmp("-quads", argv[i])) {
619  usequads = 1;
620  printf("Drawing surface using quads.\n");
621  continue;
622  }
623 #endif
624 
625  if (!strcmp("-nofloor", argv[i])) {
626  nofloor = 1;
627  printf("Drawing surface without floor.\n");
628  continue;
629  }
630 
631  if (!strcmp("-drawtex4ub", argv[i])) {
632  drawtex4ub = 1;
633  printf("Drawing surface sRGB unsigned byte texture map.\n");
634  continue;
635  }
636 
637  if (!strcmp("-nospheres", argv[i])) {
638  spheres = 0;
639  printf("Drawing surface without sphere points.\n");
640  continue;
641  }
642 
643  if (!strcmp("-ao", argv[i])) {
644  ambientocclusion = 1;
645  printf("Enabling renderer-specific AO lighting.\n");
646  continue;
647  }
648 
649  if (!strcmp("-res", argv[i])) {
650  if ((argc - i) >= 2) {
651  imgSize[0] = atoi(argv[++i]);
652  imgSize[1] = atoi(argv[++i]);
653  printf("Image resolution set to: %d x %d\n", imgSize[0], imgSize[1]);
654  }
655  continue;
656  }
657 
658  if (!strcmp("-pause", argv[i])) {
659  sleep(10);
660  }
661 
662  printf("Unrecognized flag: '%s'.\n", argv[i]);
663  }
664  }
665 
666 
667  //
668  // Calculate ColorChecker patch grid coordinates and linearized RGBA colors
669  //
670  const int numpts = COLORCHECKER_NUM_PATCHES;
671 
672  // generate chart image / texture map, and write its content as PNG file
673  int imgszx=0, imgszy=0;
674  unsigned char *chartimg4ub = img_gen_colorchecker_4ub(80, 5, colorchecker_srgbi, imgszx, imgszy);
675  printf("Writing %dx%d sRGB reference chart to 'colorchecker-refchart.png'\n",
676  imgszx, imgszy);
677  stbi_write_png("colorchecker-refchart.png", imgszx, imgszy, 4, chartimg4ub, imgszx * sizeof(int));
678 
679  float *coords = calc_colorchecker_gridpts(1.6f, 0.1f, 0.5f - 0.05f);
682 
683  int numtris=0, numindices=0;
684  float *patchverts = calc_colorchecker_patch_verts(1.6f, 0.1f, 0.5f);
685  int *patchindices = calc_colorchecker_patch_indices(numtris, numindices);
686  float *patchcols = calc_colorchecker_patch_colors(colors);
687 
688 
689  PROFILE_PUSH_RANGE("Initialize Tachyon", 0);
690  printf("Initializing TachyonOptiX...");
691 
694  unsigned int devcount = TachyonOptiX::device_count();
695  unsigned int optixversion = TachyonOptiX::optix_version();
696 
697  printf("Found %u OptiX devices\n", devcount);
698  printf("OptiX version used for build: %d.%d.%d (%u)\n",
699  optixversion/10000,
700  (optixversion%10000)/100,
701  (optixversion%100),
702  optixversion);
703 
704  TachyonOptiX *rt = new TachyonOptiX();
706 
707  PROFILE_PUSH_RANGE("Build Scene", 0);
708  //
709  // Build scene
710  //
711 
712  // create and setup camera
714  rt->framebuffer_resize(imgSize[0], imgSize[1]);
715 // rt->set_verbose_mode(TachyonOptiX::RT_VERB_MIN);
716 // rt->set_verbose_mode(TachyonOptiX::RT_VERB_DEBUG);
717  float rtbgcolor[] = { 1.0, 1.0, 1.0 };
718  float rtbggradtopcolor[] = { 0.6, 0.0, 0.0 };
719  float rtbggradbotcolor[] = { 0.0, 0.0, 0.6 };
720 
721  rt->set_bg_color(rtbgcolor);
722  rt->set_bg_color_grad_top(rtbggradtopcolor);
723  rt->set_bg_color_grad_bot(rtbggradbotcolor);
724 
725  float bggradient[] = { 0.0f, 1.0f, 0.0f };
726  rt->set_bg_gradient(bggradient);
727  rt->set_bg_gradient_topval(1.0f);
728  rt->set_bg_gradient_botval(-1.0f);
729 
730 // rt->set_bg_mode(TachyonOptiX::RT_BACKGROUND_TEXTURE_SOLID);
732 // rt->set_bg_mode(TachyonOptiX::RT_BACKGROUND_TEXTURE_SKY_SPHERE);
733 
734  rt->set_aa_samples(64);
735  rt->shadows_enable(1);
736 
737  if (ambientocclusion) {
738  rt->set_ao_samples(16);
739  rt->set_ao_ambient(0.8);
740  rt->set_ao_direct(0.2);
741  rt->set_ao_maxdist(100.2);
742  }
743 
744  rt->camera_dof_enable(0);
745 
746 
747  float lightdir0[] = { -0.5f, 0.5f, -1.0f };
748  float lightcolor0[] = { 1.0f, 1.0f, 1.0f };
749  rt->add_directional_light(lightdir0, lightcolor0);
750 
751  // set camera params
753 // rt->set_camera_type(TachyonOptiX::RT_ORTHOGRAPHIC);
754 
755  float campos[3] = {0.0f, 10.0f, -8.0f};
756 // float camU[3] = {1.0f, 0.0f, 0.0f};
757  float camV[3] = {0.0f, 1.0f, 0.0f};
758 // float camW[3] = {0.0f, 0.0f, 1.0f};
759  float camat[3] = {0.0f, 0.0f, 0.0f};
760 
761  rt->set_camera_pos(campos);
762 // rt->set_camera_ONB(camU, camV, camW);
763  rt->set_camera_lookat(camat, camV);
764 
765  rt->set_camera_zoom(0.5f);
766  rt->set_camera_dof_fnumber(64.0f);
767  rt->set_camera_dof_focal_dist(0.7f);
768  // set stereoscopic display parameters
769  rt->set_camera_stereo_eyesep(0.6f);
771 
772  // set depth cueing parameters
773  float start = 1.0f;
774  float end = 30.0f;
775  float density = 0.33f;
776 // rt->set_cue_mode(TachyonOptiX::RT_FOG_LINEAR, start, end, density);
777 // rt->set_cue_mode(TachyonOptiX::RT_FOG_EXP, start, end, density);
778 // rt->set_cue_mode(TachyonOptiX::RT_FOG_EXP2, start, end, density);
779  rt->set_cue_mode(TachyonOptiX::RT_FOG_NONE, start, end, density);
780 
781  int mat = 0;
782  float ambient = 0.0f;
783  float diffuse = 0.9f;
784  float specular = 0.0f;
785  float shininess = 0.0f;
786  float reflectivity = 0.0f;
787  float opacity = 1.0f;
788  float outline = 0.0f;
789  float outlinewidth = 0.0f;
790  int transmode = 0;
791 
792  rt->add_material(ambient, diffuse, specular, shininess, reflectivity,
793  opacity, outline, outlinewidth, transmode, mat);
795 
797 
798  // XXX Start Nsight Compute Profiles here...
799  PROFILE_START();
800 
801  PROFILE_PUSH_RANGE("Generate Scene", 0);
802  if (spheres) {
803  // Draw spheres for each patch
804  gen_colorchecker_spheres(rt, numpts, coords, 0.33f, colors, mat);
805  }
806 
807  //
808  // Draw either a texture mapped color chart using reference image,
809  // or draw the color patches using quad/triangle meshes
810  //
811  if (drawtex4ub) {
812  int mat2 = 1;
813 
814  if (chartimg4ub != NULL) {
815  rt->add_tex2d_rgba4u(chartimg4ub, imgszx, imgszy,
816  RT_TEX_COLORSPACE_sRGB, mat2);
817 
818  rt->add_material_textured(ambient, diffuse, specular,
819  shininess, reflectivity, opacity,
820  outline, outlinewidth, transmode,
821  mat2, mat2);
822  } else {
823  rt->add_material(ambient, diffuse, specular, shininess, reflectivity,
824  opacity, outline, outlinewidth, transmode, mat2);
825  }
826 
827  gen_teximg(rt, imgszx, imgszy, 11.0f, 0.5f, 7.3333333f, mat2);
828  } else {
829  // Draw a quad or triangle mesh for the patch grid
830  gen_chart(rt, numpts, patchverts, patchindices, patchcols, mat);
831  }
832 
833  if (!nofloor) {
834  // Draw a quad or triangle mesh for the floor
835  gen_floor(rt, 200.0f, -3.0f, 200.0f, mat);
836  }
837 
839 
840  PROFILE_PUSH_RANGE("Render Scene", 0);
841  printf("Rendering frames w/ accumulation buffer...\n");
842  // render 100 accumulated frames
843 // for (int frames = 0; frames < 3; frames++) {
844  rt->render();
845 // }
847 
848  rt->print_raystats_info();
849 
850  PROFILE_PUSH_RANGE("Write Output Image", 0);
851 
852  const char *filename = "colorchecker.png";
853  printf("Writing accumulated frames to '%s'...\n", filename);
854  if (filename != NULL) {
855  rt->framebuffer_get_size(imgSize[0], imgSize[1]);
856  size_t bufsz = imgSize[0] * imgSize[1] * sizeof(int);
857  unsigned char *rgb4u = (unsigned char *) calloc(1, bufsz);
858  rt->framebuffer_download_rgb4u(rgb4u);
859 
860 #if 0
861  if (writealpha) {
862 printf("Writing rgba4u alpha channel output image 2\n");
863  if (write_image_file_rgba4u(filename, rgb4u, imgSize[0], imgSize[1]))
864  printf("Failed to write image '%s'!!\n", filename);
865  } else {
866  if (write_image_file_rgb4u(filename, rgb4u, imgSize[0], imgSize[1]))
867  printf("Failed to write image '%s'!!\n", filename);
868  }
869 #else
871 
872  stbi_write_png(filename, imgSize[0], imgSize[1], 4, rgb4u, imgSize[0] * sizeof(int));
873 #endif
874 
875  free(rgb4u);
876  }
877 
878  delete [] chartimg4ub;
879  delete [] coords;
880  delete [] colors;
881  delete [] patchverts;
882  delete [] patchindices;
883  delete [] patchcols;
884 
885  return 0;
886 }
887 
888 
STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean)
void color4f_to_color3f(float *col3f, const float *col4f, int num)
void set_ao_maxdist(float dist)
set AO maximum occlusion distance
std::vector< float3 PINALLOCS(float3)> center
Definition: TachyonOptiX.h:245
static const int colorchecker_maxval
void gen_chart(TachyonOptiX *rt, int numpts, float *coords, int *trimesh_indices, float *colors, int mat)
void set_bg_gradient_topval(float v)
set background gradient "top" value (view direction dot product)
void img_draw_box_rgba4ub(int bxs, int bys, int bxe, int bye, unsigned char *rgba4ub, int imgxs, int imgys, unsigned char *img)
void set_ao_samples(int cnt)
ambient occlusion (samples > 1 == on)
int add_tex2d_rgba4u(const unsigned char *img, int xres, int yres, int texflags, int userindex)
define image to be used in a texture map
int add_material_textured(float ambient, float diffuse, float specular, float shininess, float reflectivity, float opacity, float outline, float outlinewidth, int transmode, int textureindex, int userindex)
void framebuffer_get_size(int &fbwidth, int &fbheight)
void add_directional_light(const float *dir, const float *color)
float * img_gen_colorchecker_f32(int tilesz, int pad, const int *srgbi, int maxval, int convertsrgbtolinear, int &imgszx, int &imgszy)
int add_material(float ambient, float diffuse, float specular, float shininess, float reflectivity, float opacity, float outline, float outlinewidth, int transmode, int userindex)
add a material with an associated user-provided index
CPU and GPU profiling utility macros/routines.
void img_draw_box_rgbaf32(int bxs, int bys, int bxe, int bye, float *rgbaf32, int imgxs, int imgys, float *img)
#define PROFILE_PUSH_RANGE(name, cid)
Pushes a time range annotation onto the profiler&#39;s trace stack, beginning at the time of submission...
Definition: ProfileHooks.h:275
std::vector< float PINALLOCS(float)> radius
Definition: TachyonOptiX.h:246
void framebuffer_resize(int fbwidth, int fbheight)
static const int colorchecker_srgbi[]
void gen_colorchecker_spheres(TachyonOptiX *rt, int numpts, float *coords, float radius, float *colors, int mat)
void camera_dof_enable(int onoff)
depth of field on/off
static int device_count(void)
static GPU device query
void set_camera_lookat(const float *at, const float *V)
set camera orientation to look "at" a point in space, with a given "up" direction (camera ONB "V" vec...
#define M_PI
Table of sRGB colors that match the classic pre-2014 ColorChecker charts by Gretag-Macbeth / X-Rite /...
void print_raystats_info(void)
report performance statistics
std::vector< float3 PINALLOCS(float3) > vertices
Definition: TachyonOptiX.h:286
float * calc_colorchecker_patch_verts(float tilesz, float pad, float height)
float * calc_colorchecker_patch_colors(const float *colors)
Adobe sRGB (gamma 2.2)
Output timing/perf data only.
Definition: TachyonOptiX.h:649
void set_ao_direct(float aod)
set AO direct lighting rescale factor
void set_bg_gradient(float *vec)
set world "up" direction for background gradient
void add_spherearray(SphereArray &model, int matidx)
conventional perspective
Definition: TachyonOptiX.h:638
std::vector< float3 PINALLOCS(float3) > vertcolors3f
Definition: TachyonOptiX.h:290
#define PROFILE_POP_RANGE()
Pops the innermost time range off of the profiler&#39;s trace stack, at the time of execution.
Definition: ProfileHooks.h:279
void set_camera_zoom(float zoomfactor)
set camera zoom factor
void int_to_normalized_float(const int *srgbi, int maxval, float *srgbf)
std::vector< float2 PINALLOCS(float2) > tex2d
Definition: TachyonOptiX.h:293
void set_camera_dof_fnumber(float n)
set depth of field f/stop number
float * calc_colorchecker_gridpts(float tilesz, float pad, float height)
void HSItoRGB(float h, float s, float i, float &r, float &g, float &b)
void set_bg_color_grad_bot(float *rgb)
set color for "bottom" of background gradient
void set_camera_type(CameraType m)
set the camera projection mode
void framebuffer_download_rgb4u(unsigned char *imgrgb4u)
void set_camera_stereo_convergence_dist(float dist)
set stereo convergence distance
void shadows_enable(int onoff)
enable/disable shadows
#define COLORCHECKER_NUM_PATCHES
void set_bg_color(float *rgb)
set solid background color
void set_ao_ambient(float aoa)
set AO ambient lighting factor
void set_aa_samples(int cnt)
antialiasing (samples > 1 == on)
#define PROFILE_START()
Trigger the beginning of profiler trace capture, for those that support it.
Definition: ProfileHooks.h:253
void set_cue_mode(FogMode mode, float start, float end, float density)
set depth cueing mode and parameters
void set_bg_gradient_botval(float v)
set background gradient "bottom" value (view direction dot product)
STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes)
void set_verbose_mode(TachyonOptiX::Verbosity mode)
programmatically set verbosity
void set_bg_color_grad_top(float *rgb)
set color for "top" of background gradient
__host__ __device__ float length(const float3 &v)
void set_camera_pos(const float *pos)
set the camera position
static unsigned int optix_version(void)
static OptiX version query
void linear_to_srgb(const float *lin, float *srgb)
std::vector< int3 PINALLOCS(int3) > indices
Definition: TachyonOptiX.h:287
int main(int argc, const char **argv)
void set_camera_stereo_eyesep(float eyesep)
set stereo eye separation
int * calc_colorchecker_patch_indices(int &numtris, int &meshindices)
unsigned char * img_gen_colorchecker_4ub(int tilesz, int pad, const int *srgbi, int &imgszx, int &imgszy)
void framebuffer_colorspace(int colspace)
Tachyon ray tracing host side routines and internal APIs that provide the core ray OptiX-based RTX-ac...
float * calc_colorchecker_linear_rgba(const int *srgbi, int maxval)
void gen_teximg(TachyonOptiX *rt, int imszx, int imszy, float width, float height, float length, int mat)
void set_bg_mode(BGMode m)
set background rendering mode
void set_camera_dof_focal_dist(float d)
set depth of field focal plane distance
std::vector< float3 PINALLOCS(float3)> primcolors3f
Definition: TachyonOptiX.h:247
void add_trimesh(TriangleMesh &model, int matidx)
void srgb_to_linear(const float *srgb, float *lin)
Adobe sRGB (gamma 2.2)
void gen_floor(TachyonOptiX *rt, float width, float height, float length, int mat)