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