gKit2 light
Loading...
Searching...
No Matches
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
15struct 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
41struct 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
49struct 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
66struct 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
118float 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
126float 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
136int draw( ) { return 0; }
137
138
139int 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: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
bool write_image(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .png
Definition image_io.cpp:245
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:181
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