Skip to content
This repository has been archived by the owner on May 3, 2023. It is now read-only.

Inha Univ. Computer Graphics Capstone Design Mini Project

Notifications You must be signed in to change notification settings

Astro36/ICE3016-miniproject

Repository files navigation

ICE3016 miniproject

๋ณผํŽœ 3์ฐจ์› ์ œํ’ˆ ์นดํƒˆ๋กœ๊ทธ ์ œ์ž‘

C++ OpenGL VisualStudio Blender

YouTube ๋ฐœํ‘œ์˜์ƒ
youtube

ํ”„๋กœ์ ํŠธ ์ฃผ์ œ

  • ๋ณผํŽœ 3์ฐจ์› ์ œํ’ˆ ์นดํƒˆ๋กœ๊ทธ ์ œ์ž‘
  • OpenGL๊ณผ ๋งˆ์šฐ์Šค, ํ‚ค๋ณด๋“œ๋ฅผ ์ด์šฉํ•œ interactive ํ”„๋กœ์ ํŠธ ์ž‘์„ฑ
  • Blender ๋ชจ๋ธ๋ง๋ณด๋‹ค๋Š” OpenGL์„ ํ™œ์šฉํ•œ ๋™์ ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„์— ์ค‘์ ์„ ๋‘˜ ๊ฒƒ

์ฃผ์ œ ์„ ์ • ์ด์œ 

  • ๋ณผํŽœ์€ ์‹ค์ƒํ™œ์—์„œ ๊ฐ€์žฅ ์‰ฝ๊ฒŒ ์ ‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌผ์ฒด์ž„.
  • ๋Œ€๋žต์ ์ธ ํ˜•ํƒœ๋ฅผ ๋ชจ๋ธ๋งํ•˜๋Š” ๊ฒƒ์€ ์‰ฝ์ง€๋งŒ, ๊ทธ๋ฆฝ๊ฐ์„ ์œ„ํ•œ ๋ณผํŽœ ๋ชธํ†ต์˜ ๊ณก์„ ์„ ์„ฌ์„ธํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•จ.
  • ์‚ฌ๋žŒ๋“ค์ด ๋ณผํŽœ์„ ๊ฐ€์ง€๊ณ  ํ•˜๋Š” interactiveํ•œ ๋™์ž‘(๋ณผํŽœ ๋”ธ๊น๊ฑฐ๋ฆฌ๊ธฐ, ์ด์œ ์—†์ด ๋ณผํŽœ ๋ถ„ํ•ดํ•˜๊ธฐ)์„ OpenGL ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๊ตฌํ˜„ํ•ด ๋ณผ ๊ฒƒ์ž„.
  • ๋‚˜์•„๊ฐ€ ๋ณผํŽœ์œผ๋กœ ์ข…์ด์— ๊ทธ๋ฆผ์„ ๊ทธ๋ฆฌ๋Š” ๊ธฐ๋Šฅ๊นŒ์ง€ ๊ตฌํ˜„ํ•˜๋ฉฐ bmp ํ…์Šค์ฒ˜์™€ ์นด๋ฉ”๋ผ ์ด๋™์„ ๊นŠ๊ฒŒ ํ•™์Šตํ•จ.

๊ตฌํ˜„์‚ฌ์–‘

  1. Blender๋ฅผ ์ด์šฉํ•ด ๋ณผํŽœ์„ ๋ชจ๋ธ๋งํ•˜๊ณ  OpenGL ํ”„๋กœ๊ทธ๋žจ์„ ์ด์šฉํ•ด ๋ณด์—ฌ์ค€๋‹ค. space๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋ณผํŽœ์ด‰์„ ๊บผ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  2. ๋งˆ์šฐ์Šค์™€ w, a, s, d, z, x๋ฅผ ์ด์šฉํ•ด ์นด๋ฉ”๋ผ์˜ ์‹œ์ ๊ณผ ์œ„์น˜๋ฅผ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋‹ค. 1๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ๋กœ ์ด๋™ํ•œ๋‹ค.
  3. ๋งˆ์šฐ์Šค๋ฅผ ์ด์šฉํ•ด ๋ณผํŽœ ์ƒ‰์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค. ๋ณผํŽœ ์ƒ‰์ด ๋ฐ”๋€Œ๋ฉด ๋ณผํŽœ ๋ชจ๋ธ์˜ ์ƒ‰๋„ ๋ฐ”๋€๋‹ค.
  4. ๋””์ง€ํ„ธ ์นด๋ฉ”๋ผ๋ฅผ ์ด์šฉํ•ด ๋ณผํŽœ์˜ ํ…์Šค์ฒ˜๋ฅผ ์ถ”์ถœํ•˜๊ณ  ๋งคํ•‘ํ•œ๋‹ค.
  5. ํ…์Šค์ฒ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋“ฑ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค.
  6. ํšจ๊ณผ์Œ๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ด์šฉํ•ด ์ž์—ฐ์Šค๋Ÿฌ์šด ์—ฐ์ถœ์„ ๊ตฌํ˜„ํ•œ๋‹ค.

