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 | } |