반응형

OpenGL 프로그래밍을 위한 준비과정은 다음 포스팅을 참고하세요.

[그래픽스&컴퓨터비전/Augmented Reality] - OpenGL( freeGLUT ) 을 Visual Studio 2015에 연동하기

[그래픽스&컴퓨터비전/Augmented Reality] - Ubuntu 16.04에서 OpenGL( freeGLUT ) 프로그래밍



OpenGL에서 윈도우의 사이즈를 다음처럼 지정해주어 생성합니다.

1
glutInitWindowSize(500500);   //윈도우의 width와 height

cs


그래픽스에선 보통 왼쪽 상단에 원점이 있는 2차원 좌표계를 사용합니다. 그래서 아래로 갈수록 Y값이 증가하게 됩니다.



OpenGL에서는 Normalized Device Coordinates(NDC)를 사용합니다.  Y값이 위로 올라갈수록 커지며  중앙에 원점 (0,0)이 위치합니다. 좌표로 실수값을 사용하며  X와 Y의 범위가 각각 [-1, 1]입니다.  3차원인 경우에는 Z축의 범위도 [-1, 1]이고 화면 안쪽으로 갈수록 감소합니다.


glViewport함수로 viewport를 지정하면 screen coordinates로 변환할 수 있습니다.



한변의 길이가 1인 빨간색 사각형의 중앙이 원점(0,0)에 오도록해서 그리는 예제입니다. 주의 할 점은 사각형을 구성하는 네 개의 vertex를 지정해줄 때 반드시 반시계 방향으로 차례대로 지정해줘야 합니다.

1
2
3
4
5
6
    glBegin(GL_QUADS); //4점이 하나의 사각형을 구성한다. 반시계 방향으로 4점의 vertex를 지정해줘야 한다.
    glVertex2f(-0.5f, -0.5f);    // x, y
    glVertex2f(0.5f, -0.5f);
    glVertex2f(0.5f, 0.5f);
    glVertex2f(-0.5f, 0.5f);
    glEnd();
cs






코드는 윈도우 기준으로 되어 있습니다. 리눅스의 경우에는 헤더파일을 다음처럼 지정하고 라이브러리 지정해 준 줄들을 삭제해주면 됩니다.

1
#include "GL/freeglut.h"
cs



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//상대경로로 헤더파일을 지정합니다.
#include ".\include\GL\freeglut.h"
#include <iostream>
 
//사용할 라이브러리를 지정해줍니다.
#pragma comment(lib, "freeglut.lib")
#pragma comment(lib, "glew32.lib")
 