๋ชจ๋ธ๋ง ์„ธ๋ถ€ ๊ตฌํ˜„์‚ฌ์–‘

  • ๋ณผํŽœ์ด ๊ฐ๊ฐ์˜ ํŒŒํŠธ๋กœ ๋ถ„ํ•ด๋  ์ˆ˜ ์žˆ๋„๋ก, ๋ณผํŽœ ๋ชธํ†ต, ๋ณผํŽœ ์‹ฌ(์ด‰), ๋ณผํŽœ ๋šœ๊ป‘, ๋ณผํŽœ ๋ฒ„ํŠผ, ์Šคํ”„๋ง์œผ๋กœ ๋‚˜๋‰˜์–ด ๊ฐ๊ฐ ๋ชจ๋ธ๋ง ๋œ๋‹ค.
  • ๋ณผํŽœ์ด ์›€์ง์ผ ๋•Œ ๋ชจ๋“  ํŒŒํŠธ๊ฐ€ ๋™์‹œ์— ์›€์ง์—ฌ์•ผ ํ•œ๋‹ค. (๊ฒฐํ•ฉ ์ƒํƒœ)
  • ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ์—์„œ๋Š” ๋ณผํŽœ ์ด‰์ด ์ข…์ด์™€ ๋งž๋‹ฟ์•„ ์žˆ๋‹ค. ๋ณผํŽœ ์ด‰์ด ๊บผ๋‚ด์ ธ ์žˆ์ง€ ์•Š์€ ์ƒํƒœ์—์„œ๋Š” ๊ทธ๋ฆผ์ด ๊ทธ๋ ค์ง€์ง€ ์•Š๋Š”๋‹ค.

ํ‚ค๋ณด๋“œ/๋งˆ์šฐ์Šค ์ž…๋ ฅ

  • ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ์—์„œ๋Š” ์ข…์ด๊ฐ€ ํ•œ ๋ˆˆ์— ๋ณด์ด๊ฒŒ๋” ์‹œ์ ์ด ์˜ฌ๋ผ๊ฐ„๋‹ค.
  • ๊ด€์ฐฐ ๋ชจ๋“œ์™€ ๋ถ„ํ•ด ๋ชจ๋“œ๋Š” ๋ณผํŽœ์ด ์ž˜ ๋ณด์ด๋„๋ก ์‹œ์ ์ด ํ™•๋Œ€๋œ๋‹ค. ๋ถ„ํ•ด ๋ชจ๋“œ๋Š” ๋ถ„ํ•ด ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ œ๊ณตํ•œ๋‹ค.
  • ๊ทธ๋ฆผํŒŒ์ผ ์ €์žฅ์€ ํ”„๋ฆฐํ„ฐ๊ฐ€ ์ข…์ด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๋“ฏํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ณด์—ฌ์ค€๋‹ค.
  • ์ „์ฒดํ™”๋ฉด ์ƒํƒœ์—์„œ F11์„ ํ•œ ๋ฒˆ ๋” ๋ˆ„๋ฅด๋ฉด ์›๋ž˜ ํฌ๊ธฐ๋กœ ๋Œ์•„์˜จ๋‹ค.

๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ

  • ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ์—์„œ๋Š” ์นด๋ฉ”๋ผ ์‹œ์ ์€ ๊ณ ์ •๋˜๊ณ  ๋ณผํŽœ์ด ๋งˆ์šฐ์Šค์˜ ์›€์ง์ž„์„ ๋”ฐ๋ผํ•œ๋‹ค.
  • ๋งˆ์šฐ์Šค์˜ ์›€์ง์ž„์€ ๋ณผํŽœ๊ณผ ํ•จ๊ป˜ ๋ชจ๋ธ๋ง ๋˜๋Š” ์ข…์ด์— ํš์˜ ํ˜•ํƒœ๋กœ ๊ทธ๋ ค์ง„๋‹ค.
  • ๋ณผํŽœ ์ƒ‰์€ ๊ฒ€์ •, ๋นจ๊ฐ•, ํŒŒ๋ž‘ 3์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค.

๋ชจ๋ธ๋ง

Blender ๋ชจ๋ธ๋ง ๊ณผ์ •

  • ๋ณผํŽœ ์ด๋ฏธ์ง€๋ฅผ Blender์— ์˜ฌ๋ ค ๋†“๊ณ  Cylinder์„ ์ด์šฉํ•ด ๋ชจ์–‘์„ ๋งŒ๋“ฆ
  • ๋ณผํŽœ์˜ ๊ฐ ๋ถ€ํ’ˆ ๋ณ„๋กœ ๋”ฐ๋กœ ๋ชจ๋ธ๋งํ•˜์—ฌ OpenGL ํ”„๋กœ๊ทธ๋žจ ์ƒ์—์„œ ๊ฐœ๋ณ„์ ์œผ๋กœ ์กฐ์ž‘ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ

Blender UV ๋งคํ•‘ ๊ณผ์ •

  • ์Šค๋งˆํŠธํฐ ์นด๋ฉ”๋ผ๋กœ ๋ณผํŽœ์„ ์ดฌ์˜ํ•˜๊ณ  ๋กœ๊ณ  ๋ถ€๋ถ„์˜ ํ…์Šค์ฒ˜๋ฅผ ๋งคํ•‘
  • ๋กœ๊ณ ๊ฐ€ ์„ ๋ช…ํ•˜๊ฒŒ ๋ณด์ด๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด UV ๋งต์—์„œ ๋กœ๊ณ  ๋ถ€๋ถ„๋งŒ ํ™•๋Œ€ํ•˜์—ฌ ์ƒ๋Œ€์ ์œผ๋กœ ๋†’์€ ํ•ด์ƒ๋„์˜ ํ…์Šค์ฒ˜๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ํ•จ

๋ณผํŽœ ๋ชจ๋ธ ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ

๊ฐ๊ฐ์˜ .obj ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์™€ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ƒ‰์„ ์ž…ํž˜ ํ…์Šค์ฒ˜๋Š” ๋ณผํŽœ ๋ชธํ†ต๋งŒ ์ ์šฉ

ํ”„๋กœ๊ทธ๋ž˜๋ฐ

.obj ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

struct Vec2f {
    float x;
    float y;
};

struct Vec3f {
    float x;
    float y;
    float z;
};

class Object {
public:
    Object(const std::string& filename);
    ~Object() = default;
    void render() const;
private:
    std::string name;
    std::vector<Vec3f> vertices; // v
    std::vector<Vec2f> textures; // vt
    std::vector<Vec3f> normals; // vn
    std::vector<std::size_t> vertex_indices; // f
    std::vector<std::size_t> texture_indices; // f
    std::vector<std::size_t> normal_indices; // f
};
void Object::render() const {
    glBegin(GL_TRIANGLES);
    for (std::size_t n = 0; n < vertex_indices.size(); n += 3) {
        glTexCoord2f(textures[texture_indices[n] - 1].x, textures[texture_indices[n] -1].y);
        glNormal3f(normals[normal_indices[n] - 1].x, normals[normal_indices[n] - 1].y, normals[normal_indices[n] - 1].z);
        glVertex3f(vertices[vertex_indices[n] - 1].x, vertices[vertex_indices[n] - 1].y, vertices[vertex_indices[n] - 1].z);
        glTexCoord2f(textures[texture_indices[n + 1] - 1].x, textures[texture_indices[n + 1] - 1].y);
        glNormal3f(normals[normal_indices[n + 1] - 1].x, normals[normal_indices[n + 1] - 1].y, normals[normal_indices[n + 1] - 1].z);
        glVertex3f(vertices[vertex_indices[n + 1] - 1].x, vertices[vertex_indices[n + 1] - 1].y, vertices[vertex_indices[n + 1] - 1].z);
        glTexCoord2f(textures[texture_indices[n + 2] - 1].x, textures[texture_indices[n + 2] - 1].y);
        glNormal3f(normals[normal_indices[n + 2] - 1].x, normals[normal_indices[n + 2] - 1].y, normals[normal_indices[n + 2] - 1].z);
        glVertex3f(vertices[vertex_indices[n + 2] - 1].x, vertices[vertex_indices[n + 2] - 1].y, vertices[vertex_indices[n + 2] - 1].z);
    }
    glEnd();
}

