gKit2 light

- Main Page
- Related Pages
- Modules
- Classes
- Files

- File List
- File Members

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