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












+ Recent posts