gKit2 light
Loading...
Searching...
No Matches
tuto_englobant.cpp
Go to the documentation of this file.
1
3
4#include <algorithm>
5#include <vector>
6#include <cfloat>
7#include <chrono>
8
9#include "vec.h"
10#include "mat.h"
11#include "color.h"
12#include "image.h"
13#include "image_io.h"
14#include "image_hdr.h"
15#include "orbiter.h"
16#include "mesh.h"
17#include "wavefront.h"
18
19
20struct RayHit
21{
22 Point o; // origine
23 float t; // p(t)= o + td, position du point d'intersection sur le rayon
24 Vector d; // direction
25 int triangle_id; // indice du triangle dans le mesh
26 float u, v;
27 int x, y;
28
29 RayHit( const Point& _o, const Point& _e ) : o(_o), t(1), d(Vector(_o, _e)), triangle_id(-1), u(), v(), x(), y() {}
30 RayHit( const Point& _o, const Point& _e, const int _x, const int _y ) : o(_o), t(1), d(Vector(_o, _e)), triangle_id(-1), u(), v(), x(_x), y(_y) {}
31 operator bool ( ) { return (triangle_id != -1); }
32};
33
34
35struct BBox
36{
37 Point pmin, pmax;
38
39 BBox( ) : pmin(), pmax() {}
40
41 BBox( const Point& p ) : pmin(p), pmax(p) {}
42 BBox( const BBox& box ) : pmin(box.pmin), pmax(box.pmax) {}
43
44 BBox& insert( const Point& p ) { pmin= min(pmin, p); pmax= max(pmax, p); return *this; }
45 BBox& insert( const BBox& box ) { pmin= min(pmin, box.pmin); pmax= max(pmax, box.pmax); return *this; }
46
47 float centroid( const int axis ) const { return (pmin(axis) + pmax(axis)) / 2; }
48
49 bool intersect( const RayHit& ray ) const
50 {
51 Vector invd= Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
52 return intersect(ray, invd);
53 }
54
55 bool intersect( const RayHit& ray, const Vector& invd ) const
56 {
57 Point rmin= pmin;
58 Point rmax= pmax;
59 if(ray.d.x < 0) std::swap(rmin.x, rmax.x);
60 if(ray.d.y < 0) std::swap(rmin.y, rmax.y);
61 if(ray.d.z < 0) std::swap(rmin.z, rmax.z);
62 Vector dmin= (rmin - ray.o) * invd;
63 Vector dmax= (rmax - ray.o) * invd;
64
65 float tmin= std::max(dmin.z, std::max(dmin.y, std::max(dmin.x, 0.f)));
66 float tmax= std::min(dmax.z, std::min(dmax.y, std::min(dmax.x, ray.t)));
67 return (tmin <= tmax);
68 }
69};
70
71
72struct Triangle
73{
74 Point p; // sommet a du triangle
75 Vector e1, e2; // aretes ab, ac du triangle
76 int id;
77
78 Triangle( const TriangleData& data, const int _id ) : p(data.a), e1(Vector(data.a, data.b)), e2(Vector(data.a, data.c)), id(_id) {}
79
80 /* calcule l'intersection ray/triangle
81 cf "fast, minimum storage ray-triangle intersection"
82
83 renvoie faux s'il n'y a pas d'intersection valide (une intersection peut exister mais peut ne pas se trouver dans l'intervalle [0 tmax] du rayon.)
84 renvoie vrai + les coordonnees barycentriques (u, v) du point d'intersection + sa position le long du rayon (t).
85 convention barycentrique : p(u, v)= (1 - u - v) * a + u * b + v * c
86 */
87 void intersect( RayHit &ray ) const
88 {
89 Vector pvec= cross(ray.d, e2);
90 float det= dot(e1, pvec);
91
92 float inv_det= 1 / det;
93 Vector tvec(p, ray.o);
94
95 float u= dot(tvec, pvec) * inv_det;
96 if(u < 0 || u > 1) return;
97
98 Vector qvec= cross(tvec, e1);
99 float v= dot(ray.d, qvec) * inv_det;
100 if(v < 0 || u + v > 1) return;
101
102 float t= dot(e2, qvec) * inv_det;
103 if(t < 0 || t > ray.t) return;
104
105 // touche !!
106 ray.t= t;
107 ray.triangle_id= id;
108 ray.u= u;
109 ray.v= v;
110 }
111
112 BBox bounds( ) const
113 {
114 BBox box(p);
115 return box.insert(p+e1).insert(p+e2);
116 }
117};
118
119
120
121void direct(
122 const std::vector<Triangle>& triangles, const int tbegin, const int tend,
123 std::vector<RayHit>& rays, const int rbegin, const int rend )
124{
125 for(int i= rbegin; i < rend; i++)
126 for(int k= tbegin; k < tend; k++)
127 triangles[k].intersect(rays[i]);
128}
129
130struct triangle_less1
131{
132 int axis;
133 float cut;
134
135 triangle_less1( const int _axis, const float _cut ) : axis(_axis), cut(_cut) {}
136
137 bool operator() ( const Triangle& triangle ) const
138 {
139 // re-construit l'englobant du triangle
140 BBox bounds= triangle.bounds();
141 return bounds.centroid(axis) < cut;
142 }
143};
144
145struct ray_less1
146{
147 BBox bounds;
148
149 ray_less1( const BBox& _bounds ) : bounds(_bounds) {}
150
151 bool operator() ( const RayHit& ray ) const
152 {
153 return bounds.intersect(ray);
154 }
155};
156
157
158void divide( const BBox& bounds,
159 std::vector<Triangle>& triangles, const int tbegin, const int tend,
160 std::vector<RayHit>& rays, const int rbegin, const int rend )
161{
162 if(tbegin == tend || rbegin == rend)
163 // plus de triangles ou de rayons, rien a faire...
164 return;
165
166 // il ne reste plus que quelques triangles, finir les calculs d'intersection...
167 if(tend - tbegin <= 4)
168 {
169 direct(triangles, tbegin, tend, rays, rbegin, rend);
170 return;
171 }
172
173 // axe le plus etire de l'englobant
174 Vector d= Vector(bounds.pmin, bounds.pmax);
175 int axis;
176 if(d.x > d.y && d.x > d.z) // x plus grand que y et z ?
177 axis= 0;
178 else if(d.y > d.z) // y plus grand que z ? (et que x implicitement)
179 axis= 1;
180 else // x et y ne sont pas les plus grands...
181 axis= 2;
182
183 // coupe l'englobant au milieu
184 float cut= bounds.centroid(axis);
185
186 // repartit les triangles
187 Triangle *pm= std::partition(triangles.data() + tbegin, triangles.data() + tend, triangle_less1(axis, cut));
188 int m= std::distance(triangles.data(), pm);
189
190 // la repartition des triangles peut echouer, et tous les triangles sont dans la meme partie...
191 // forcer quand meme un decoupage en 2 ensembles
192 if(m == tbegin || m == tend)
193 m= (tbegin + tend) / 2;
194 assert(m != tbegin);
195 assert(m != tend);
196
197 // construit les englobants
198 BBox left= triangles[tbegin].bounds();
199 for(int i= tbegin+1; i < m; i++)
200 left.insert(triangles[i].bounds());
201
202 // repartit les rayons
203 RayHit *prleft= std::partition(rays.data() + rbegin, rays.data() + rend, ray_less1(left));
204 int rleft= std::distance(rays.data(), prleft);
205
206 divide(left, triangles, tbegin, m, rays, rbegin, rleft);
207
208 // on recommence pour la droite
209 BBox right= triangles[m].bounds();
210 for(int i= m+1; i < tend; i++)
211 right.insert(triangles[i].bounds());
212
213 RayHit *prright= std::partition(rays.data() + rbegin, rays.data() + rend, ray_less1(right));
214 int rright= std::distance(rays.data(), prright);
215
216 divide(right, triangles, m, tend, rays, rbegin, rright);
217}
218
219
220int main( const int argc, const char **argv )
221{
222 const char *mesh_filename= "data/cornell.obj";
223 if(argc > 1)
224 mesh_filename= argv[1];
225
226 const char *orbiter_filename= "data/cornell_orbiter.txt";
227 if(argc > 2)
228 orbiter_filename= argv[2];
229
230 Orbiter camera;
231 if(camera.read_orbiter(orbiter_filename) < 0)
232 return 1;
233
234 Mesh mesh= read_mesh(mesh_filename);
235 BBox bounds;
236 mesh.bounds(bounds.pmin, bounds.pmax);
237
238 // recupere les triangles
239 std::vector<Triangle> triangles;
240 {
241 int n= mesh.triangle_count();
242 for(int i= 0; i < n; i++)
243 triangles.emplace_back(mesh.triangle(i), i);
244 }
245
246 Image image(1024, 768);
247
248 // recupere les transformations
249 camera.projection(image.width(), image.height(), 45);
250 Transform model= Identity();
251 Transform view= camera.view();
252 Transform projection= camera.projection();
253 Transform viewport= camera.viewport();
254 Transform inv= Inverse(viewport * projection * view * model);
255
256 // genere un rayon par pixel de l'image
257 std::vector<RayHit> rays;
258 for(unsigned y= 0; y < image.height(); y++)
259 for(unsigned x= 0; x < image.width(); x++)
260 {
261 // generer le rayon
262 Point origine= inv(Point(x + .5f, y + .5f, 0));
263 Point extremite= inv(Point(x + .5f, y + .5f, 1));
264
265 rays.emplace_back(origine, extremite, x, y);
266 }
267
268// mesure les temps d'execution
269#if 0
270 {
271 auto start= std::chrono::high_resolution_clock::now();
272 direct(triangles, 0, int(triangles.size()), rays, 0, int(rays.size()));
273
274 auto stop= std::chrono::high_resolution_clock::now();
275 int cpu= std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
276 printf("direct %dms\n", cpu);
277 }
278#endif
279
280 {
281 auto start= std::chrono::high_resolution_clock::now();
282 divide(bounds, triangles, 0, int(triangles.size()), rays, 0, int(rays.size()));
283
284 auto stop= std::chrono::high_resolution_clock::now();
285 int cpu= std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
286 printf("divide %dms\n", cpu);
287 }
288
289 // reconstruit l'image
290 for(unsigned i= 0; i < rays.size(); i++)
291 {
292 if(rays[i])
293 {
294 int x= rays[i].x;
295 int y= rays[i].y;
296 float u= rays[i].u;
297 float v= rays[i].v;
298 float w= 1 - u - v;
299 image(x, y)= Color(w, u, v);
300 }
301 }
302 write_image(image, "divide.png");
303
304
305 return 0;
306}
representation d'une image.
Definition image.h:21
representation d'un objet / maillage.
Definition mesh.h:121
void bounds(Point &pmin, Point &pmax) const
renvoie min et max les coordonnees des extremites des positions des sommets de l'objet (boite engloba...
Definition mesh.cpp:489
representation de la camera, type orbiter, placee sur une sphere autour du centre de l'objet.
Definition orbiter.h:17
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().
Definition text.cpp:140
bool write_image(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .png
Definition image_io.cpp:225
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Definition mat.cpp:197
Point max(const Point &a, const Point &b)
renvoie la plus grande composante de chaque point { max(a.x, b.x), max(a.y, b.y), max(a....
Definition vec.cpp:35
Transform Identity()
construit la transformation identite.
Definition mat.cpp:187
Point min(const Point &a, const Point &b)
renvoie la plus petite composante de chaque point { min(a.x, b.x), min(a.y, b.y), min(a....
Definition vec.cpp:30
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
Definition vec.cpp:181
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Definition vec.cpp:173
Mesh read_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
Definition wavefront.cpp:14
void bounds(const MeshData &data, Point &pmin, Point &pmax)
renvoie l'englobant.
boite englobante.
Definition tuto_bvh.cpp:47
representation d'une couleur (rgba) transparente ou opaque.
Definition color.h:14
representation d'un point 3d.
Definition vec.h:21
representation d'une transformation, une matrice 4x4, organisee par ligne / row major.
Definition mat.h:21
vec3 c
positions
Definition mesh.h:96
triangle pour le bvh, cf fonction bounds() et intersect().
Definition tuto_bvh.cpp:84
representation d'un vecteur 3d.
Definition vec.h:67