์‹ค์Šต์‹œ๊ฐ„์— ์ œ๊ณต๋ฐ›์€ ObjParser.h ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•ด ๊ฐ์ฒด์ง€ํ–ฅ์ ์ธ Object Parser์™€ Drawer๋ฅผ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ

Bitmap ํด๋ž˜์Šค

class Bitmap {
public:
    Bitmap() = delete;
    Bitmap(int width, int height);
    Bitmap(const std::string& filename);
    ~Bitmap();
    void fill_pixel(int x, int y, const Color& color);
    int get_channels() const;
    int get_width() const;
    int get_height() const;
    unsigned char* get_pixels() const;
    void save(const std::string& out_filename, const
    std::string& ref_filename) const;
private:
    int width;
    int height;
    int channels;
    unsigned char* pixels;
};
Bitmap::Bitmap(const std::string& filename) {
    FILE* fp = fopen(filename.c_str(), "rb");
    if (!fp) {
        perror("File opening failed\n");
    }

    unsigned char header[54]; // bitmap header
    if (fread(header, sizeof(unsigned char), 54, fp) != 54 || header[0] != 'B' || header[1] != 'M') {
        perror("Invalid BMP file\n");
        fclose(fp);
    }

    unsigned int offset = *(unsigned int*) &(header[0x0A]);
    width = *(int*) &(header[0x12]);
    height = *(int*) &(header[0x16]);
    unsigned int size = *(unsigned int*) &(header[0x22]); // image size

    if (size == width * height) {
        channels = 1;
    } else {
        channels = 3;
    }
    size = width * height * channels;
    if (offset == 0) {
        offset = 54; // The BMP header is done that way
    }
    pixels = new unsigned char[size]; // image pixel data
    fread(pixels, sizeof(unsigned char), size, fp);
    fclose(fp);

    if (channels == 3) { // BGR order -> RGB order
        reverse_each_pixel(width, height, pixels);
    }
}

์‹ค์Šต์‹œ๊ฐ„์— ์ œ๊ณต๋ฐ›์€ bmpfunc.cpp ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•ด ๊ฐ์ฒด์ง€ํ–ฅ์ ์ธ Bitmap Parser๋ฅผ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•จ

Bitmap::Bitmap(int width, int height)
: width(width), height(height), channels(3) {
    int size = width * height * 3;
    pixels = new unsigned char[size]; // image pixel data
    std::fill(pixels, pixels + size, 0xff);
}

ํฐ ์ƒ‰ ์ข…์ด ํ…์Šค์ฒ˜๋ฅผ ์œ„ํ•œ Bitmap(int width, int height)

MyPen Object ํด๋ž˜์Šค

