gKit2 light
pipeline3d.cpp
1 
2 #include <cstdio>
3 #include <cmath>
4 
5 #include "vec.h"
6 #include "mat.h"
7 
8 #include "mesh.h"
9 #include "image.h"
10 #include "orbiter.h"
11 
12 #include "wavefront.h"
13 
14 
15 struct ZBuffer
16 {
17  std::vector<float> data;
18  int width;
19  int height;
20 
21  ZBuffer( const int w, const int h )
22  {
23  width= w;
24  height= h;
25  data.assign(w * h, 1);
26  }
27 
28  void clear( const float value= 1 )
29  {
30  data.assign(width * height, value);
31  }
32 
33  float& operator() ( const int x, const int y )
34  {
35  const unsigned long int offset= y * width + x;
36  return data[offset];
37  }
38 };
39 
40 
41 struct Fragment
42 {
43  float x, y, z; // coordonnees espace image
44  float u, v, w; // coordonnees barycentriques du fragment dans le triangle abc, p(u, v, w) = u * c + v * a + w * b;
45 };
46 
47 
48 // interface
49 struct Pipeline
50 {
51  Pipeline( ) {}
52  virtual ~Pipeline( ) {}
53 
54  // vertex shader, doit renvoyer les coordonnees du sommet dans le repere projectif
55  virtual Point vertex_shader( const int vertex_id ) const = 0;
56 
57  // fragment shader, doit renvoyer la couleur du fragment de la primitive
58  // doit interpoler lui meme les "varyings", fragment.uvw definissent les coefficients.
59  virtual Color fragment_shader( const int primitive_id, const Fragment fragment ) const = 0;
60  // pour simplifier le code, les varyings n'existent pas dans cette version,
61  // il faut recuperer les infos des sommets de la primitive et faire l'interpolation.
62  // remarque : les gpu amd gcn fonctionnent comme ca...
63 };
64 
65 // pipeline simple
66 struct BasicPipeline : public Pipeline
67 {
68  const Mesh& mesh;
69  Transform model;
70  Transform view;
71  Transform projection;
72  Transform mvp;
73  Transform mv;
74  Transform mvn;
75 
76  BasicPipeline( const Mesh& _mesh, const Transform& _model, const Transform& _view, const Transform& _projection )
77  : Pipeline(), mesh(_mesh), model(_model), view(_view), projection(_projection)
78  {
79  mvp= projection * view * model;
80  mv= view * model;
81  mvn= make_normal_transform(mv);
82  }
83 
84  Point vertex_shader( const int vertex_id ) const
85  {
86  // recupere la position du sommet
87  Point p= make_point( mesh.positions[vertex_id] );
88  //~ // renvoie les coordonnees dans le repere projectif
89  //~ return transform(mvp, p);
90 
91  // renvoie les coordonnees dans le repere camera
92  return transform(mv, p);
93  }
94 
95  Color fragment_shader( const int primitive_id, const Fragment fragment ) const
96  {
97  // recuperer les normales des sommets de la primitive
98  Vector a= transform(mvn, make_vector( mesh.normals[primitive_id * 3] ));
99  Vector b= transform(mvn, make_vector( mesh.normals[primitive_id * 3 +1] ));
100  Vector c= transform(mvn, make_vector( mesh.normals[primitive_id * 3 +2] ));
101 
102  // interpoler la normale
103  Vector n= fragment.u * c + fragment.v * a + fragment.w * b;
104  // et la normaliser, l'interpolation ne conserve pas la longueur des vecteurs
105  n= normalize(n);
106 
107  // calcule une couleur qui depend de l'orientation de la primitive par rapport a la camera
108  Color color= make_color(1.0 - (primitive_id % 100) / 99.f, float(primitive_id % 10) / 9.f, float(primitive_id % 1000) / 999.f);
109  return color * std::abs(n.z);
110 
111  // on peut faire autre chose, par exemple, afficher la normale...
112  // return make_color(std::abs(n.x), std::abs(n.y), std::abs(n.z));
113  }
114 };
115 
116 
118 float area( const Point p, const Point a, const Point b )
119 {
120  Vector pa= make_vector(p, a); pa.z= 0;
121  Vector pb= make_vector(p, b); pb.z= 0;
122  return cross(pa, pb).z;
123 }
124 
126 float volume( const Point p, const Point a, const Point b, const Point c )
127 {
128  Vector ab= make_vector(a, b);
129  Vector ac= make_vector(a, c);
130  Vector ap= make_vector(a, p);
131 
132  return dot(ap, cross(ab, ac));
133 }
134 
135 // juste pour linker avec window.cpp
136 int draw( ) { return 0; }
137 
138 
139 int main( int argc, char **argv )
140 {
141  //~ Image color= create_image(1024, 640, 3, make_color(0.2, 0.2, 0.2));
142  Image color(512, 320, Color(0.2, 0.2, 0.2));
143  ZBuffer depth= ZBuffer(color.width, color.height);
144 
145  Mesh mesh= read_mesh("data/bigguy.obj");
146  //~ Mesh mesh= read_mesh("data/cube.obj");
147  if(mesh == Mesh::error())
148  return 1;
149  printf(" %u positions\n", (unsigned int) mesh.positions.size());
150  printf(" %u indices\n", (unsigned int) mesh.indices.size());
151 
152  // regle le point de vue de la camera pour observer l'objet
153  Point pmin, pmax;
154  mesh_bounds(mesh, pmin, pmax);
155  Orbiter camera= make_orbiter_lookat(center(pmin, pmax), distance(pmin, pmax));
156  //~ Orbiter camera= make_orbiter_lookat(center(pmin, pmax), 3);
157 
158  Transform model= make_identity();
159  Transform view= orbiter_view_transform(camera);
160  Transform projection= orbiter_projection_transform(camera, color.width(), color.height(), 45);
161  Transform viewport= make_viewport(color.width(), color.height());
162 
163  BasicPipeline pipeline(mesh, model, view, projection);
164 
165  // prepare fragmentation 3d, repere camera
166  Transform t= viewport * projection; // passage camera vers image
167  Transform tinv= make_inverse(t); // l'inverse, passage image vers camera
168 
169  Point d0= transform(tinv, make_point(0, 0, 0)); // origine du plan image, repere camera
170  Point d1= transform(tinv, make_point(1, 0, 0)); // axe x du plan image
171  Point d2= transform(tinv, make_point(0, 1, 0)); // axe y du plan image
172  Vector dx= make_vector(d0, d1);
173  Vector dy= make_vector(d0, d2);
174 
175  // draw(pipeline, mesh.positions.size());
176  for(unsigned int i= 0; i +2 < (unsigned int) mesh.positions.size(); i= i +3)
177  {
178  // transforme les 3 sommets du triangle
179  Point a= pipeline.vertex_shader(i);
180  Point b= pipeline.vertex_shader(i+1);
181  Point c= pipeline.vertex_shader(i+2);
182 
183  // visibilite
184  //~ if(visible(a, b, c) == false)
185  //~ continue;
186 
187  // passage dans le repere image
188  //~ a= transform(viewport, a);
189  //~ b= transform(viewport, b);
190  //~ c= transform(viewport, c);
191 
192  // question: comment ne pas dessiner le triangle s'il est mal oriente ?
193 
194  // aire du triangle abc
195  //~ float n= area(a, b, c);
196  float n= volume(make_point(0, 0, 0), a, b, c);
197  if(n < 0)
198  continue;
199 
200  //~ // determine si le pixel est a l'interieur du triangle
201  //~ Edge ab(a ,b);
202  //~ Edge bc(b, c);
203  //~ Edge ca(c, a);
204  Point e= make_point(0, 0, 0);
205  //~ Vector face_aeb == cross(make_vector(a, e), make_vector(a, b));
206  //~ Vector face_bec == cross(make_vector(b, e), make_vector(b, c));
207  //~ Vector face_cea == cross(make_vector(c, e), make_vector(c, a));
208  //~ // rotation circulaire
209  //~ Vector face_aeb == eba == cross(make_vector(e, b), make_vector(e, a));
210  //~ Vector face_bec == ecb == cross(make_vector(e, c), make_vector(e, b));
211  //~ Vector face_cea == eac == cross(make_vector(e, a), make_vector(e, c));
212  Vector face_eba= cross(make_vector(b), make_vector(a));
213  Vector face_ecb= cross(make_vector(c), make_vector(b));
214  Vector face_eac= cross(make_vector(a), make_vector(c));
215 
216  // dessiner le triangle
217  // solution naive, parcours tous les pixels de l'image
218  // question : comment eviter de tester tous les pixels ?
219  // indice : il est sans doute possible de determiner que le triangle ne touche pas un bloc de pixels en ne testant que les coins...
220  for(int y= 0; y < color.height(); y++)
221  for(int x= 0; x < color.width(); x++)
222  {
223  // fragment
224  Fragment frag;
225 
226  //~ frag.u= area(make_point(x, y, 0), a, b); // distance c / ab
227  //~ frag.v= area(make_point(x, y, 0), b, c); // distance a / bc
228  //~ frag.w= area(make_point(x, y, 0), c, a); // distance b / ca
229  //~ frag.u= ab.eval(x, y); // distance c / ab
230  //~ frag.v= bc.eval(x, y); // distance a / bc
231  //~ frag.w= ca.eval(x, y); // distance b / ca
232 
233  Point p= d0 + x*dx + y*dy;
234  //~ frag.u= volume(p, a, e, b); // volume p / face aeb == dot(ap, cross(ae, ab))
235  //~ frag.v= volume(p, b, e, c); // volume p / face bec == dot(bp, cross(be, bc))
236  //~ frag.w= volume(p, c, e, a); // volume p / face cea == dot(cp, cross(ce, ca))
237  //~ // permutation circulaire, ne change pas l'orientation
238  frag.u= dot(make_vector(p), face_eba); // volume p / face eba == dot(ep, cross(eb, ea))
239  frag.v= dot(make_vector(p), face_ecb); // volume p / face ecb == dot(ep, cross(ec, eb))
240  frag.w= dot(make_vector(p), face_eac); // volume p / face eac == dot(ep, cross(ea, ec))
241  // ww= volume(p, a, b, c);
242  if(frag.u > 0 && frag.v > 0 && frag.w > 0)
243  {
244  // t tel que ww == 0, p est sur le plan du triangle abc
245  float t= dot(make_vector(a), cross(make_vector(a, b), make_vector(a, c))) / dot(make_vector(p), cross(make_vector(a, b), make_vector(a, c)));
246  frag.u= dot(t * make_vector(p), face_eba);
247  frag.v= dot(t * make_vector(p), face_ecb);
248  frag.w= dot(t * make_vector(p), face_eac);
249 
250  // normalise les coordonnees barycentriques du fragment
251  float nabc= area(a, b, c);
252  frag.u= frag.u / nabc;
253  frag.v= frag.v / nabc;
254  frag.w= frag.w / nabc;
255 
256  // interpole z
257  frag.x= x;
258  frag.y= y;
259  frag.z= frag.u * c.z + frag.v * a.z + frag.w * b.z;
260 
261  // evalue la couleur du fragment du triangle
262  Color frag_color= pipeline.fragment_shader(i/3, frag);
263 
264  // ztest
265  if(frag.z < depth(x, y))
266  {
267  color(x, y)= Color(frag_color, 1);
268  depth(x, y)= frag.z;
269  }
270 
271  // question : pour quelle raison le ztest est-il fait apres l'execution du fragment shader ? est-ce obligatoire ?
272  // question : peut on eviter d'executer le fragment shader sur un bloc de pixels entierement a l'interieur par le triangle ?
273  // dans quelles conditions sait-on que le triangle ne sera pas visible pour les pixels d'un bloc ?
274  }
275  }
276  }
277 
278  write_image(color, "render.png");
279  return 0;
280 }
representation d'une image.
Definition: image.h:21
representation d'un objet / maillage.
Definition: mesh.h:112
static Mesh & error()
Definition: mesh.h:348
representation de la camera, type orbiter, placee sur une sphere autour du centre de l'objet.
Definition: orbiter.h:17
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.
Definition: widgets.cpp:191
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
int write_image(const Image &image, const char *filename)
enregistre une image dans un fichier png.
Definition: image_io.cpp:85
Point center(const Point &a, const Point &b)
renvoie le milieu du segment ab.
Definition: vec.cpp:24
float distance(const Point &a, const Point &b)
renvoie la distance etre 2 points.
Definition: vec.cpp:14
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
Definition: vec.cpp:137
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Definition: vec.cpp:123
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Definition: vec.cpp:129
Mesh read_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
Definition: wavefront.cpp:14
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
representation d'un vecteur 3d.
Definition: vec.h:59