15 Mesh make_grid(
const int n= 10 )
21 for(
int x= 0; x < n; x++)
23 float px= float(x) - float(n)/2 + .5f;
28 for(
int z= 0; z < n; z++)
30 float pz= float(z) - float(n)/2 + .5f;
57 camera.vertex(-0.25, -0.25, -0.5);
59 camera.vertex(-0.25, 0.25, -0.5);
61 camera.vertex(0.25, 0.25, -0.5);
63 camera.vertex(0.25, -0.25, -0.5);
65 camera.vertex(-0.25, -0.25, -0.5);
66 camera.vertex(-0.25, 0.25, -0.5);
68 camera.vertex(-0.25, 0.25, -0.5);
69 camera.vertex(0.25, 0.25, -0.5);
71 camera.vertex(0.25, 0.25, -0.5);
72 camera.vertex(0.25, -0.25, -0.5);
74 camera.vertex(0.25, -0.25, -0.5);
75 camera.vertex(-0.25, -0.25, -0.5);
79 camera.vertex(
Point(0, 0, 0));
80 camera.vertex(
Point(0.25, 0, 0));
82 camera.color(
Green());
83 camera.vertex(
Point(0, 0, 0));
84 camera.vertex(
Point(0, 0.25, 0));
87 camera.vertex(
Point(0, 0, 0));
88 camera.vertex(
Point(0, 0, 0.25));
93 Mesh make_image_plane(
const float z= -1,
const int n= 8 )
98 for(
int py= 0; py < n; py++)
99 for(
int px= 0; px < n; px++)
102 float x= 2 * float(px + 0.5) / float(n) - 1;
103 float y= 2 * float(py + 0.5) / float(n) - 1;
111 Mesh make_frustum(
const float znear= -0.5,
const float zfar= 0.95 )
118 frustum.
vertex(-1, -1, znear);
119 frustum.
vertex(-1, 1, znear);
120 frustum.
vertex(-1, 1, znear);
121 frustum.
vertex( 1, 1, znear);
122 frustum.
vertex( 1, 1, znear);
123 frustum.
vertex( 1, -1, znear);
124 frustum.
vertex( 1, -1, znear);
125 frustum.
vertex(-1, -1, znear);
128 frustum.
vertex(-1, -1, zfar);
129 frustum.
vertex(-1, 1, zfar);
130 frustum.
vertex(-1, 1, zfar);
131 frustum.
vertex( 1, 1, zfar);
132 frustum.
vertex( 1, 1, zfar);
133 frustum.
vertex( 1, -1, zfar);
134 frustum.
vertex( 1, -1, zfar);
135 frustum.
vertex(-1, -1, zfar);
138 frustum.
vertex(-1, -1, znear);
139 frustum.
vertex(-1, -1, zfar);
140 frustum.
vertex(-1, 1, znear);
141 frustum.
vertex(-1, 1, zfar);
142 frustum.
vertex( 1, 1, znear);
143 frustum.
vertex( 1, 1, zfar);
144 frustum.
vertex( 1, -1, znear);
145 frustum.
vertex( 1, -1, zfar);
159 Ray(
const Point& _o,
const Vector& _d ) : o(_o), d(_d), tmax(FLT_MAX) {}
160 Ray(
const Point& _o,
const Vector& _d,
const float _tmax ) : o(_o), d(_d), tmax(_tmax) {}
174 Hit( ) : t(FLT_MAX), u(), v(), node_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
175 Hit(
const Ray& ray ) : t(ray.tmax), u(), v(), node_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
177 Hit(
const float _t,
const float _u,
const float _v,
const int _node_id,
const int _mesh_id,
const int _primitive_id,
const int _id ) : t(_t), u(_u), v(_v),
178 node_id(_node_id), mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
180 operator bool ( ) {
return (triangle_id != -1); }
188 BBoxHit() : tmin(FLT_MAX), tmax(-FLT_MAX) {}
189 BBoxHit(
const float _tmin,
const float _tmax ) : tmin(_tmin), tmax(_tmax) {}
191 operator bool( )
const {
return tmin <= tmax; }
200 BBox( ) : pmin(), pmax() {}
202 BBox(
const Point& p ) : pmin(p), pmax(p) {}
203 BBox(
const BBox& box ) : pmin(box.pmin), pmax(box.pmax) {}
204 BBox(
const BBox& a,
const BBox& b ) : pmin(
min(a.pmin, b.pmin)), pmax(
max(a.pmax, b.pmax)) {}
206 BBox& insert(
const Point& p ) { pmin=
min(pmin, p); pmax=
max(pmax, p);
return *
this; }
207 BBox& insert(
const BBox& box ) { pmin=
min(pmin, box.pmin); pmax=
max(pmax, box.pmax);
return *
this; }
209 float centroid(
const int axis )
const {
return (pmin(axis) + pmax(axis)) / 2; }
210 Point centroid( )
const {
return (pmin + pmax) / 2; }
212 BBoxHit intersect(
const Ray& ray,
const Vector& invd,
const float htmax )
const
216 if(ray.d.x < 0) std::swap(rmin.x, rmax.x);
217 if(ray.d.y < 0) std::swap(rmin.y, rmax.y);
218 if(ray.d.z < 0) std::swap(rmin.z, rmax.z);
219 Vector dmin= (rmin - ray.o) * invd;
220 Vector dmax= (rmax - ray.o) * invd;
236 bool internal( )
const {
return right > 0; }
237 int internal_left( )
const { assert(
internal());
return left; }
238 int internal_right( )
const { assert(
internal());
return right; }
240 bool leaf( )
const {
return right < 0; }
241 int leaf_begin( )
const { assert(leaf());
return -left; }
242 int leaf_end( )
const { assert(leaf());
return -right; }
249 assert(node.internal());
263 template <
typename T >
267 int build(
const std::vector<T>& _primitives )
269 primitives= _primitives;
271 nodes.reserve(primitives.size());
274 root= build(0, primitives.size());
279 Hit intersect(
const Ray& ray,
const float htmax )
const
283 Vector invd=
Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
284 intersect_fast(root, ray, invd, hit);
289 Hit intersect(
const Ray& ray )
const {
return intersect(ray, ray.tmax); }
291 bool occluded(
const Ray& ray )
const
293 Vector invd=
Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
294 return occluded(root, ray, invd, 1);
297 bool occluded(
const Point& p,
const Point& q )
const {
Ray ray(p, q);
return occluded(ray); }
299 bool visible(
const Point& p,
const Point& q )
const {
return !occluded(p, q); }
300 bool visible(
const Ray& ray )
const {
return !occluded(ray); }
304 std::vector<Node> nodes;
305 std::vector<T> primitives;
308 int build(
const int begin,
const int end )
313 int index= nodes.size();
322 if(d.x > d.y && d.x > d.z)
330 float cut= cbounds.centroid(axis);
333 T *pm= std::partition(primitives.data() +
begin, primitives.data() +
end,
334 [axis, cut](
const T& primitive )
336 return primitive.bounds().centroid(axis) < cut;
349 int left= build(
begin, m);
352 int right= build(m,
end);
355 int index= nodes.size();
365 bbox.insert(primitives[i].bounds());
373 BBox bbox= primitives[
begin].bounds().centroid();
375 bbox.insert(primitives[i].bounds().centroid());
380 void intersect_fast(
const int index,
const Ray& ray,
const Vector& invd,
Hit& hit )
const
382 const Node& node= nodes[index];
386 if(node.bounds.intersect(ray, invd, hit.t))
388 for(
int i= node.leaf_begin(); i < node.leaf_end(); i++)
389 if(
Hit h= primitives[i].intersect(ray, hit.t))
395 BBoxHit left= nodes[node.internal_left()].bounds.intersect(ray, invd, hit.t);
396 BBoxHit right= nodes[node.internal_right()].bounds.intersect(ray, invd, hit.t);
401 if(left.tmin < right.tmin)
403 intersect_fast(node.internal_left(), ray, invd, hit);
404 intersect_fast(node.internal_right(), ray, invd, hit);
408 intersect_fast(node.internal_right(), ray, invd, hit);
409 intersect_fast(node.internal_left(), ray, invd, hit);
415 intersect_fast(node.internal_left(), ray, invd, hit);
417 intersect_fast(node.internal_right(), ray, invd, hit);
421 bool occluded(
const int index,
const Ray& ray,
const Vector& invd,
const float htmax )
const
423 const Node& node= nodes[index];
424 if(node.bounds.intersect(ray, invd, htmax))
428 for(
int i= node.leaf_begin(); i < node.leaf_end(); i++)
429 if(primitives[i].intersect(ray, htmax))
434 if(occluded(node.internal_left(), ray, invd, htmax) || occluded(node.internal_right(), ray, invd, htmax))
454 Triangle(
const vec3& a,
const vec3& b,
const vec3&
c,
const int _node_id,
const int _mesh_id,
const int _primitive_id,
const int _id ) :
456 node_id(_node_id), mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
465 Hit intersect(
const Ray &ray,
const float htmax )
const
468 float det=
dot(e1, pvec);
470 float inv_det= 1 / det;
473 float u=
dot(tvec, pvec) * inv_det;
474 if(u < 0 || u > 1)
return Hit();
477 float v=
dot(ray.d, qvec) * inv_det;
478 if(v < 0 || u + v > 1)
return Hit();
480 float t=
dot(e2, qvec) * inv_det;
481 if(t < 0 || t > htmax)
return Hit();
483 return Hit(t, u, v, node_id, mesh_id, primitive_id, triangle_id);
489 return box.insert(p+e1).insert(p+e2);
499 assert(hit.triangle_id != -1);
500 return ray.o + hit.t * ray.d;
510 assert(hit.mesh_id != -1);
511 assert(hit.primitive_id != -1);
517 return default_material;
525 return (1 - material.
metallic) * material.
color / float(M_PI);
532 assert(hit.node_id != -1);
533 assert(hit.mesh_id != -1);
534 assert(hit.primitive_id != -1);
535 assert(hit.triangle_id != -1);
541 int a= primitives.indices[hit.triangle_id];
542 int b= primitives.indices[hit.triangle_id+1];
543 int c= primitives.indices[hit.triangle_id+2];
546 assert(primitives.normals.size());
547 Vector na= primitives.normals[a];
548 Vector nb= primitives.normals[b];
549 Vector nc= primitives.normals[c];
553 Vector n= (1 - hit.u - hit.v) * na + hit.u * nb + hit.v * nc;
574 World( ) : t(), b(), n() {}
577 float sign= std::copysign(1.0f, n.z);
578 float a= -1.0f / (sign + n.z);
579 float d= n.x * n.y * a;
580 t=
Vector(1.0f + sign * n.x * n.x * a, sign * d, -sign * n.x);
581 b=
Vector(d, sign + n.y * n.y * a, -n.y);
585 Vector operator( ) (
const Vector& local )
const {
return local.x * t + local.y * b + local.z * n; }
598 std::uniform_real_distribution<float> u01;
599 std::default_random_engine rng;
601 Sampler(
const unsigned _seed ) : u01(), rng(_seed) {}
602 void seed(
const unsigned _seed ) { rng.seed(_seed); }
604 float sample( ) {
return u01(rng); }
605 unsigned sample_binary( ) {
return rng(); }
606 int sample_range(
const int n ) {
return int(sample() * n); }
617 Source(
const Point& _a,
const Point& _b,
const Point& _c,
const Color& _emission ) : a(_a), b(_b), c(_c), n(), emission(_emission), area()
625 Point sample(
const float u1,
const float u2 )
const
630 float r1= std::sqrt(u1);
632 float beta= (1 - u2) * r1;
633 float gamma= u2 * r1;
635 return alpha*a + beta*b +
gamma*c;
642 float offset= t2 - t1;
648 return t1*a + t2*b + (1 - t1 - t2)*c;
652 float pdf(
const Point& p )
const
659 float blinn(
const float roughness )
661 if(roughness < 0.001)
664 return 2 / (roughness*roughness*roughness*roughness) - 2;
677 Brdf(
const Vector& ng,
const Color& color,
const float plastic,
const float n ) : world(ng),
diffuse(color), kd(1 - plastic), ns(
n) {}
681 float cos_theta=
dot(world.n, l);
686 float cos_theta_h=
dot(world.n, h);
692 Color s= (ns + 8) /
float(8*M_PI) * std::pow(cos_theta_h, ns) *
White();
693 return kd * d + (1 - kd) * s;
696 Vector sample(
const float u1,
const float u2,
const float u3,
const Vector& o )
const
703 float phi= float(2*M_PI) * u3;
704 float cos_theta= std::sqrt(u2);
705 float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
707 return world(
Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta));
714 float phi= float(2*M_PI) * u3;
715 float cos_theta= std::pow(u2, 1 /
float(ns +1));
716 assert(cos_theta >= 0);
717 float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
718 Vector h= world(
Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta));
720 return -o + 2 *
dot(h, o) * h;
727 float cos_theta=
dot(world.n, l);
732 float d= cos_theta / float(M_PI);
736 if(
dot(world.n, h) <= 0)
741 float cos_theta_h=
std::max(
float(0),
dot(world.n, h));
742 float s= (ns + 1) /
float(2*M_PI) * std::pow(cos_theta_h, ns) / (4 *
dot(l, h));
746 return kd*d + (1 - kd)*s;
762 const std::vector<Source>& sources;
775 F(
const int w,
const int h,
const float _scale,
const int _samples,
const GLTFScene& _scene,
const BVH& _bvh,
const std::vector<Source>& _sources ) :
776 scene(_scene), bvh(_bvh), sources(_sources),
777 La(w, h), Le(w, h), Li(w, h), Fr(w, h), M2(w, h), Lo(), width(w), height(h), samples(_samples), scale(_scale) {}
781 if(sources.size() == 0)
791 for(
int i= 0; i < samples; i++)
793 int s= rng.sample_range(sources.size());
794 Point q= sources[s].sample(rng.sample(), rng.sample());
799 float cos_theta=
std::max(
float(0),
dot(pbrdf.world.n, l));
802 if(bvh.occluded(p +
float(.001)*pbrdf.world.n, q +
float(.001)*qn))
805 float pdf= sources[s].pdf(q) * 1 / float(sources.size());
806 Color sample= sources[s].emission * fr * G * V * cos_theta / pdf;
808 color= color + sample;
814 M= Mn1 + (sample - Mn1) /
float(n);
815 M2= M2 + (sample - Mn1) * (sample - M);
818 return { color / float(samples), M, M2 / float(n), n };
823 std::random_device hwseed;
828 #pragma omp parallel for
829 for(
int y= 0; y < height; y++)
833 for(
int x= 0; x < width; x++)
837 float u= (x + float(0.5)) /
float(width) * 2 -1;
838 float v= (y + float(0.5)) /
float(height) * 2 -1;
840 float w= 1 - u*u - v*v;
845 d= pbrdf.world(
Vector(u, v, w));
855 Fr(x, y)= scale * fr * cos_theta;
858 Ray ray(p +
float(.001) * pn, d);
859 if(
Hit hit= bvh.intersect(ray))
863 La(x, y)= scale * material.
color * cos_theta;
864 Le(x, y)= scale * material.
emission + material.
color * cos_theta;
867 Li(x, y)= scale * material.
emission + scale * mc.color;
874 Lo= Lo + material.
emission * fr * cos_theta;
882 void plot(
Image& image,
const float px,
const float py,
const Color& color )
884 int x= std::floor(px * image.
width());
885 int y= std::floor(py * image.
height());
888 image(x-1, y)= color;
889 image(x+1, y)= color;
890 image(x, y-1)= color;
891 image(x, y+1)= color;
899 for(
int ti= 0; ti <= 10; ti++)
900 for(
int pi= 0; pi <= 10; pi++)
902 Vector d= pbrdf.sample(1,
float(ti + .5f) / 10,
float(pi + .5f) / 10,
normalize(o - p));
909 plot.vertex(d * fr.max());
917 Image image(camera.width(), camera.height());
919 printf(
"render sun %dx%d...\n", image.width(), image.height());
927 std::random_device hwseed;
928 for(
int py= 0; py < image.height(); py++)
932 for(
int px= 0; px < image.width(); px++)
935 for(
int pa= 0; pa < 64; pa++)
937 float ax= rng.sample();
938 float ay= rng.sample();
944 if(
Hit hit= bvh.intersect(ray))
950 if(
dot(ray.d, pn) > 0)
953 const float epsilon= 0.0001;
954 float V= bvh.visible(
Ray(p+epsilon*pn, sun) ) ? 1 : 0;
956 color= color + diffuse * V * emission * cos_theta;
961 image(px, py)=
Color(color, 1);
970 Image image(camera.width(), camera.height());
972 printf(
"render sky %dx%d...\n", image.width(), image.height());
980 std::random_device hwseed;
981 for(
int py= 0; py < image.height(); py++)
985 for(
int px= 0; px < image.width(); px++)
988 for(
int pa= 0; pa < 64; pa++)
990 float ax= rng.sample();
991 float ay= rng.sample();
997 if(
Hit hit= bvh.intersect(ray))
1003 if(
dot(ray.d, pn) > 0)
1007 color= color + diffuse * emission * (cos_theta+1)/2;
1012 image(px, py)=
Color(color, 1);
1021 float fract(
const float v ) {
return v - std::floor(v); }
1024 Vector fibonacci(
const int i,
const int N,
const float rotation= 0 )
1026 const float ratio= (std::sqrt(5) + 1) / 2;
1028 float phi= float(2 * M_PI) * fract((i + rotation) / ratio);
1029 float cos_theta= 1 - float(2*i +1) / float(N * 2);
1030 float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1032 return Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1038 Image image(camera.width(), camera.height());
1041 const int N= samples;
1043 printf(
"render fibonacci %dx%d %d samples...\n", image.width(), image.height(), samples);
1051 #pragma omp parallel for schedule(dynamic, 1)
1052 for(
int py= 0; py < image.height(); py++)
1054 std::random_device hwseed;
1057 for(
int px= 0; px < image.width(); px++)
1061 for(
int pa= 0; pa < AA; pa++)
1067 float ar= rng.sample();
1074 if(
Hit hit= bvh.intersect(ray))
1080 if(
dot(ray.d, pn) > 0)
1086 for(
int i= 0; i < N; i++)
1088 Vector l= world( fibonacci(i, N, 0) );
1091 const float epsilon= 0.0001;
1093 if(
Hit source= bvh.intersect(
Ray(p+epsilon*pn, l) ))
1101 color= color + diffuse * Le * cos_theta;
1107 emission= emission / float(AA);
1109 color= color / float(N);
1110 color= color / float(AA);
1113 image(px, py)=
Color(color + emission, 1);
1122 Image render_disco(
const Orbiter& camera,
const GLTFScene& scene,
const BVH& bvh,
const float rotation,
const int samples,
const Color& emission )
1124 Image image(camera.width(), camera.height());
1127 const int N= samples;
1129 printf(
"render disco %dx%d %d samples...\n", image.width(), image.height(), samples);
1137 #pragma omp parallel for schedule(dynamic, 1)
1138 for(
int py= 0; py < image.height(); py++)
1140 std::random_device hwseed;
1143 for(
int px= 0; px < image.width(); px++)
1147 for(
int pa= 0; pa < AA; pa++)
1160 if(
Hit hit= bvh.intersect(ray))
1166 if(
dot(ray.d, pn) > 0)
1172 for(
int i= 0; i < N; i++)
1175 Vector l= world( fibonacci(i, N, rotation) );
1177 const float epsilon= 0.0001;
1178 if(
Hit source= bvh.intersect(
Ray(p+epsilon*pn, l) ))
1186 color= color + diffuse * Le * cos_theta;
1192 emission= emission / float(AA);
1194 color= color / float(N);
1195 color= color / float(AA);
1198 image(px, py)=
Color(color + emission, 1);
1208 Image image(camera.width(), camera.height());
1211 const int N= samples;
1213 printf(
"render uniform %dx%d %d samples...\n", image.width(), image.height(), samples);
1221 #pragma omp parallel for schedule(dynamic, 1)
1222 for(
int py= 0; py < image.height(); py++)
1224 std::random_device hwseed;
1227 for(
int px= 0; px < image.width(); px++)
1231 for(
int pa= 0; pa < AA; pa++)
1243 if(
Hit hit= bvh.intersect(ray))
1249 if(
dot(ray.d, pn) > 0)
1255 for(
int i= 0; i < N; i++)
1260 float cos_theta= rng.sample();
1261 float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
1262 float phi= float(2*M_PI) * rng.sample();
1264 l= world(
Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
1268 const float epsilon= 0.0001;
1269 if(
Hit source= bvh.intersect(
Ray(p+epsilon*pn, l) ))
1277 color= color + diffuse * Le * cos_theta / pdf;
1283 emission= emission / float(AA);
1285 color= color / float(N);
1286 color= color / float(AA);
1288 image(px, py)=
Color(color + emission, 1);
1295 Image render_sources(
const Orbiter& camera,
const GLTFScene& scene,
const BVH& bvh,
const int samples,
const std::vector<Source>& sources )
1297 Image image(camera.width(), camera.height());
1300 const int N= samples;
1302 printf(
"render uniform %dx%d %d samples...\n", image.width(), image.height(), samples);
1306 for(
unsigned i= 0; i < sources.size(); i++)
1307 total+= sources[i].area;
1315 #pragma omp parallel for schedule(dynamic, 1)
1316 for(
int py= 0; py < image.height(); py++)
1318 std::random_device hwseed;
1321 for(
int px= 0; px < image.width(); px++)
1325 for(
int pa= 0; pa < AA; pa++)
1337 if(
Hit hit= bvh.intersect(ray))
1343 if(
dot(ray.d, pn) > 0)
1349 for(
int i= 0; i < N; i++)
1357 int s= rng.sample_range(sources.size());
1358 q= sources[s].sample(rng.sample(), rng.sample());
1359 pdf= float(1)/float(sources.size()) * sources[s].pdf(q);
1377 const float epsilon= 0.0001;
1378 if(
Hit source= bvh.intersect(
Ray(p+epsilon*pn, l) ))
1385 color= color + diffuse * Le * cos_theta * cos_theta_s /
length2(l) / pdf;
1391 emission= emission / float(AA);
1393 color= color / float(N);
1394 color= color / float(AA);
1396 image(px, py)=
Color(color + emission, 1);
1408 TP(
const char *filename ) :
AppCamera(1024, 640, 3,3, 8)
1416 if(m_scene.
meshes.size() == 0)
1419 std::vector<Triangle> triangles;
1420 std::vector<Color> colors;
1422 for(
unsigned node_id= 0; node_id < m_scene.
nodes.size(); node_id++)
1430 for(
unsigned primitive_id= 0; primitive_id < mesh.
primitives.size(); primitive_id++)
1436 if(material_id != -1)
1438 if(m_scene.
materials[material_id].emission.max() > 0)
1441 color= m_scene.
materials[material_id].color;
1444 for(
unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
1447 Point a= model(
Point(primitives.positions[primitives.indices[i]]) );
1448 Point b= model(
Point(primitives.positions[primitives.indices[i+1]]) );
1449 Point c= model(
Point(primitives.positions[primitives.indices[i+2]]) );
1455 colors.push_back(color);
1456 triangles.push_back(
Triangle(a, b, c, node_id, mesh_id, primitive_id, i) );
1462 assert(triangles.size());
1466 m_objet_sources=
Mesh(GL_LINES);
1469 for(
unsigned node_id= 0; node_id < m_scene.
nodes.size(); node_id++)
1475 for(
unsigned primitive_id= 0; primitive_id < mesh.
primitives.size(); primitive_id++)
1483 for(
unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
1486 Point a= model(
Point(primitives.positions[primitives.indices[i]]) );
1487 Point b= model(
Point(primitives.positions[primitives.indices[i+1]]) );
1488 Point c= model(
Point(primitives.positions[primitives.indices[i+2]]) );
1506 m_bvh.build(triangles);
1509 m_objet=
Mesh(GL_TRIANGLES);
1511 for(
unsigned i= 0; i < triangles.size(); i++)
1513 const Triangle& triangle= triangles[i];
1515 m_objet.
color(colors[i]);
1516 m_objet.
vertex(triangle.p);
1517 m_objet.
vertex(triangle.p+triangle.e1);
1518 m_objet.
vertex(triangle.p+triangle.e2);
1524 m_objet.
bounds(pmin, pmax);
1527 m_repere= make_grid(10);
1528 m_hit= make_grid(0);
1531 m_repere.
bounds(gmin, gmax);
1533 pmin=
min(pmin, gmin);
1534 pmax=
max(pmax, gmax);
1538 m_directions.
create(GL_LINES);
1542 m_camera= make_camera();
1543 m_image_plane= make_image_plane(0.9);
1544 m_frustum= make_frustum();
1557 glGenFramebuffers(1, &m_framebuffer);
1558 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);
1559 glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_La_texture, 0);
1560 glReadBuffer(GL_COLOR_ATTACHMENT0);
1562 glGenFramebuffers(1, &m_zoom_framebuffer);
1563 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_zoom_framebuffer);
1564 glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_zoom_texture, 0);
1565 glReadBuffer(GL_COLOR_ATTACHMENT0);
1571 glClearColor(0.2f, 0.2f, 0.2f, 1.f);
1574 glDepthFunc(GL_LESS);
1575 glEnable(GL_DEPTH_TEST);
1579 glEnable(GL_LINE_SMOOTH);
1594 GLuint
update(
const GLuint texture,
const Image& image )
1596 glBindTexture(GL_TEXTURE_2D, texture);
1597 glTexImage2D(GL_TEXTURE_2D, 0,
1599 GL_RGBA, GL_FLOAT, image.
data());
1604 void display(
const GLuint texture,
const int x,
const int y,
const int zoom= 1 )
1606 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);
1607 glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
1609 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_zoom_framebuffer);
1610 glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256*zoom, 256*zoom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1612 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_zoom_framebuffer);
1614 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
1615 glBlitFramebuffer(0, 0, 256*zoom, 256*zoom, x*zoom, y*zoom, x*zoom+256*zoom, y*zoom+256*zoom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1621 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1627 static int export_capture= 0;
1628 static int show_capture= 1;
1629 static int show_frustum= 1;
1630 static int show_brdf= 1;
1633 static int samples= 64;
1634 static int sample_mode= 0;
1635 static int freeze= 0;
1644 m_camera_freeze=
camera();
1651 SDL_GetMouseState(&x, &y);
1674 show_frustum= !show_frustum;
1690 Ray ray( inv(
Point(m_pixelx, m_pixely, 0) ), inv(
Point(m_pixelx, m_pixely, 1) ) );
1691 if(
Hit hit= m_bvh.intersect(ray))
1698 plot_brdf(m_directions, hit_inv(ray.o), hit_inv(p), hit_inv(n), material);
1704 F f(256, 256, m_scale, samples, m_scene, m_bvh, m_sources);
1705 f.sample(ray.o, p, n, material);
1707 if(sample_mode == 1)
1709 std::random_device hwseed;
1715 float gold= (std::sqrt(
float(5)) +1) / 2;
1716 for(
int i= 0; i < samples; i++)
1718 float phi= float(2*M_PI) * ((i+u) / gold - std::floor((i+u) / gold));
1719 float cos_theta= 1 - float(2*i -1) / float(2*samples);
1720 float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1722 Vector d=
Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1725 float x= d.x / 2 + float(0.5);
1726 float y= d.y / 2 + float(0.5);
1727 plot(f.Le, x, y,
Red());
1728 plot(f.Li, x, y,
Red());
1729 plot(f.Fr, x, y,
Red());
1732 else if(sample_mode == 2)
1734 std::random_device hwseed;
1738 for(
int i= 0; i < samples; i++)
1740 float phi= float(2*M_PI) * rng.sample();
1741 float cos_theta= std::sqrt(rng.sample());
1742 float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1744 Vector d=
Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1747 float x= d.x / 2 + float(0.5);
1748 float y= d.y / 2 + float(0.5);
1749 plot(f.Le, x, y,
Red());
1750 plot(f.Li, x, y,
Red());
1751 plot(f.Fr, x, y,
Red());
1754 else if(sample_mode == 4)
1756 if(m_sources.size())
1758 std::random_device hwseed;
1764 for(
unsigned i= 0; i < m_sources.size(); i++)
1765 total+= m_sources[i].area;
1768 for(
int i= 0; i < samples; i++)
1776 float u= rng.sample();
1777 for(
unsigned i= 0; i < m_sources.size(); i++)
1779 cdf+= m_sources[i].area / total;
1782 q= m_sources[i].sample(rng.sample(), rng.sample());
1791 float x= l.x / 2 + float(0.5);
1792 float y= l.y / 2 + float(0.5);
1793 plot(f.Le, x, y,
Red());
1794 plot(f.Fr, x, y,
Red());
1799 else if(sample_mode == 3)
1803 std::random_device hwseed;
1807 for(
int i= 0; i < samples; i++)
1809 Vector l= pbrdf.world.local( pbrdf.sample(rng.sample(), rng.sample(), rng.sample(),
normalize(ray.o - p)) );
1813 float x= l.x / 2 + float(0.5);
1814 float y= l.y / 2 + float(0.5);
1815 plot(f.Fr, x, y,
Red());
1816 plot(f.Li, x, y,
Red());
1817 plot(f.Le, x, y,
Red());
1824 std::random_device hwseed;
1828 for(
int i= 0; i < samples; i++)
1830 float phi= float(2*M_PI) * rng.sample();
1831 float cos_theta= rng.sample();
1832 float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1834 Vector d=
Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1837 float x= d.x / 2 + float(0.5);
1838 float y= d.y / 2 + float(0.5);
1839 plot(f.Fr, x, y,
Red());
1840 plot(f.Le, x, y,
Red());
1841 plot(f.Li, x, y,
Red());
1845 update(m_La_texture, f.La);
1846 update(m_Le_texture, f.Le);
1847 update(m_Li_texture, f.Li);
1848 update(m_Fr_texture, f.Fr);
1852 F f(512, 512, m_scale, samples*4, m_scene, m_bvh, m_sources);
1853 f.sample(ray.o, p, n, material);
1864 Image image= render_sun(m_camera_freeze, m_scene, m_bvh,
Vector(1, 10, -2),
Color(10));
1870 Image image= render_sky(m_camera_freeze, m_scene, m_bvh,
Vector(1, 10, -2),
Color(10));
1878 for(
int n : { 1, 4, 16, 64, 256 })
1880 Image image= render_fibonacci(m_camera_freeze, m_scene, m_bvh, n,
Color(10));
1886 sprintf(tmp,
"fibonacci-%03d.hdr", n);
1897 for(
int r= 0; r < 64; r++)
1899 Image image= render_disco(m_camera_freeze, m_scene, m_bvh,
float(r) /
float(64), 64,
Color(10));
1901 sprintf(tmp,
"disco-%03d.hdr", r);
1911 for(
int n : { 1, 4, 16, 64, 256 })
1913 Image image= render_uniform(m_camera_freeze, m_scene, m_bvh, n,
Color(10));
1915 sprintf(tmp,
"uniform-%03d.hdr", n);
1925 for(
int n : { 1, 4, 16, 64, 256 })
1928 Image image= render_sources(m_camera_freeze, m_scene, m_bvh, n, m_sources);
1931 sprintf(tmp,
"mcdirect_n-%03d.hdr", n);
1951 draw(m_image_plane,
Inverse(m_camera_projection * m_camera_view),
camera());
1952 draw(m_frustum,
Inverse(m_camera_projection * m_camera_view),
camera());
1962 if(mode == 0) display(m_La_texture, 0, 0, zoom+1);
1963 if(mode == 1) display(m_Le_texture, 0, 0, zoom+1);
1964 if(mode == 2) display(m_Li_texture, 0, 0, zoom+1);
1965 if(show_brdf) display(m_Fr_texture, 256, 0, zoom+1);
1973 button(m_widgets,
"show", show_capture);
1974 button(m_widgets,
"show brdf", show_brdf);
1975 button(m_widgets,
"show frustum", show_frustum);
1979 if(
select(m_widgets,
"ambient occlusion", 0, mode))
capture= 1;
1982 if(
value(m_widgets,
"scale", m_scale, 0, 100, 1))
capture= 1;
1985 if(
select(m_widgets,
"fibonacci", 1, sample_mode))
capture= 1;
1986 if(
select(m_widgets,
"uniform", 0, sample_mode))
capture= 1;
1989 if(mode == 1)
if(
select(m_widgets,
"sources", 4, sample_mode))
capture= 1;
1990 if(
value(m_widgets,
"samples", samples, 0, 256, 1))
capture= 1;
2002 std::vector<Source> m_sources;
2004 GLuint m_zoom_framebuffer;
2005 GLuint m_zoom_texture;
2006 GLuint m_framebuffer;
2007 GLuint m_La_texture;
2008 GLuint m_Le_texture;
2009 GLuint m_Li_texture;
2010 GLuint m_Fr_texture;
2013 Mesh m_objet_sources;
2028 int m_pixelx, m_pixely;
2035 int main(
int argc,
char **argv )
2037 const char *filename=
"cornell.gltf";
const Orbiter & camera() const
renvoie l'orbiter gere par l'application.
representation d'une image.
int height() const
renvoie la hauteur de l'image.
const void * data() const
renvoie un pointeur sur le stockage des couleurs des pixels.
int width() const
renvoie la largeur de l'image.
representation d'un objet / maillage.
unsigned int vertex(const vec3 &p)
insere un sommet de position p, et ses attributs (s'ils sont definis par color(), texcoord(),...
void bounds(Point &pmin, Point &pmax) const
renvoie min et max les coordonnees des extremites des positions des sommets de l'objet (boite engloba...
Color default_color() const
renvoie la couleur par defaut du mesh, utilisee si les sommets n'ont pas de couleur associee.
int create(const GLenum primitives)
construit les objets openGL.
GLenum primitives() const
renvoie le type de primitives.
void release()
detruit les objets openGL.
Mesh & color(const vec4 &c)
definit la couleur du prochain sommet.
bool has_position() const
verifie que les attributs sont decrits de maniere coherente.
representation de la camera, type orbiter, placee sur une sphere autour du centre de l'objet.
void lookat(const Point ¢er, const float size)
observe le point center a une distance size.
Transform viewport() const
renvoie la transformation viewport actuelle. doit etre initialise par projection(width,...
Transform projection(const int width, const int height, const float fov)
fixe la projection reglee pour une image d'aspect width / height, et une demi ouverture de fov degres...
Transform view() const
renvoie la transformation vue.
int render()
a deriver pour afficher les objets. renvoie 1 pour continuer, 0 pour fermer l'application.
int quit()
a deriver pour detruire les objets openGL. renvoie -1 pour indiquer une erreur, 0 sinon.
int update(const float time, const float delta)
a deriver et redefinir pour animer les objets en fonction du temps.
int init()
a deriver pour creer les objets openGL. renvoie -1 pour indiquer une erreur, 0 sinon.
int mesh_index
indice du maillage.
int material_index
indice de la matiere des primitives.
GLTFScene read_gltf_scene(const char *filename)
charge un fichier .gltf et construit une scene statique, sans animation.
Transform model
transformation model pour dessiner le maillage.
description d'un maillage.
position et orientation d'un maillage dans la scene.
groupe de triangles d'un maillage. chaque groupe est associe a une matiere.
void begin(Widgets &w)
debut de la description des elements de l'interface graphique.
Widgets create_widgets()
cree une interface graphique. a detruire avec release_widgets( ).
void release_widgets(Widgets &w)
detruit l'interface graphique.
bool button(Widgets &w, const char *text, int &status)
int window_height()
renvoie la hauteur de la fenetre de l'application.
bool value(Widgets &w, const char *label, int &value, const int value_min, const int value_max, const int value_step)
valeur editable par increment.
void clear_key_state(const SDL_Keycode key)
desactive une touche du clavier.
void printf(Text &text, const int px, const int py, const char *format,...)
affiche un texte a la position x, y. meme utilisation que printf().
bool select(Widgets &w, const char *text, const int option, int &status)
void end(Widgets &w)
termine la description des elements de l'interface graphique.
int key_state(const SDL_Keycode key)
renvoie l'etat d'une touche du clavier. cf la doc SDL2 pour les codes.
void begin_line(Widgets &w)
place les prochains elements sur une nouvelle ligne.
int window_width()
renvoie la largeur de la fenetre de l'application.
Color Red()
utilitaire. renvoie une couleur rouge.
Color Yellow()
utilitaire. renvoie une couleur jaune.
Color Blue()
utilitaire. renvoie une couleur bleue.
Color Black()
utilitaire. renvoie une couleur noire.
Color Green()
utilitaire. renvoie une couleur verte.
Color gamma(const Color &color)
correction gamma : rgb vers srgb
Color White()
utilitaire. renvoie une couleur blanche.
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Point Origin()
renvoie le point origine (0, 0, 0)
Transform Rotation(const Vector &axis, const float angle)
renvoie la matrice representation une rotation de angle degree autour de l'axe axis.
Point max(const Point &a, const Point &b)
renvoie la plus grande composante de chaque point. x, y, z= max(a.x, b.x), max(a.y,...
Transform Viewport(const float width, const float height)
renvoie la matrice representant une transformation viewport.
Transform Identity()
construit la transformation identite.
float distance(const Point &a, const Point &b)
renvoie la distance etre 2 points.
Point min(const Point &a, const Point &b)
renvoie la plus petite composante de chaque point. x, y, z= min(a.x, b.x), min(a.y,...
float length2(const Vector &v)
renvoie la carre de la longueur d'un vecteur.
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
float distance2(const Point &a, const Point &b)
renvoie le carre de la distance etre 2 points.
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Transform Translation(const Vector &v)
renvoie la matrice representant une translation par un vecteur.
float length(const Vector &v)
renvoie la longueur d'un vecteur.
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Transform Scale(const float x, const float y, const float z)
renvoie la matrice representant une mise a l'echelle / etirement.
int capture(const char *prefix)
GLuint make_vec4_texture(const int unit, const int width, const int height, const GLenum texel_type)
creation de textures pour stocker des donnees (autres qu'une couleur).
int write_image_pfm(const Image &image, const char *filename)
enregistre une image dans un fichier .pfm.
int write_image_hdr(const Image &image, const char *filename)
enregistre une image dans un fichier .hdr.
void bounds(const MeshData &data, Point &pmin, Point &pmax)
renvoie l'englobant.
intersection avec une boite / un englobant.
bvh parametre par le type des primitives, cf triangle et instance...
regroupe tous les parametres de la matiere du point d'intersection.
Vector n
normale interpolee du point d'intersection
representation d'une couleur (rgba) transparente ou opaque.
float roughness
rugosite de la micro surface.
Color emission
emission pour les sources de lumieres ou pas (= noir).
float metallic
metallic / dielectrique.
std::vector< GLTFMaterial > materials
matieres.
std::vector< GLTFNode > nodes
noeuds / position et orientation des maillages dans la scene.
std::vector< GLTFMesh > meshes
ensemble de maillages.
intersection avec un triangle.
construction de l'arbre / BVH.
representation d'un point 3d.
generation de nombres aleatoires entre 0 et 1.
triangle pour le bvh, cf fonction bounds() et intersect().
representation d'un vecteur 3d.
vecteur generique, utilitaire.
const GLTFMaterial & hit_material(const Hit &hit, const GLTFScene &scene)
renvoie la matiere du point d'intersection.
Vector hit_normal(const Hit &hit, const GLTFScene &scene)
renvoie la normale interpolee au point d'intersection dans le repere de la scene.
Point hit_position(const Hit &hit, const Ray &ray)
renvoie la position du point d'intersection sur le rayon.
Node make_leaf(const BBox &bounds, const int begin, const int end)
creation d'une feuille.
Node make_node(const BBox &bounds, const int left, const int right)
creation d'un noeud interne.