class MyPen {
public:
    MyPen() = default;
    ~MyPen() = default;
    const Color& get_line_color() const;
    bool is_clicked() const;
    bool is_drawing_mode() const;
    void move(float x, float y);
    void disable_drawing_mode();
    void enable_drawing_mode();
    void perform_click();
    void perform_draw_line(Paper* paper, int x1, 
    int y1, int x2, int y2);
    void render(float disassembled = 0.0f) const;
    void set_line_color(const Color& color);
private:
    // ...
    float x = 0.0f;
    float y = 0.0f;
    bool clicked = false;
    bool drawing = false;
    Color line_color = { 0.0f, 0.0f, 0.0f };
};
void MyPen::render(float animated) const {
    if (drawing) {
        glTranslatef(x, 1.89f, y);
        glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
    } else {
        glTranslatef(x, 0.5f, y);
    }

    glTranslatef(-1.5f * animated, 0.0f, 0.0f);
    pen_barrel.render();
    pen_clip.render();
    pen_grip.render();

    if (!clicked) {
        glTranslatef(-0.1f, 0.0f, 0.0f);
    }
    glPushMatrix();
    glTranslatef(-0.5f * animated, 0.0f, 0.0f);
    pen_cap.render();
    glPopMatrix();

    glTranslatef(1.0f * animated, 0.0f, 0.0f);
    pen_reservoir.render();
    pen_socket.render();

    glTranslatef(0.75f * animated, 0.0f, 0.0f);
    pen_spring.render();

    glColor3f(line_color.red, line_color.green, line_color.blue);
    glPushMatrix();
    glTranslatef(1.88f, 0.0f, 0.0f);
    glutSolidSphere(0.004, 10, 10); // ball
    glPopMatrix();

    if (!clicked) {
        glTranslatef(0.1f, 0.0f, 0.0f);
    }
    glTranslatef(0.75f * animated, 0.0f, 0.0f);
    pen_tip.render();
}

float disassembled๋Š” ๋ณผํŽœ ๋ถ„ํ•ด ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ณด์—ฌ์ค„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ณ„์ˆ˜

์กฐ๋ฆฝ ์ƒํƒœ์ผ ๋•Œ๋Š” disassembled=-1.0

๋ณผํŽœ ์ƒ‰์— ๋”ฐ๋ผ ๋ณผํŽœ ์ด‰ ๋์˜ ์ƒ‰์ด ๋‹ฌ๋ผ์ง€๋„๋ก glColor3f์™€ glutSolidSphere์œผ๋กœ ๊ตฌํ˜„

๋ณผํŽœ ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ

  • ์นด๋ฉ”๋ผ 1์„ ๋ˆ„๋ฅด๋ฉด ์นด๋ฉ”๋ผ ์‹œ์ ์ด ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ ์ข…์ด๊ฐ€ ํ•œ ๋ˆˆ์— ๋ณด์ž„
  • ํ‚ค๋ณด๋“œ ์ŠคํŽ˜์ด์Šค๋ฐ”๋ฅผ ๋ˆ„๋ฅด๋ฉด ํšจ๊ณผ์Œ๊ณผ ํ•จ๊ป˜ ํŽœ์ด‰์ด ํŠ€์–ด๋‚˜์˜ค๋ฉด์„œ ๋งˆ์šฐ์Šค๋ฅผ ๋“œ๋ž˜๊ทธ ํ•˜๋Š”๋Œ€๋กœ ์„ ์ด ๊ทธ๋ ค์ง (Bresenham ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ด์šฉ)
  • ์šฐ์ธก ์ƒ๋‹จ์˜ ์›์„ ํด๋ฆญํ•˜๋ฉด ๋ณผํŽœ ์ƒ‰์ด ๋ฐ”๋€œ
  • ๋งˆ์šฐ์Šค ์šฐํด๋ฆญ > Clear๋กœ ๊ทธ๋ฆผ์„ ์ง€์šธ ์ˆ˜ ์žˆ์Œ
void motion_cb(int x, int y) {
    float pen_x = (x * paper->get_width() / window_width) - (paper->get_width() / 2);
    float pen_y = (y * paper->get_height() / window_height) - (paper->get_height() / 2);
    mypen->move(pen_x, pen_y);
    int paper_x = x * paper->get_image_width() / window_width;
    int paper_y = paper->get_image_height() - (y * paper->get_image_height() / 
    window_height);
    if (prev_paper_x >= 0 && prev_paper_y >= 0 && mypen->is_clicked() && mypen->is_drawing_mode()) {
        mypen->perform_draw_line(paper, paper_x, paper_y, prev_paper_x, prev_paper_y);
        paper->update_texture();
    }
    prev_paper_x = paper_x;
    prev_paper_y = paper_y;
}
void Bitmap::fill_pixel(int x, int y, const Color& color) {
    pixels[channels * (y * width + x)] = color.red * 255;
    pixels[channels * (y * width + x) + 1] = color.green * 255;
    pixels[channels * (y * width + x) + 2] = color.blue * 255;
}

