View difference between Paste ID: rjk1YMCD and Qepzm3NP
SHOW: | | - or go back to the newest paste.
1
#include <math.h>
2
#include <stdlib.h>
3
4
#if defined(__APPLE__)
5
  #include <OpenGL/gl.h>
6
  #include <OpenGL/glu.h>
7
  #include <GLUT/glut.h>
8
#else
9
  #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
10
    #include <windows.h>
11
  #endif
12
  #include <GL/gl.h>
13
  #include <GL/glu.h>
14
  #include <GL/glut.h>
15
#endif
16
17
#ifndef M_PI
18
  #define M_PI 3.14159265359
19
#endif
20
21
template <typename T>
22
T max(T a, T b) {
23
  return a > b ? a : b;
24
}
25
26
template <typename T>
27
T min(T a, T b) {
28
  return a < b ? a : b;
29
}
30
31
struct Vector {
32
  union { float x, r; }; // x és r néven is lehessen hivatkozni erre a tagra.
33
  union { float y, g; };
34
  union { float z, b; };
35
36
  Vector(float v = 0) : x(v), y(v), z(v) { }
37
  Vector(float x, float y, float z) : x(x), y(y), z(z) { }
38
  Vector operator+(const Vector& v) const { return Vector(x + v.x, y + v.y, z + v.z); }
39
  Vector operator-(const Vector& v) const { return Vector(x - v.x, y - v.y, z - v.z); }
40
  Vector operator*(const Vector& v) const { return Vector(x * v.x, y * v.y, z * v.z); }
41
  Vector operator/(const Vector& v) const { return Vector(x / v.x, y / v.y, z / v.z); }
42
  Vector& operator+=(const Vector& v) { x += v.x, y += v.y, z += v.z; return *this; }
43
  Vector& operator-=(const Vector& v) { x -= v.x, y -= v.y, z -= v.z; return *this; }
44
  Vector& operator*=(const Vector& v) { x *= v.x, y *= v.y, z *= v.z; return *this; }
45
  Vector& operator/=(const Vector& v) { x /= v.x, y /= v.y, z /= v.z; return *this; }
46
  Vector operator-() const { return Vector(-x, -y, -z); }
47
  float dot(const Vector& v) const { return x*v.x + y*v.y + z*v.z; }
48
  Vector cross(const Vector& v) const { return Vector(y*v.z - z*v.y, z*v.x - x*v.z, x*v.y - y*v.x); }
49
  float length() const { return sqrt(x*x + y*y + z*z); }
50
  Vector normalize() const { float l = length(); if(l > 1e-3) { return (*this/l); } else { return Vector(); } }
51
  bool isNull() const { return length() < 1e-3; }
52
  Vector saturate() const { return Vector(max(min(x, 1.0f), 0.0f), max(min(y, 1.0f), 0.0f), max(min(z, 1.0f), 0.0f)); }
53
};
54
55
// Azoknak, akik a shader kódokban használt szintakszishoz hozzá vannak szokva (mint pl. én)
56
inline float dot(const Vector& lhs, const Vector& rhs) {
57
  return lhs.dot(rhs);
58
}
59
60
inline Vector cross(const Vector& lhs, const Vector& rhs) {
61
  return lhs.cross(rhs);
62
}
63
64
inline Vector operator*(float f, const Vector& v) {
65
  return v*f;
66
}
67
68
typedef Vector Color;
69
70
struct Screen {
71
  static const int width = 600;
72
  static const int height = 600;
73
  static Color image[width * height];
74
  static void Draw() {
75
    glDrawPixels(width, height, GL_RGB, GL_FLOAT, image);
76
  }
77
  static Color& Pixel(size_t x, size_t y) {
78
    return image[y*width + x];
79
  }
80
};
81
Color Screen::image[width * height]; // A statikus adattagat out-of-line példányosítani kell (kivéve az inteket és enumokat).
82
83
struct Ray {
84
  Vector origin, direction;
85
};
86
87
struct Intersection {
88
  Vector pos, normal;
89
  bool is_valid;
90
  Intersection(Vector pos = Vector(), Vector normal = Vector(), bool is_valid = false) 
91
    : pos(pos), normal(normal), is_valid(is_valid) { }
92
};
93
94
struct Light {
95
  enum LightType {Ambient, Directional} type;
96-
  Vector pos;
96+
  Vector pos, dir;
97
  Color color;
98
};
99
100
struct Material {
101
  virtual ~Material() { }
102
  virtual Color getColor(Intersection, const Light[], size_t) = 0;
103
};
104
105
struct Object {
106
  Material *mat;
107
  Object(Material* m) : mat(m) { }
108
  virtual ~Object() { }
109
  virtual Intersection intersectRay(Ray) = 0;
110
};
111
112
struct Scene {
113
  static const size_t max_obj_num = 100;
114
  size_t obj_num;
115
  Object* objs[max_obj_num];
116
117
  void AddObject(Object *o) {
118
    objs[obj_num++] = o;
119
  }
120
121
  ~Scene() {
122
    for(int i = 0; i != obj_num; ++i) {
123
      delete objs[i];
124
    }
125
  }
126
127
  static const size_t max_lgt_num = 10;
128
  size_t lgt_num;
129
  Light lgts[max_obj_num];
130
131
  void AddLight(const Light& l) {
132
    lgts[lgt_num++] = l;
133
  }
134
135
  static const Vector env_color;
136
137
  Scene() : obj_num(0) { } 
138
139
  Intersection getClosestIntersection(Ray r, int* index = NULL) const {
140
    Intersection closest_intersection;
141
    float closest_intersection_dist;
142
    int closest_index = -1;
143
144
    for(int i = 0; i < obj_num; ++i) {
145
      Intersection inter = objs[i]->intersectRay(r);
146
      if(!inter.is_valid)
147
        continue;
148
      float dist = (inter.pos - r.origin).length();
149
      if(closest_index == -1 || dist < closest_intersection_dist) {
150
        closest_intersection = inter;
151
        closest_intersection_dist = dist;
152
        closest_index = i;
153
      }
154
    }
155
156-
    if(closest_index != -1) {
156+
    if(index) {
157-
      return objs[closest_index]->mat->getColor(closest_intersection, lgts, lgt_num);
157+
      *index = closest_index;
158
    }
159
    return closest_intersection;
160
  } 
161
162
  Color shootRay(Ray r) const {
163
    int index;
164
    Intersection inter = getClosestIntersection(r, &index);
165
166
    if(index != -1) {
167
      return objs[index]->mat->getColor(inter, lgts, lgt_num);
168
    } else {
169
      return env_color;
170
    }
171
  }
172
} scene;
173
const Vector Scene::env_color = Vector();
174
175
struct Camera {
176
  Vector pos, plane_pos, right, up;
177
178
  Camera(float fov, const Vector& eye, const Vector& target, const Vector& plane_up) 
179
      : pos(eye - (target-eye).normalize() / (2*tan((fov*M_PI/180)/2))), plane_pos(eye) 
180
   { 
181
      Vector fwd = (plane_pos - pos).normalize();
182
      right = cross(fwd, plane_up).normalize();
183
      up = cross(right, fwd).normalize();
184
   } 
185
186
  void takePicture() {
187
    for(int x = 0; x < Screen::height; ++x)
188
      for(int y = 0; y < Screen::width; ++y)
189
        capturePixel(x, y);
190
  }
191
192
  void capturePixel(float x, float y) {
193
    Vector pos_on_plane = Vector(
194
      (x - Screen::width/2) / (Screen::width/2),
195
      // Itt nem kell megfordítani az y tengelyt. A bal fölső sarok az origó most.
196
      (y - Screen::height/2) / (Screen::height/2), 
197
      0
198
    );
199
200
    Vector plane_intersection = plane_pos + pos_on_plane.x * right + pos_on_plane.y * up;
201
202
    Ray r = {pos, (plane_intersection - pos).normalize()};
203
    Screen::Pixel(x, y) = scene.shootRay(r);
204
  }
205
} camera(60, Vector(-3, 2, -2), Vector(), Vector(0, 1, 0));
206
207
// Idáig egy általános raytracert definiáltam. Innentől jönnek a konkrétumok.
208
209
struct DiffuseMaterial : public Material {
210
  Color own_color;
211
212
  DiffuseMaterial(const Color& color) : own_color(color) { }
213
214-
          float intensity = max(dot(inter.normal, light.pos.normalize()), 0.0f);
214+
215
    Color accum_color;
216
217
    for(int i = 0; i < lgt_num; ++i) {
218
      const Light& light = lgts[i];
219
      switch(light.type) {
220
        case Light::Ambient: {
221
          accum_color += light.color * own_color;
222
        } break;
223
        case Light::Directional: {
224
          float intensity = max(dot(inter.normal, light.dir.normalize()), 0.0f);
225
          accum_color += intensity * light.color * own_color;
226
        } break;
227
      }
228
    }
229
230
    return accum_color.saturate();
231
  }
232
};
233
234
DiffuseMaterial blue(Color(0.0f, 0.4f, 1.0f));
235
236
237
struct Triangle : public Object {
238
  Vector a, b, c, normal; 
239
240
  // Az óra járásával ellentétes (CCW) körüljárási irányt feltételez ez a kód.
241
  Triangle(Material* mat, const Vector& a, const Vector& b, const Vector& c) 
242
    : Object(mat), a(a), b(b), c(c) {
243
      Vector ab = b - a;
244
      Vector ac = c - a;
245
      normal = cross(ab.normalize(), ac.normalize()).normalize();
246
  }
247
248
  // Ennek a függvénynek a megértéséhez rajzolj magadnak egyszerű ábrákat!
249
  Intersection intersectRay(Ray r) {
250
    // Először számoljuk ki, hogy melyen mekkora távot 
251
    // tesz meg a sugár, míg eléri a háromszög síkját
252
    // A számoláshoz tudnuk kell hogy ha egy 'v' vektort 
253
    // skaliráisan szorzunk egy egységvektorral, akkor
254
    // az eredmény a 'v'-nek az egységvektorra vetített 
255
    // hossza lesz. Ezt felhasználva, ha a sugár kiindulási 
256
    // pontjából a sík egy pontjba mutató vektort levetítjük 
257
    // a sík normál vektorára, akkor megkapjuk, hogy milyen 
258
    // távol van a sugár kiindulási pontja a síktól. Továbbá,
259
    // ha az a sugár irányát vetítjük a normálvektorra, akkor meg
260
    // megtudjuk, hogy az milyen gyorsan halad a sík fele.
261
    // Innen a már csak a t = s / v képletet kell csak használnunk. 
262
    float ray_travel_dist = dot(a - r.origin, normal) / dot(r.direction, normal);
263
264
    // Ha a háromszög az ellenkező irányba van, mint 
265
    // amerre a sugár megy, akkor nincs metszéspontjuk
266
    if(ray_travel_dist < 0)
267
      return Intersection(); 
268
269
    // Számoljuk ki, hogy a sugár hol metszi a sugár síkját.
270
    Vector plane_intersection = r.origin + ray_travel_dist * r.direction;
271
272
    /* Most már csak el kell döntenünk, hogy ez a pont a háromszög
273
       belsejében van-e. Erre két lehetőség van: 
274
     
275
       - A háromszög összes élére megnézzük, hogy a pontot a hároszög 
276
       egy megfelelő pontjával összekötve a kapott szakasz, és a háromszög
277
       élének a vektoriális szorzata a normál irányába mutat-e.
278
       Pl:
279
     
280
                 a
281
               / |
282
              /  |
283
             /   |
284
            /  x |  y
285
           /     |
286
          b------c
287
288
       Nézzük meg az x és y pontra ezt az algoritmust.
289
       A cross(ab, ax), a cross(bc, bx), és a cross(ca, cx) és kifele mutat a 
290
       képernyőből, ugyan abba az irányba mint a normál vektor. Ezt amúgy a 
291
       dot(cross(ab, ax), normal) >= 0 összefüggéssel egyszerű ellenőrizni.
292
       Az algoritmus alapján az x a háromszög belsejében van.
293
294
       Míg az y esetében a cross(ca, cy) befele mutat, a normállal ellenkező irányba,
295
       tehát a dot(cross(ca, cy), normal) < 0 ami az algoritmus szerint azt jelenti, 
296
       hogy az y pont a háromszögön kívül van. 
297
      
298
       - A ötlet lehetőség a barycentrikus koordinátáknak azt a tulajdonságát használja
299
       ki, hogy azok a háromszög belsejében lévő pontokra kivétel nélkül nem negatívak, 
300
       míg a háromszögön kívül lévő pontokra legalább egy koordináta negatív.
301
       Ennek a megoldásnak a használatához ki kell jelölnünk két tetszőleges, de egymásra 
302
       merőleges vektort a síkon, ezekre le kell vetíteninünk a háromszög pontjait, és 
303
       kérdéses pontot, és az így kapott koordinátákra alakzmanunk kell egy a wikipediáról
304
       egyszerűen kimásolható képletet: 
305
       http://en.wikipedia.org/wiki/Barycentric_coordinate_system#Converting_to_barycentric_coordinates
306
      
307
       Én az első lehetőséget implementálom. */
308
309
    const Vector& x = plane_intersection;
310
311
    Vector ab = b - a;
312
    Vector ax = x - a;
313
314
    Vector bc = c - b;
315
    Vector bx = x - b;
316
317
    Vector ca = a - c;
318
    Vector cx = x - c;
319
320
    if(dot(cross(ab, ax), normal) >= 0) 
321
      if(dot(cross(bc, bx), normal) >= 0) 
322
        if(dot(cross(ca, cx), normal) >= 0) 
323
          return Intersection(x, normal, true);
324
325
    return Intersection();
326
  }
327
};
328
329
void onDisplay() {
330
  glClear(GL_COLOR_BUFFER_BIT);
331
332
  camera.takePicture();
333
  Screen::Draw();
334
335
  glutSwapBuffers();
336
}
337-
  Light amb = {Light::Ambient, Vector(), Color(0.2f, 0.2f, 0.2f)};
337+
338-
  Light dir = {Light::Directional, Vector(-2, 4, -1), Color(1.0f, 1.0f, 1.0f)};
338+
339
  static bool first_call = true;
340
  if(first_call) {
341
    glutPostRedisplay();
342
    first_call = false;
343
  }
344
}
345
346
void onInitialization() {
347
  Light amb = {Light::Ambient, Vector(), Vector(), Color(0.2f, 0.2f, 0.2f)};
348
  Light dir = {Light::Directional, Vector(), Vector(-2, 4, -1), Color(1.0f, 1.0f, 1.0f)};
349
  scene.AddLight(amb);
350
  scene.AddLight(dir);
351
352
  // Front face
353
  scene.AddObject(new Triangle(&blue, Vector(+1, -1, -1), Vector(-1, -1, -1), Vector(-1, +1, -1)));
354
  scene.AddObject(new Triangle(&blue, Vector(-1, +1, -1), Vector(+1, +1, -1), Vector(+1, -1, -1)));
355
356
  // Back face
357
  scene.AddObject(new Triangle(&blue, Vector(+1, -1, +1), Vector(-1, -1, +1), Vector(-1, +1, +1)));
358
  scene.AddObject(new Triangle(&blue, Vector(-1, +1, +1), Vector(+1, +1, +1), Vector(+1, -1, +1)));
359
360
  // Right face
361
  scene.AddObject(new Triangle(&blue, Vector(+1, -1, -1), Vector(+1, -1, +1), Vector(+1, +1, +1)));
362
  scene.AddObject(new Triangle(&blue, Vector(+1, +1, +1), Vector(+1, +1, -1), Vector(+1, -1, -1)));
363
364
  // Left face
365
  scene.AddObject(new Triangle(&blue, Vector(-1, -1, -1), Vector(-1, -1, +1), Vector(-1, +1, +1)));
366
  scene.AddObject(new Triangle(&blue, Vector(-1, +1, +1), Vector(-1, +1, -1), Vector(-1, -1, -1)));
367
368
  // Upper face
369
  scene.AddObject(new Triangle(&blue, Vector(-1, +1, -1), Vector(-1, +1, +1), Vector(+1, +1, +1)));
370
  scene.AddObject(new Triangle(&blue, Vector(+1, +1, -1), Vector(-1, +1, -1), Vector(+1, +1, +1)));
371
372
  // Lower face
373
  scene.AddObject(new Triangle(&blue, Vector(-1, -1, +1), Vector(-1, -1, -1), Vector(+1, -1, +1)));
374
  scene.AddObject(new Triangle(&blue, Vector(+1, -1, -1), Vector(+1, -1, +1), Vector(-1, -1, -1)));
375
}
376
377
void onKeyboard(unsigned char key, int, int) {}
378
379
void onKeyboardUp(unsigned char key, int, int) {}
380
381
void onMouse(int, int, int, int) {}
382
383
void onMouseMotion(int, int) {}
384
385
int main(int argc, char **argv) {
386
  glutInit(&argc, argv);
387
  glutInitWindowSize(Screen::width, Screen::height);
388
  glutInitWindowPosition(100, 100);
389
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
390
391
  glutCreateWindow("Grafika pelda program");
392
393
  glMatrixMode(GL_MODELVIEW);
394
  glLoadIdentity();
395
  glMatrixMode(GL_PROJECTION);
396
  glLoadIdentity();
397
398
  onInitialization();
399
400
  glutDisplayFunc(onDisplay);
401
  glutMouseFunc(onMouse);
402
  glutIdleFunc(onIdle);
403
  glutKeyboardFunc(onKeyboard);
404
  glutKeyboardUpFunc(onKeyboardUp);
405
  glutMotionFunc(onMouseMotion);
406
407
  glutMainLoop();
408
409
  return 0;
410
}