gKit2 light
Loading...
Searching...
No Matches
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
18struct 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
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
44struct 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
61struct 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
107float 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
115bool 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
124int 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:121
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
Color White()
utilitaire. renvoie une couleur blanche.
Definition color.cpp:23
bool write_image(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .png
Definition image_io.cpp:245
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:167
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
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:67