void Paper::update_texture() const {
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, image->get_width(), image->get_height(), 0, 
    GL_RGB, GL_UNSIGNED_BYTE, image->get_pixels());
}

๋ณผํŽœ ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ โ€“ ์ƒ‰์ƒ ์„ ํƒ

void pick(int x, int y) {
    int viewport_width = 160;
    int viewport_height = 60;
    unsigned int select_buf[256];
    glSelectBuffer(256, select_buf);
    int viewport[4] = { window_width - viewport_width, 0, viewport_width, viewport_height }; // (x, y, width, height)
    glRenderMode(GL_SELECT);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluPickMatrix(x, y, 0.1, 0.1, viewport);
    glOrtho(-viewport_width / 2, viewport_width / 2, -viewport_height / 2, viewport_height / 2, -50.0, 50.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    draw_color_spheres();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glFlush();

    int hits = glRenderMode(GL_RENDER);
    if (hits >= 1) {
        unsigned int idx = 0;
        unsigned int selected_z = -1; // max value of unsigned int(overflow)
        sphere_selected = -1;
        for (int i = 1; i <= hits; i += 1) {
            unsigned int name_count = select_buf[idx]; // always 1
            unsigned int z_min = select_buf[idx + 1];
            unsigned int z_max = select_buf[idx + 2];
            unsigned int name = select_buf[idx + 3];
            idx += name_count + 3;
            if (selected_z > z_max) {
                selected_z = z_max;
                sphere_selected = name;
            }
        }
    }
}
// mini viewport
int viewport_width = 160;
int viewport_height = 60;
glViewport(window_width - viewport_width, 
window_height - viewport_height, viewport_width, 
viewport_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-viewport_width / 2, viewport_width / 2, -viewport_height / 2, viewport_height / 2, -50.0, 50.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glDisable(GL_LIGHTING);
draw_color_spheres();
glEnable(GL_LIGHTING);

Multi-viewport๋ฅผ ์ด์šฉํ•ด ์ƒ‰์ƒ ์„ ํƒ ์›์„ ๊ทธ๋ ธ๊ธฐ ๋•Œ๋ฌธ์—, glGetIntegerv(GL_VIEWPORT, viewport) ๋Œ€์‹  viewport ํฌ๊ธฐ๋ฅผ ๋”ฐ๋กœ ์ง€์ •ํ•ด์•ผ ํ•จ

๋ณผํŽœ ํด๋ฆญ ํšจ๊ณผ์Œ ์ถœ๋ ฅ

#include <mmsystem.h>
mypen->perform_click();
PlaySound(TEXT("res/click.wav"), NULL, SND_ASYNC);

๋ณผํŽœ ๊ด€์ฐฐ ๋ชจ๋“œ

ํ‚ค๋ณด๋“œ 2๋ฅผ ๋ˆ„๋ฅด๋ฉด ์นด๋ฉ”๋ผ๊ฐ€ ํ™•๋Œ€๋˜๊ณ  ์นด๋ฉ”๋ผ๋ฅผ ์ด๋™ํ•˜๋ฉฐ ๋ณผํŽœ์„ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ์Œ

๋ณผํŽœ ์ƒ‰์ด ๋ฐ”๋€Œ๋ฉด ๋ณผํŽœ ๋ชธํ†ต์ƒ‰๋„ ๋ฐ”๋€œ

๋ณผํŽœ ๊ด€์ฐฐ ๋ชจ๋“œ โ€“ ํ…์Šค์ฒ˜ ์ƒ‰์ƒ ๋ณ€๊ฒฝ

void MyPen::Barrel::set_color(const Color& color) {
    int width = image->get_width();
    int height = image->get_height();
    int channels = image->get_channels();
    unsigned char* pixels = new unsigned char[width * height * channels];
    std::memcpy(pixels, image->get_pixels(), width * height * channels);
    for (int x = 0; x < width; x += 1) {
        for (int y = 0; y < height; y += 1) {
            if (pixels[channels * (y * width + x)] == 0 && pixels[channels * (y * width + x) + 1] == 0 && pixels[channels * (y * width + x) + 2] == 0) {
                pixels[channels * (y * width + x)] = color.red * 120;
                pixels[channels * (y * width + x) + 1] = color.green * 120;
                pixels[channels * (y * width + x) + 2] = color.blue * 120;
            }
        }
    }
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
    delete[] pixels;
}

๋ณผํŽœ ๋ชธํ†ต ํ…์Šค์ฒ˜์˜ ๊ฒ€์€ ๋ถ€๋ถ„์„ ์›ํ•˜๋Š” ์ƒ‰์œผ๋กœ ์น˜ํ™˜ํ•˜๋ฉด ๋ณผํŽœ ๋ชธํ†ต์˜ ์ƒ‰์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ

ํ•˜๋‚˜์˜ ํ…์Šค์ฒ˜๋กœ ๋‹ค์–‘ํ•œ ๋ณผํŽœ ๋ชธํ†ต ์ƒ‰์„ ํ‘œํ˜„ ๊ฐ€๋Šฅ

๋ณผํŽœ ๋ถ„ํ•ด ๋ชจ๋“œ

void time_cb(int value) {
    if (disassembling_animate >= 0.0f && disassembling_animate < 1.5f) {
        disassembling_animate += 0.01f;
        if (disassembling_animate >= 1.5f) {
            disassembling_animate = 1.5f;
        }
    }
    glutTimerFunc(interval, time_cb, NULL);
}

ํ‚ค๋ณด๋“œ 3๋ฅผ ๋ˆ„๋ฅด๋ฉด ์นด๋ฉ”๋ผ๊ฐ€ ํ™•๋Œ€๋˜๊ณ  ๋ณผํŽœ์ด ๋ถ„ํ•ด๋˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์—ฐ์ถœ

๊ทธ๋ฆผํŒŒ์ผ ์ถœ๋ ฅ ๋ชจ๋“œ

F2๋ฅผ ๋ˆ„๋ฅด๋ฉด ํ”„๋ฆฐํ„ฐ๋กœ ์ข…์ด๋ฅผ ์ธ์‡„ํ•˜๋Š” ๋“ฏํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋ณด์—ฌ์ง€๋ฉฐ ํŒŒ์ผ์ด ์ €์žฅ๋จ

// ๋‹ค๋ฅธ .bmp ํŒŒ์ผ ํ—ค๋”๋ฅผ ๋ณต์‚ฌํ•ด ์ƒˆ๋กœ์šด BMP ํŒŒ์ผ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
void Bitmap::save(const std::string& out_filename, const std::string& ref_filename) const {
    FILE* ifp = fopen(ref_filename.c_str(), "rb");
    if (!ifp) {
        perror("File opening failed\n");
        return;
    }
    fseek(ifp, 10, SEEK_SET);

    int ref_offset;
    fread(&ref_offset, sizeof(int), 1, ifp);

    fseek(ifp, 18, SEEK_SET);

    int ref_width;
    int ref_height;
    fread(&ref_width, sizeof(int), 1, ifp);
    fread(&ref_height, sizeof(int), 1, ifp);

    fseek(ifp, 0, SEEK_SET);

    unsigned char* ref_header = new unsigned char[ref_offset];
    fread(ref_header, sizeof(unsigned char), ref_offset, ifp);
    fclose(ifp);

    FILE* ofp = fopen(out_filename.c_str(), "wb");
    if (!ofp) {
        perror("File opening failed\n");
        delete[] ref_header;
        return;
    }
    fwrite(ref_header, sizeof(unsigned char), ref_offset, ofp);
    delete[] ref_header;

    int size = channels * width * height;
    unsigned char* out_pixels = new unsigned char[size];
    std::copy(pixels, pixels + size, out_pixels);
    if (channels == 3) {
        reverse_each_pixel(width, height, out_pixels);
    }
    fwrite(out_pixels, sizeof(unsigned char), size, ofp);
    delete[] out_pixels;

    fclose(ofp);
}

์‹ค์Šต์‹œ๊ฐ„์— ์ œ๊ณต๋ฐ›์€ bmpfunc.cpp ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•ด ์ข…์ด์— ์ ์šฉ๋œ ํ…์Šค์ฒ˜๋ฅผ BMP ํŒŒ์ผ๋กœ ์ €์žฅํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

// main viewport
glViewport(0, 0, window_width, window_height);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (printing_animate < 0.0f) {
    gluPerspective(45.0, (double) window_width / window_height, 0.1, 100.0);
} else {
    gluPerspective(45.0, (double) window_width / window_height, printing_animate, 50.0);
}

Z near๋ฅผ ์กฐ์ ˆํ•ด ํ”„๋ฆฐํŠธ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„

์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ

void toggle_fullscreen() {
    fullscreen = !fullscreen;
    if (fullscreen) {
        prev_window_width = glutGet(GLUT_WINDOW_WIDTH);
        prev_window_height = glutGet(GLUT_WINDOW_HEIGHT);
        glutFullScreen();
    } else {
        glutReshapeWindow(prev_window_width, prev_window_height);
    }
}

void special_keyboard_cb(int key, int x, int y) {
    switch (key) {
    case GLUT_KEY_F11:
        std::cout << "Toggle fullscreen\n";
        toggle_fullscreen();
    break;
    }
}

glutFullScreen์„ ์ด์šฉํ•ด ํ™”๋ฉด์„ ์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ๋กœ ๋ณ€๊ฒฝ

์ด์ „ ์œˆ๋„์šฐ ํฌ๊ธฐ๋ฅผ ์ €์žฅํ•ด๋‘๊ณ  F11๋ฅผ ํ•œ ๋ฒˆ ๋” ๋ˆ„๋ฅด๋ฉด ์›๋ž˜ ํฌ๊ธฐ๋กœ ๋˜๋Œ์•„์˜ด

ํ•œ๊ธ€ ํ…์ŠคํŠธ ๊ทธ๋ฆฌ๊ธฐ

#include <Windows.h>

HDC hdc; // handle display context

์ „์—ญ๋ณ€์ˆ˜ ์„ ์–ธ

void init_font(HDC& hdc) {
    hdc = wglGetCurrentDC();
    HFONT font = CreateFont(24, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, HANGUL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, TEXT("๋ง‘์€ ๊ณ ๋”•"));
    SelectObject(hdc, font);
}

init ํ•จ์ˆ˜์—์„œ ์ดˆ๊ธฐํ™”

void draw_text(const HDC& hdc, const std::wstring& text) {
    glDisable(GL_LIGHTING);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0.0, 10.0, 10.0, 0.0);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glRasterPos3f(0.5f, 1.0f, 0.0f);
    for (int i = 0; i < text.size(); i += 1) {
        int list = glGenLists(1);
        wglUseFontBitmapsW(hdc, text[i], 1, list);
        glCallList(list);
        glDeleteLists(list, 1);
    }
    glPopMatrix();

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    glMatrixMode(GL_MODELVIEW);
    glEnable(GL_LIGHTING);
}

draw_string์—์„œ ๊ธ€์ž๋ฅผ ๋น„ํŠธ๋งต์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  glCallList๋กœ ํ™”๋ฉด์— ๊ทธ๋ฆผ

ํ๋ธŒ๋งต ๋ฐฐ๊ฒฝ

CubeMap::CubeMap(float size)
: size(size) {
    glGenTextures(1, &texture);
    glEnable(GL_TEXTURE_CUBE_MAP);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    Bitmap image_px{ "res/cubemap/px.bmp" };
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, image_px.get_width(), image_px.get_height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image_px.get_pixels());
    Bitmap image_nx{ "res/cubemap/nx.bmp" };
    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, image_nx.get_width(), image_nx.get_height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image_nx.get_pixels());
    Bitmap image_py{ "res/cubemap/py.bmp" };
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, image_py.get_width(), image_py.get_height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image_py.get_pixels());
    Bitmap image_ny{ "res/cubemap/ny.bmp" };
    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, image_ny.get_width(), image_ny.get_height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image_ny.get_pixels());
    Bitmap image_pz{ "res/cubemap/pz.bmp" };
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, image_pz.get_width(), image_pz.get_height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image_pz.get_pixels());
    Bitmap image_nz{ "res/cubemap/nz.bmp" };
    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, image_nz.get_width(), image_nz.get_height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image_nz.get_pixels());

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
    glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
}