OpenGL로 외부 이미지파일을 읽어와서 출력할 때

glDrawPixels()함수를 쓰는 방법과 텍스쳐(texture)를 사용하는 방법이 있는데,

glDrawPixels()는 그릴 때마다 메인 메모리에서 픽셀값들을 읽어들이는 반면

텍스쳐는 생성할 때 그래픽 처리 장치의 메모리에 저장되므로 속도가 더 빠르다.

또한 텍스쳐를 그릴 때 확대/축소와 위치 지정 자유롭게 할 수 있다.



먼저 테스트용으로 쓸 이미지 배열을 만든다.

여기서는 텍스쳐 포멧으로 RGBA_8888를 사용할 것인데,

Red, Green, Blue, Alpha(불투명도)에 해당하는 정보들을 각각 8비트(1바이트)씩 사용하는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
int w = 100;
int h = 100;
GLubyte map[100*100*4];
int i,j;
for(i=0; i<h; i++) {
    for(j=0; j<w; j++) { 
        map[w*i*4+j*4+0= 0x99; //Red
        map[w*i*4+j*4+1= 0x99; //Green
        map[w*i*4+j*4+2= 0xCC; //Blue
        map[w*i*4+j*4+3= 0xAA; //Alpha
    }
}
 

cs


100x100 크기의 0x9999CCAA색 사각형을 나타내는 1차원 배열이 생성된다.

(그릴 때는 2차원으로 처리된다.)





알파 채널을 사용하기 위해 GL_BLEND를 활성화하고,

텍스쳐id로 텍스쳐를 생성한 뒤 glBindTexture()로 텍스쳐id와 텍스쳐라이징 대상을 연결한다.


그리고 glTexImage2D()로 텍스쳐로 쓸 GLubyte 배열을 쓴다.


glTexParameteri()로 확대/축소시 텍스쳐 필터를 설정하고

glTexEnvi()로 텍스쳐 환경 설정을 한다.(레퍼런스 참고)

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);


glTexImage2D(
    GL_TEXTURE_2D, 0, GL_RGBA,
    w, h, 0,
    GL_RGBA, GL_UNSIGNED_BYTE,
    map
);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 


텍스쳐의 너비와 높이가 각각 2의 거듭제곱 꼴일 때 높은 성능을 얻을 수 있다.

(메모리에 2의 거듭제곱 단위로 할당되기 때문에..)

OpenGL ES에서는 아예 2의 거듭제곱꼴만 하용하고 있다.

예시) 64x128, 128x128, 256x64 등







이제 텍스쳐를 그리는 부분이다.

여기서는 평면상에 텍스쳐를 그릴 것인데, 먼저 OpenGL의 좌표계에 대해 알아야 한다.


별도의 설정을 하지 않을 시, 화면의 중앙이 원점이 되며 우측 상단으로 갈 수록 x와 y 값이 각각 증가한다.

우측 상단 꼭지점의 좌표는 (1, 1)이다.


텍스쳐 이미지 자체의 좌표는, 이미지의 좌측하단이 원점이며,

이미지의 우측상단이 (1, 1)이다.




glOrtho()함수를 쓰면 좌표계(클리핑 범위)를 재설정할 수 있다. 형식은 아래와 같다.

glOrtho(왼쪽, 오른쪽, 아래, 위, z축 근거리방향, z축 원거리방향)


2D만 고려할 때, glOrtho(0, 너비, 높이, 0 정도로 지정하면

MFC, JS 등에서처럼 좌측상단을 원점으로 쓸 수 있다.


glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glOrtho(0, 창의 너비, 창의 높이, 0, 0, 1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glBindTexture(GL_TEXTURE_2D, texId);

glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex2f(0, 0);
    glTexCoord2f(1.0, 0.0); glVertex2f(w, 0);
    glTexCoord2f(1.0, 1.0); glVertex2f(w, h);
    glTexCoord2f(0.0, 1.0); glVertex2f(0, h);
glEnd();
 



glTexCoord2f()로 그릴 텍스쳐 영역을 지정할 때

텍스쳐가 뒤집혀 있는 상태이므로 아래와 같은 순서로 그린 것이다.




아래는 png 이미지를 로드하여 화면에 두 번 표시해본 화면이다.

이미지를 로드하기 위해 SDL2를 사용했는데, SDL2 대신 glut과 pnglib라이브러리 써도 png 이미지파일을 읽어올 수 있다. (pnglib라이브러리 없이도 BMP파일은 로드 가능.)



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
#pragma comment (lib,"SDL2")
#pragma comment (lib,"SDL2main")
#pragma comment (lib,"SDL2_image")
#pragma comment (lib,"opengl32.lib")
 
#include <cstdio>
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_image.h>
 
using namespace std;
 
 
SDL_Renderer *renderer;
 
 
int main(int argc, char **argv){
    int winWidth = 800;
    int winHeight = 600;
 
    SDL_Window *win = NULL;
    SDL_Renderer *renderer = NULL;
    SDL_Surface *image;
    SDL_RWops *rwop;
    rwop = SDL_RWFromFile("../res/img.png""rb");
    image = IMG_LoadPNG_RW(rwop);
    GLubyte *map = (GLubyte*)image->pixels;
 
    int w, h; 
    w = image->w;
    h = image->h;
 
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
        return 1;
 
    win = SDL_CreateWindow("gl"100100, winWidth, winHeight, SDL_WINDOW_OPENGL);
    renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
 
    SDL_GLContext context;
    context = SDL_GL_CreateContext(win);
 
    
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glEnable(GL_TEXTURE_2D);
 
    GLuint texId;
    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexImage2D(
        GL_TEXTURE_2D, 0, GL_RGBA,
        w, h, 0,
        GL_RGBA, GL_UNSIGNED_BYTE,
        map
    );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
 
    while (1) {
        SDL_Event e;
        if (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT)
                break;
            else if (e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE)
                break;
        }
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glOrtho(0, winWidth, winHeight, 0-11);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        
        glBindTexture(GL_TEXTURE_2D, texId);
 
        glBegin(GL_QUADS);
            glTexCoord2f(0.00.0); glVertex2f(00);
            glTexCoord2f(1.00.0); glVertex2f(w, 0);
            glTexCoord2f(1.01.0); glVertex2f(w, h);
            glTexCoord2f(0.01.0); glVertex2f(0, h);
        glEnd();
 
        glBegin(GL_QUADS);
            glTexCoord2f(0.00.0); glVertex2f(0+300+30);
            glTexCoord2f(1.00.0); glVertex2f(w+300+30);
            glTexCoord2f(1.01.0); glVertex2f(w+30, h+30);
            glTexCoord2f(0.01.0); glVertex2f(0+30, h+30);
        glEnd();
 
        
        SDL_GL_SwapWindow(win);
        SDL_Delay(21);
 
    }
 
    SDL_GL_DeleteContext(context);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);
 
    return 0;
 
}
 
 
cs












SDL


VS2013에서 SDL2와 SDL_Image, openGL을 사용하기 위해

개발환경을 설정하는 단계를 포스팅해봅니다.




1 .SDL 홈페이지(https://www.libsdl.org)에서 SDL2 다운로드 페이지로 들어간다.

2. Windows용Development Libraries zip 파일을 다운받아서 압축을 푼다

3. 비주얼스튜디오를 열고 win32 콘솔 프로그램 프로젝트를 만든다.

4. 프로젝트 속성 창을 열고 Configuration Properties의 VC++ Directories로 들어간다.




5. Include Directories 항목에는 SDL2의 헤더파일(.h)이 있는 폴더를 지정하고,

   Library Directories 항목에는 SDL2의 lib파일이 있는 폴더를 지정한다.

  여기서 32비트와 64비트가 있는데 32비트로 진행하였다.


6. 이제 링커 설정에서 라이브러리 의존성을 지정해야 한다.

  5번과 같은 속성창에서 Linker - Input - Additional Dependencies에

라이브러리 파일의 이름을 추가해도 되지만 여기서는 소스코드에 직접 넣었다.

1
2
3
4
5
6
7
8
9
#pragma comment (lib,"SDL2")
#pragma comment (lib,"SDL2main")
 
#include <SDL.h>

cs

소스코드의 윗부분에 #pragma comment로 lib파일을 쓰면 된다.

SDL2.lib과 SDL2main.lib을 추가한다.



경우에 따라 라이브러리파일의 이름이 SDL2.lib이 아니라 SDL.lib일 수도 있으니 주의.





7. 이제 SDL의 이미지파일 처리 등을 도와주는 SDL_Image 라이브러리를 설치한다.

 https://www.libsdl.org/projects/SDL_image/에서

2번과 같이 Development Libraries zip파일을 받는다.

5번에서처럼 Include Directories와 Library Directories를 각각 추가한다.

*다른 SDL 관련 추가 라이브러리도 이와 같은 방법으로 추가하면 된다.





8. 사용할 라이브러리와 헤더파일들을 소스코드에 명시한다.

opengl함수들을 직접 사용하기 위해서는 opengl32.lib 파일을 링크해야 한다.

1
2
3
4
5
6
7
8
9
#pragma comment (lib,"SDL2")
#pragma comment (lib,"SDL2main")
#pragma comment (lib,"SDL2_image")
#pragma comment (lib,"opengl32.lib")
 

#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_image.h>
cs




※ 실행시 SDL.dll을 찾을 수 없다는 오류가 난다면

다운받은 라이브러리 폴더에 있는 SDL2.dll과 SDL2_image.dll을 복사하여

프로젝트 폴더 내의 Debug 혹은 Release폴더에 넣는다.




아래는 간단하게 작성해본 테스트용 코드..

프로젝트 디렉토리에 res 라는 폴더가 있고

그 폴더에 있는 img.png 파일(512x512)을 로드하여 화면에 표시한다.


(SDL의 함수들만 사용해도 이미지를 띄울 수 있지만

opengl함수들을 사용해보기 위해 아래와 같이 작성하였습니다.)


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
#pragma comment (lib,"SDL2")
#pragma comment (lib,"SDL2main")
#pragma comment (lib,"SDL2_image")
#pragma comment (lib,"opengl32.lib")
 
#include <cstdio>
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_image.h>
 
using namespace std;
 
int main(int argc, char **argv){
 
    SDL_Window *win = NULL;
    SDL_Surface *image;
    SDL_RWops *rwop;
    rwop = SDL_RWFromFile("../res/img.png""rb");
    image = IMG_LoadPNG_RW(rwop);
    GLubyte *map = (GLubyte*)image->pixels;
 
    //Initialize SDL.
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
        return 1;
 
    //Create the window
    win = SDL_CreateWindow("SDL_GL"100100400300, SDL_WINDOW_OPENGL);
 
    SDL_GLContext context;
    context = SDL_GL_CreateContext(win);
 
    glEnable(GL_TEXTURE_2D);
    GLuint texId;
    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexImage2D(
        GL_TEXTURE_2D, 0, GL_RGBA,
        5125120,
        GL_RGBA, GL_UNSIGNED_BYTE,
        map
    );
    
    // Texture filter
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
    // main loop
    while (1) {
 
        //event handling
        SDL_Event e;
        if (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT)
                break;
        }
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
        glBindTexture(GL_TEXTURE_2D, texId);
        glBegin(GL_QUADS);
        glTexCoord2f(0.00.0); glVertex2f(-0.71.0);
        glTexCoord2f(1.00.0); glVertex2f(0.71.0);
        glTexCoord2f(1.01.0); glVertex2f(0.7-1.0);
        glTexCoord2f(0.01.0); glVertex2f(-0.7-1.0);
        glEnd();
 
        SDL_GL_SwapWindow(win);
        SDL_Delay(16);
 
    }
 
    SDL_GL_DeleteContext(context);
    SDL_DestroyWindow(win);
SDL_Quit();
    return 0;
 
}
 
 
cs




실행결과








자세한 코드 내용은 다음 포스트에 계속돱니다

http://tibyte.kr/232







+ Recent posts