Advertisement
RohamCsiga

Vik Wiki Grafika - Spot lámpa

Jan 25th, 2014
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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, Point, Spot} type;
  96.   Vector pos, dir;
  97.   Color color;
  98.   float spot_cutoff;
  99. };
  100.  
  101. struct Material {
  102.   virtual ~Material() { }
  103.   virtual Color getColor(Intersection, const Light[], size_t) = 0;
  104. };
  105.  
  106. struct Object {
  107.   Material *mat;
  108.   Object(Material* m) : mat(m) { }
  109.   virtual ~Object() { }
  110.   virtual Intersection intersectRay(Ray) = 0;
  111. };
  112.  
  113. struct Scene {
  114.   static const size_t max_obj_num = 100;
  115.   size_t obj_num;
  116.   Object* objs[max_obj_num];
  117.  
  118.   void AddObject(Object *o) {
  119.     objs[obj_num++] = o;
  120.   }
  121.  
  122.   ~Scene() {
  123.     for(int i = 0; i != obj_num; ++i) {
  124.       delete objs[i];
  125.     }
  126.   }
  127.  
  128.   static const size_t max_lgt_num = 10;
  129.   size_t lgt_num;
  130.   Light lgts[max_obj_num];
  131.  
  132.   void AddLight(const Light& l) {
  133.     lgts[lgt_num++] = l;
  134.   }
  135.  
  136.   static const Vector env_color;
  137.  
  138.   Scene() : obj_num(0) { }
  139.  
  140.   Intersection getClosestIntersection(Ray r, int* index = NULL) const {
  141.     Intersection closest_intersection;
  142.     float closest_intersection_dist;
  143.     int closest_index = -1;
  144.  
  145.     for(int i = 0; i < obj_num; ++i) {
  146.       Intersection inter = objs[i]->intersectRay(r);
  147.       if(!inter.is_valid)
  148.         continue;
  149.       float dist = (inter.pos - r.origin).length();
  150.       if(closest_index == -1 || dist < closest_intersection_dist) {
  151.         closest_intersection = inter;
  152.         closest_intersection_dist = dist;
  153.         closest_index = i;
  154.       }
  155.     }
  156.  
  157.     if(index) {
  158.       *index = closest_index;
  159.     }
  160.     return closest_intersection;
  161.   }
  162.  
  163.   Color shootRay(Ray r) const {
  164.     int index;
  165.     Intersection inter = getClosestIntersection(r, &index);
  166.  
  167.     if(index != -1) {
  168.       return objs[index]->mat->getColor(inter, lgts, lgt_num);
  169.     } else {
  170.       return env_color;
  171.     }
  172.   }
  173. } scene;
  174. const Vector Scene::env_color = Vector();
  175.  
  176. struct Camera {
  177.   Vector pos, plane_pos, right, up;
  178.  
  179.   Camera(float fov, const Vector& eye, const Vector& target, const Vector& plane_up)
  180.       : pos(eye - (target-eye).normalize() / (2*tan((fov*M_PI/180)/2))), plane_pos(eye)
  181.    {
  182.       Vector fwd = (plane_pos - pos).normalize();
  183.       right = cross(fwd, plane_up).normalize();
  184.       up = cross(right, fwd).normalize();
  185.    }
  186.  
  187.   void takePicture() {
  188.     for(int x = 0; x < Screen::height; ++x)
  189.       for(int y = 0; y < Screen::width; ++y)
  190.         capturePixel(x, y);
  191.   }
  192.  
  193.   void capturePixel(float x, float y) {
  194.     Vector pos_on_plane = Vector(
  195.       (x - Screen::width/2) / (Screen::width/2),
  196.       // Itt nem kell megfordítani az y tengelyt. A bal fölső sarok az origó most.
  197.       (y - Screen::height/2) / (Screen::height/2),
  198.       0
  199.     );
  200.  
  201.     Vector plane_intersection = plane_pos + pos_on_plane.x * right + pos_on_plane.y * up;
  202.  
  203.     Ray r = {pos, (plane_intersection - pos).normalize()};
  204.     Screen::Pixel(x, y) = scene.shootRay(r);
  205.   }
  206. } camera(60, Vector(-3, 2, -2), Vector(), Vector(0, 1, 0));
  207.  
  208. // Idáig egy általános raytracert definiáltam. Innentől jönnek a konkrétumok.
  209.  
  210. struct DiffuseMaterial : public Material {
  211.   Color own_color;
  212.  
  213.   DiffuseMaterial(const Color& color) : own_color(color) { }
  214.  
  215.   Color getColor(Intersection inter, const Light* lgts, size_t lgt_num) {
  216.     Color accum_color;
  217.  
  218.     for(int i = 0; i < lgt_num; ++i) {
  219.       const Light& light = lgts[i];
  220.       switch(light.type) {
  221.         case Light::Ambient: {
  222.           accum_color += light.color * own_color;
  223.         } break;
  224.         case Light::Directional: {
  225.           float intensity = max(dot(inter.normal, light.dir.normalize()), 0.0f);
  226.           accum_color += intensity * light.color * own_color;
  227.         } break;
  228.         case Light::Spot: {
  229.           Vector light_to_pos = inter.pos - light.pos;
  230.           if(dot(light_to_pos.normalize(), light.dir) < light.spot_cutoff) {
  231.             break; // Ha nincs megvilágítva, akkor ne csináljuk semmit.
  232.           } // Különben számoljuk pont fényforrással.
  233.         } // NINCS break!
  234.         case Light::Point: {
  235.           Vector pos_to_light = light.pos - inter.pos;
  236.           float attenuation = pow(1/pos_to_light.length(), 2);
  237.           float intensity = max(dot(inter.normal, pos_to_light.normalize()), 0.0f);
  238.           accum_color += attenuation * intensity * light.color * own_color;
  239.         } break;
  240.       }
  241.     }
  242.  
  243.     return accum_color.saturate();
  244.   }
  245. };
  246.  
  247. DiffuseMaterial blue(Color(0.0f, 0.4f, 1.0f));
  248.  
  249. struct Triangle : public Object {
  250.   Vector a, b, c, normal;
  251.  
  252.   // Az óra járásával ellentétes (CCW) körüljárási irányt feltételez ez a kód.
  253.   Triangle(Material* mat, const Vector& a, const Vector& b, const Vector& c)
  254.     : Object(mat), a(a), b(b), c(c) {
  255.       Vector ab = b - a;
  256.       Vector ac = c - a;
  257.       normal = cross(ab.normalize(), ac.normalize()).normalize();
  258.   }
  259.  
  260.   // Ennek a függvénynek a megértéséhez rajzolj magadnak egyszerű ábrákat!
  261.   Intersection intersectRay(Ray r) {
  262.     // Először számoljuk ki, hogy melyen mekkora távot
  263.     // tesz meg a sugár, míg eléri a háromszög síkját
  264.     // A számoláshoz tudnuk kell hogy ha egy 'v' vektort
  265.     // skaliráisan szorzunk egy egységvektorral, akkor
  266.     // az eredmény a 'v'-nek az egységvektorra vetített
  267.     // hossza lesz. Ezt felhasználva, ha a sugár kiindulási
  268.     // pontjából a sík egy pontjba mutató vektort levetítjük
  269.     // a sík normál vektorára, akkor megkapjuk, hogy milyen
  270.     // távol van a sugár kiindulási pontja a síktól. Továbbá,
  271.     // ha az a sugár irányát vetítjük a normálvektorra, akkor meg
  272.     // megtudjuk, hogy az milyen gyorsan halad a sík fele.
  273.     // Innen a már csak a t = s / v képletet kell csak használnunk.
  274.     float ray_travel_dist = dot(a - r.origin, normal) / dot(r.direction, normal);
  275.  
  276.     // Ha a háromszög az ellenkező irányba van, mint
  277.     // amerre a sugár megy, akkor nincs metszéspontjuk
  278.     if(ray_travel_dist < 0)
  279.       return Intersection();
  280.  
  281.     // Számoljuk ki, hogy a sugár hol metszi a sugár síkját.
  282.     Vector plane_intersection = r.origin + ray_travel_dist * r.direction;
  283.  
  284.     /* Most már csak el kell döntenünk, hogy ez a pont a háromszög
  285.        belsejében van-e. Erre két lehetőség van:
  286.      
  287.        - A háromszög összes élére megnézzük, hogy a pontot a hároszög
  288.        egy megfelelő pontjával összekötve a kapott szakasz, és a háromszög
  289.        élének a vektoriális szorzata a normál irányába mutat-e.
  290.        Pl:
  291.      
  292.                  a
  293.                / |
  294.               /  |
  295.              /   |
  296.             /  x |  y
  297.            /     |
  298.           b------c
  299.  
  300.        Nézzük meg az x és y pontra ezt az algoritmust.
  301.        A cross(ab, ax), a cross(bc, bx), és a cross(ca, cx) és kifele mutat a
  302.        képernyőből, ugyan abba az irányba mint a normál vektor. Ezt amúgy a
  303.        dot(cross(ab, ax), normal) >= 0 összefüggéssel egyszerű ellenőrizni.
  304.        Az algoritmus alapján az x a háromszög belsejében van.
  305.  
  306.        Míg az y esetében a cross(ca, cy) befele mutat, a normállal ellenkező irányba,
  307.        tehát a dot(cross(ca, cy), normal) < 0 ami az algoritmus szerint azt jelenti,
  308.        hogy az y pont a háromszögön kívül van.
  309.      
  310.        - A ötlet lehetőség a barycentrikus koordinátáknak azt a tulajdonságát használja
  311.        ki, hogy azok a háromszög belsejében lévő pontokra kivétel nélkül nem negatívak,
  312.        míg a háromszögön kívül lévő pontokra legalább egy koordináta negatív.
  313.        Ennek a megoldásnak a használatához ki kell jelölnünk két tetszőleges, de egymásra
  314.        merőleges vektort a síkon, ezekre le kell vetíteninünk a háromszög pontjait, és
  315.        kérdéses pontot, és az így kapott koordinátákra alakzmanunk kell egy a wikipediáról
  316.        egyszerűen kimásolható képletet:
  317.        http://en.wikipedia.org/wiki/Barycentric_coordinate_system#Converting_to_barycentric_coordinates
  318.      
  319.        Én az első lehetőséget implementálom. */
  320.  
  321.     const Vector& x = plane_intersection;
  322.  
  323.     Vector ab = b - a;
  324.     Vector ax = x - a;
  325.  
  326.     Vector bc = c - b;
  327.     Vector bx = x - b;
  328.  
  329.     Vector ca = a - c;
  330.     Vector cx = x - c;
  331.  
  332.     if(dot(cross(ab, ax), normal) >= 0)
  333.       if(dot(cross(bc, bx), normal) >= 0)
  334.         if(dot(cross(ca, cx), normal) >= 0)
  335.           return Intersection(x, normal, true);
  336.  
  337.     return Intersection();
  338.   }
  339. };
  340.  
  341. void onDisplay() {
  342.   glClear(GL_COLOR_BUFFER_BIT);
  343.  
  344.   camera.takePicture();
  345.   Screen::Draw();
  346.  
  347.   glutSwapBuffers();
  348. }
  349.  
  350. void onIdle() {
  351.   static bool first_call = true;
  352.   if(first_call) {
  353.     glutPostRedisplay();
  354.     first_call = false;
  355.   }
  356. }
  357.  
  358. void onInitialization() {
  359.   Light amb = {Light::Ambient, Vector(), Vector(), Color(0.2f, 0.2f, 0.2f)};
  360.   Light spot = {Light::Spot, Vector(-3, 3, -2), (-Vector(-3, 3, -2)).normalize(), Color(20.0f, 20.0f, 20.0f), 0.98f};
  361.   scene.AddLight(amb);
  362.   scene.AddLight(spot);
  363.  
  364.   // Front 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.   // Back 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.   // Right 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.   // Left face
  377.   scene.AddObject(new Triangle(&blue, Vector(-1, -1, -1), Vector(-1, -1, +1), Vector(-1, +1, +1)));
  378.   scene.AddObject(new Triangle(&blue, Vector(-1, +1, +1), Vector(-1, +1, -1), Vector(-1, -1, -1)));
  379.  
  380.   // Upper face
  381.   scene.AddObject(new Triangle(&blue, Vector(-1, +1, -1), Vector(-1, +1, +1), Vector(+1, +1, +1)));
  382.   scene.AddObject(new Triangle(&blue, Vector(+1, +1, -1), Vector(-1, +1, -1), Vector(+1, +1, +1)));
  383.  
  384.   // Lower face
  385.   scene.AddObject(new Triangle(&blue, Vector(-1, -1, +1), Vector(-1, -1, -1), Vector(+1, -1, +1)));
  386.   scene.AddObject(new Triangle(&blue, Vector(+1, -1, -1), Vector(+1, -1, +1), Vector(-1, -1, -1)));
  387. }
  388.  
  389. void onKeyboard(unsigned char key, int, int) {}
  390.  
  391. void onKeyboardUp(unsigned char key, int, int) {}
  392.  
  393. void onMouse(int, int, int, int) {}
  394.  
  395. void onMouseMotion(int, int) {}
  396.  
  397. int main(int argc, char **argv) {
  398.   glutInit(&argc, argv);
  399.   glutInitWindowSize(Screen::width, Screen::height);
  400.   glutInitWindowPosition(100, 100);
  401.   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
  402.  
  403.   glutCreateWindow("Grafika pelda program");
  404.  
  405.   glMatrixMode(GL_MODELVIEW);
  406.   glLoadIdentity();
  407.   glMatrixMode(GL_PROJECTION);
  408.   glLoadIdentity();
  409.  
  410.   onInitialization();
  411.  
  412.   glutDisplayFunc(onDisplay);
  413.   glutMouseFunc(onMouse);
  414.   glutIdleFunc(onIdle);
  415.   glutKeyboardFunc(onKeyboard);
  416.   glutKeyboardUpFunc(onKeyboardUp);
  417.   glutMotionFunc(onMouseMotion);
  418.  
  419.   glutMainLoop();
  420.  
  421.   return 0;
  422. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement