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