using namespace std;
 
 
void keyboard(unsigned char key, int x, int y)
{
    cout << "다음 키가 눌러졌습니다. \"" << key << "\" ASCII: " << (int)key << endl;
 
    //ESC 키가 눌러졌다면 프로그램 종료
    if (key == 27)
    {
        exit(0);
    }
}
 
 
void drawBitmapText(char *str, float x, float y, float z)
{
    glRasterPos3f(x, y, z); //문자열이 그려질 위치 지정
 
    while(*str)
    {
        //GLUT_BITMAP_TIMES_ROMAN_24 폰트를 사용하여 문자열을 그린다.
        glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *str);
 
        str++;
    }
}
 
 
void display() {
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //glClear에서 컬러 버퍼 지운 후 윈도우를 채울 색을 지정, 검은색
    glClear(GL_COLOR_BUFFER_BIT);         //컬러 버퍼를 지운다.
    glLoadIdentity();
 
    
    //중앙이 원점에 오도록 한변의 길이가 1인 빨간색 정사각형을 그린다. 
    glColor3f(1.0f, 0.0f, 0.0f); //빨간색 지정
    glBegin(GL_QUADS); //4점이 하나의 사각형을 구성한다. 반시계 방향으로 4점의 vertex를 지정해줘야 한다.
    glVertex2f(-0.5f, -0.5f);    // x, y
    glVertex2f(0.5f, -0.5f);
    glVertex2f(0.5f, 0.5f);
    glVertex2f(-0.5f, 0.5f);
    glEnd();
 
    glColor3f(1.0f, 1.0f, 1.0f);//흰색 지정
    drawBitmapText("-0.5, -0.5"-0.5f, -0.5f, 0.0f);// 지정한 좌표에 문자열 출력
    drawBitmapText("0.5, -0.5"0.5f, -0.5f, 0.0f);
    drawBitmapText("0.5, 0.5"0.5f, 0.5f, 0.0f);
    drawBitmapText("-0.5, 0.5"-0.5f, 0.5f, 0.0f);
    
    //더블 버퍼링을 하고 있다면, 프론트 버퍼와 백 버퍼 2개가 사용된다.현재 화면에 보여지는 것은 프론트 버퍼에 있는 내용이다.
    //백 버퍼는 다음 장면을 위해 렌더링을 하고 있는 곳이다.백 버퍼의 렌더링이 완료되면 두 개의 버퍼를 교환(swap)한다.
    //화면에 업데이트된 프론트 버퍼에 있는 내용이 출력된다.
 
    //싱글 버퍼라면 버퍼에 있는 것을 화면에 출력한다.
    glutSwapBuffers();
}
 
 
int main(int argc, char** argv) 
{
    glutInit(&argc, argv);  //GLUT 초기화
 
    glutInitWindowSize(500500);   //윈도우의 width와 height
    glutInitWindowPosition(100100); //윈도우의 위치 (x,y)
    glutCreateWindow("OpenGL Example"); //윈도우 생성
 
    //디스플레이 콜백 함수 등록, display함수는 윈도우 처음 생성할 때와 화면 다시 그릴 필요 있을때 호출된다. 
    glutDisplayFunc(display); 
    //키보드 콜백 함수 등록, 키보드가 눌러지면 호출된다. 
    glutKeyboardFunc(keyboard);
 
    //GLUT event processing loop에 진입한다.
    //이 함수는 리턴되지 않기 때문에 다음줄에 있는 코드가 실행되지 않는다. 
    glutMainLoop();          
 
    return 0;
}
cs



윈도우의 크기를 조정하면 정사각형의 모양이 왜곡되는 것을 볼 수 있습니다. 


윈도우의 크기 조정시 호출되는 reshape 콜백 함수에서  변경된 윈도우 크기의 aspect ratio를 참고하여 orthographic projection를 적용시켜주면 해결됩니다.  


orthographic projection은 원근이 있지만 거리가 멀어져도 정면에서는 항상 동일한 크기로 보이게 해줍니다.  참고로 perspective  projection은 거리가 멀어질 수록 정면에서 봤을때 작게 보이게 만듭니다. 

이 둘의 차이는 https://wiki.blender.org/index.php/Doc:KO/2.4/Manual/3D_interaction/Navigating/3D_View 에서 가져온 아래 이미지를 보면 이해가 될듯합니다. 



윈도우 크기 조정시 왜곡되는 문제를 해결한 코드입니다. 굵은 글씨가 추가된 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//상대경로로 헤더파일을 지정합니다.
#include ".\include\GL\freeglut.h"
#include <iostream>
 
//사용할 라이브러리를 지정해줍니다.
#pragma comment(lib, "freeglut.lib")
#pragma comment(lib, "glew32.lib")
 
