gKit2 light
pipeline.cpp
Go to the documentation of this file.
1 
3 
4 #include <cstdio>
5 #include <cmath>
6 
7 #include "vec.h"
8 #include "mat.h"
9 
10 #include "mesh.h"
11 #include "image.h"
12 #include "image_io.h"
13 #include "orbiter.h"
14 
15 #include "wavefront.h"
16 
17 
18 struct ZBuffer
19 {
20  std::vector<float> data;
21  int width;
22  int height;
23 
24  ZBuffer( const int w, const int h, const float z= 1 ) : data(w*h, z), width(w), height(h) {}
25 
26  void clear( const float value= 1 ) { data.assign(width * height, value); }
27 
28  float& operator() ( const int x, const int y )
29  {
30  std::size_t offset= y * width + x;
31  return data[offset];
32  }
33 };
34 
35 
36 struct Fragment
37 {
38  float x, y, z; // coordonnees espace image
39  float u, v, w; // coordonnees barycentriques du fragment dans le triangle abc, p(u, v, w) = u * c + v * a + w * b;
40 };
41 
42 
43 // interface
44 struct Pipeline
45 {
46  Pipeline( ) {}
47  virtual ~Pipeline( ) {}
48 
49  // vertex shader, doit renvoyer les coordonnees du sommet dans le repere projectif
50  virtual Point vertex_shader( const int vertex_id ) const = 0;
51 
52  // fragment shader, doit renvoyer la couleur du fragment de la primitive
53  // doit interpoler lui meme les "varyings", fragment.uvw definissent les coefficients.
54  virtual Color fragment_shader( const int primitive_id, const Fragment fragment ) const = 0;
55  // pour simplifier le code, les varyings n'existent pas dans cette version,
56  // il faut recuperer les infos des sommets de la primitive et faire l'interpolation.
57  // remarque : les gpu amd gcn fonctionnent comme ca...
58 };
59 
60 // pipeline simple
61 struct BasicPipeline : public Pipeline
62 {
63  const Mesh& mesh;
64  Transform model;
65  Transform view;
66  Transform projection;
67  Transform mvp;
68  Transform mv;
69 
70  BasicPipeline( const Mesh& _mesh, const Transform& _model, const Transform& _view, const Transform& _projection )
71  : Pipeline(), mesh(_mesh), model(_model), view(_view), projection(_projection)
72  {
73  mvp= projection * view * model;
74  mv= Normal(view * model);
75  }
76 
77  Point vertex_shader( const int vertex_id ) const
78  {
79  // recupere la position du sommet
80  Point p= Point( mesh.positions().at(vertex_id) );
81  // renvoie les coordonnees dans le repere projectif
82  return mvp(p);
83  }
84 
85  Color fragment_shader( const int primitive_id, const Fragment fragment ) const
86  {
87  // recuperer les normales des sommets de la primitive
88  Vector a= mv( Vector( mesh.normals().at(primitive_id * 3) ));
89  Vector b= mv( Vector( mesh.normals().at(primitive_id * 3 +1) ));
90  Vector c= mv( Vector( mesh.normals().at(primitive_id * 3 +2) ));
91 
92  // interpoler la normale
93  Vector n= fragment.u * c + fragment.v * a + fragment.w * b;
94  // et la normaliser, l'interpolation ne conserve pas la longueur des vecteurs
95  n= normalize(n);
96 
97  // calcule une couleur qui depend de l'orientation de la primitive par rapport a la camera
98  return White() * std::abs(n.z);
99 
100  // on peut faire autre chose, par exemple, afficher directement la normale...
101  // return Color(std::abs(n.x), std::abs(n.y), std::abs(n.z));
102  }
103 };
104 
105 
106 // cf http://geomalgorithms.com/a01-_area.html, section modern triangles
107 float area( const Point p, const Point a, const Point b )
108 {
109  Vector pa= Vector(p, a); pa.z= 0;
110  Vector pb= Vector(p, b); pb.z= 0;
111  return cross(pa, pb).z;
112 }
113 
114 
115 bool visible( const Point p )
116 {
117  if(p.x < -1 || p.x > 1) return false;
118  if(p.y < -1 || p.y > 1) return false;
119  if(p.z < -1 || p.z > 1) return false;
120  return true;
121 }
122 
123 
124 int main( int argc, char **argv )
125 {
126  Image color(640, 320);
127  ZBuffer depth(color.width(), color.height());
128 
129  Mesh mesh= read_mesh("data/bigguy.obj");
130  if(mesh == Mesh::error())
131  return 1;
132  printf(" %d positions\n", mesh.vertex_count());
133  printf(" %d indices\n", mesh.index_count());
134 
135  // regle le point de vue de la camera pour observer l'objet
136  Point pmin, pmax;
137  mesh.bounds(pmin, pmax);
138  Orbiter camera(pmin, pmax);
139 
140  BasicPipeline pipeline(
141  mesh,
142  Identity(),
143  camera.view(),
144  camera.projection(color.width(), color.height(), 45) );
145 
146  Transform viewport= Viewport(color.width(), color.height());
147 
148  // draw(pipeline, mesh.vertex_count());
149  for(unsigned int i= 0; i +2 < (unsigned int) mesh.vertex_count(); i= i +3)
150  {
151  // transforme les 3 sommets du triangle
152  Point a= pipeline.vertex_shader(i);
153  Point b= pipeline.vertex_shader(i+1);
154  Point c= pipeline.vertex_shader(i+2);
155 
156  // visibilite
157  if(visible(a) == false && visible(b) == false && visible(c) == false)
158  continue;
159  // faux dans pas mal de cas...
160  // question : comment faire un test correct ?
161  // indication : si tous les sommets sont du meme cote d'une face de la region observee par la camera, on est sur que le triangle n'est pas visible.
162  // comment definir la region observee par la camera ? quelle est sa forme (dans quel repere) ? les coordonnees de ses sommets ?
163 
164  // passage dans le repere image
165  a= viewport(a);
166  b= viewport(b);
167  c= viewport(c);
168 
169  // question: comment ne pas dessiner le triangle s'il est mal oriente ?
170  // aire du triangle abc
171  float n= area(a, b, c);
172  if(n < 0)
173  continue;
174 
175  // dessiner le triangle
176  // solution naive, parcours tous les pixels de l'image
177  // question : comment eviter de tester tous les pixels ?
178  // indice : il est sans doute possible de determiner que le triangle ne touche pas un bloc de pixels en ne testant que les 4 coins du bloc...
179  // question : dessiner de tout petits triangles est tres long. comment gagner du temps en determinant qu'un triangle est suffisament petit/etire pour "passer" entre les pixels ?
180  // (remarque: oui, ca arrive tout le temps...)
181  for(int y= 0; y < color.height(); y++)
182  for(int x= 0; x < color.width(); x++)
183  {
184  // fragment
185  Fragment frag;
186  frag.u= area(Point(x, y, 0), a, b); // distance c / ab
187  frag.v= area(Point(x, y, 0), b, c); // distance a / bc
188  frag.w= area(Point(x, y, 0), c, a); // distance b / ac
189 
190  if(frag.u > 0 && frag.v > 0 && frag.w > 0)
191  {
192  // normalise les coordonnees barycentriques du fragment
193  frag.u= frag.u / n;
194  frag.v= frag.v / n;
195  frag.w= frag.w / n;
196 
197  frag.x= x;
198  frag.y= y;
199  // interpole z
200  frag.z= frag.u * c.z + frag.v * a.z + frag.w * b.z;
201 
202  // evalue la couleur du fragment du triangle
203  Color frag_color= pipeline.fragment_shader(i/3, frag);
204 
205  // ztest
206  if(frag.z < depth(x, y))
207  {
208  color(x, y)= Color(frag_color, 1);
209  depth(x, y)= frag.z;
210  }
211 
212  // question : pour quelle raison le ztest est-il fait apres l'execution du fragment shader ? est-ce obligatoire ?
213  // question : peut on eviter d'executer le fragment shader sur un bloc de pixels couverts par le triangle ?
214  // dans quelles conditions sait-on qu'il n'y a rien a dessiner dans un bloc de pixels ?
215  // == aucun fragment du triangle appartenant au bloc, ne peut modifier l'image et le zbuffer ?
216  }
217  }
218  }
219 
220  write_image(color, "render.png");
221  return 0;
222 }
representation d'une image.
Definition: image.h:21
representation d'un objet / maillage.
Definition: mesh.h:112
static Mesh & error()
Definition: mesh.h:348
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:503
int vertex_count() const
renvoie le nombre de sommets.
Definition: mesh.h:291
int index_count() const
renvoie le nombre d'indices de sommets.
Definition: mesh.h:293
representation de la camera, type orbiter, placee sur une sphere autour du centre de l'objet.
Definition: orbiter.h:17
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...
Definition: orbiter.cpp:47
Transform view() const
renvoie la transformation vue.
Definition: orbiter.cpp:40
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
Color White()
utilitaire. renvoie une couleur blanche.
Definition: color.cpp:52
Transform Normal(const Transform &m)
renvoie la transformation a appliquer aux normales d'un objet transforme par la matrice m.
Definition: mat.cpp:202
Transform Viewport(const float width, const float height)
renvoie la matrice representant une transformation viewport.
Definition: mat.cpp:357
Transform Identity()
construit la transformation identite.
Definition: mat.cpp:187
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