using namespace std;
 
 
void keyboard(unsigned char key, int x, int y)
{
    cout << "다음 키가 눌러졌습니다. \"" << key << "\" ASCII: " << (int)key << endl;
 
    //ESC 키가 눌러졌다면 프로그램 종료
    if (key == 27)
    {
        exit(0);
    }
}
 
 
void drawBitmapText(char *str, float x, float y, float z)
{
    glRasterPos3f(x, y, z); //문자열이 그려질 위치 지정
 
    while(*str)
    {
        //GLUT_BITMAP_TIMES_ROMAN_24 폰트를 사용하여 문자열을 그린다.
        glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *str);
 
        str++;
    }
}
 
 
void display() 
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //glClear에서 컬러 버퍼 지운 후 윈도우를 채울 색을 지정, 검은색
    glClear(GL_COLOR_BUFFER_BIT);         //컬러 버퍼를 지운다.
    glLoadIdentity();
 
    
    //중앙이 원점에 오도록 한변의 길이가 1인 빨간색 정사각형을 그린다. 
    glColor3f(1.0f, 0.0f, 0.0f); //빨간색 지정
    glBegin(GL_QUADS); //4점이 하나의 사각형을 구성한다. 반시계 방향으로 4점의 vertex를 지정해줘야 한다.
    glVertex2f(-0.5f, -0.5f);    // x, y
    glVertex2f(0.5f, -0.5f);
    glVertex2f(0.5f, 0.5f);
    glVertex2f(-0.5f, 0.5f);
    glEnd();
 
    glColor3f(1.0f, 1.0f, 1.0f);//흰색 지정
    drawBitmapText("-0.5, -0.5"-0.5f, -0.5f, 0.0f);// 지정한 좌표에 문자열 출력
    drawBitmapText("0.5, -0.5"0.5f, -0.5f, 0.0f);
    drawBitmapText("0.5, 0.5"0.5f, 0.5f, 0.0f);
    drawBitmapText("-0.5, 0.5"-0.5f, 0.5f, 0.0f);
    
    //더블 버퍼링을 하고 있다면, 프론트 버퍼와 백 버퍼 2개가 사용된다.현재 화면에 보여지는 것은 프론트 버퍼에 있는 내용이다.
    //백 버퍼는 다음 장면을 위해 렌더링을 하고 있는 곳이다.백 버퍼의 렌더링이 완료되면 두 개의 버퍼를 교환(swap)한다.
    //화면에 업데이트된 프론트 버퍼에 있는 내용이 출력된다.
 
    //싱글 버퍼라면 버퍼에 있는 것을 화면에 출력한다.
    glutSwapBuffers();
}
 
 
void reshape(GLsizei width, GLsizei height) 
{  
    if (height == 0) height = 1;                // 0으로 나누는 것 방지
    GLfloat aspect = (GLfloat)width / (GLfloat)height;
 
    //변경된 윈도우크기로 viewport를 설정한다.
    glViewport(00, width, height);
 
    glMatrixMode(GL_PROJECTION);  //뒤에 오는 계산들은 Projection matrix에 영향을 주도록 설정
    glLoadIdentity();             //projection matrix 초기화
    if ( width >= height) {
        // aspect >= 1 이면, height를 [-1,1]로 설정하고 width는 [-1*aspect, 1*aspect]로 설정한다.
        // left, right, top, bottom
        gluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.01.0);
    }
    else {
        // aspect < 1이면, width를 [-1,1]로 설정하고 height를  [-1/aspect, 1/aspect]로 설정한다.
        gluOrtho2D(-1.01.0-1.0 / aspect, 1.0 / aspect);
    }
 
    //뒤에 오는 계산들은 Modelview Matrix에 영향을 주도록 설정한다. 
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity(); //Modelview matrix 초기화
}
 
 
int main(int argc, char** argv) 
{
    glutInit(&argc, argv);  //GLUT 초기화
 
    glutInitWindowSize(500500);   //윈도우의 width와 height
    glutInitWindowPosition(100100); //윈도우의 위치 (x,y)
    glutCreateWindow("OpenGL Example"); //윈도우 생성
 
    //디스플레이 콜백 함수 등록, display함수는 윈도우 처음 생성할 때와 화면 다시 그릴 필요 있을때 호출된다. 
    glutDisplayFunc(display); 
    //키보드 콜백 함수 등록, 키보드가 눌러지면 호출된다. 
    glutKeyboardFunc(keyboard);
    //reshape 콜백 함수 등록, reshape함수는 윈도우 처음 생성할 때와 윈도우 크기 변경시 호출된다.
    glutReshapeFunc(reshape);
 
    //GLUT event processing loop에 진입한다.
    //이 함수는 리턴되지 않기 때문에 다음줄에 있는 코드가 실행되지 않는다. 
    glutMainLoop();          
 
    return 0;
}
cs






반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts