Software Development/Graphics

OpenGL Texture 그리기

huiyu 2018. 8. 4. 15:53

SDL과 OpenGL을 이용한 텍스쳐 그리기

소스코드 :
https://github.com/huiyueun/GraphicsStudy/tree/master/texture_sample


먼저 SDL_LoadBMP() API를 통해 그림파일(bmp)을 로드한다.
(다른 확장자를 로드하려면 SDL_Image 활용)

1
2
SDL_Surface* image_surface;
image_surface = SDL_LoadBMP("./cat512.bmp");
cs

SDL_Surface는 화면에 그려질 이미지 정보를 담고 있게 된다.
이 데이터 정보를 갖고 있어, 이 데이터 정보를 통해 gl로 그리면 된다.

SDL_Surface Data Fields

Uint32

flags

(internal use)

SDL_PixelFormat*

format

the format of the pixels stored in the surface; see SDL_PixelFormat for details (read-only)

int

w, h

the width and height in pixels (read-only)

int

pitch

the length of a row of pixels in bytes (read-only)

void*

pixels

the pointer to the actual pixel data; see Remarks for details (read-write)

void*

userdata

an arbitrary pointer you can set (read-write)

int

locked

used for surfaces that require locking (internal use)

void*

lock_data

used for surfaces that require locking (internal use)

SDL_Rect

clip_rect

an SDL_Rect structure used to clip blits to the surface which can be set by SDL_SetClipRect() (read-only)

SDL_BlitMap*

map

info for fast blit mapping to other surfaces (internal use)

int

refcount

reference count that can be incremented by the application


기존 GL을 통해 직사각형을 그리는 샘플에 텍스쳐를 입혀보도록 하겠다.

SDL_LoadBMP()를 통해 읽어온 데이터를 쉐이더로 전달해 그려주면 된다.
화면에 텍스쳐를 그리기 위해선 텍스쳐 좌표와 이미지를 그릴 실제 이미지 데이터가 필요하다.

2D 텍스쳐의 경우 텍스쳐 좌표의 범위는 x/y축 각각 0~1사이이며, 아래와 같다.

직사각형에 이미지를 그릴 것이므로 텍스쳐 좌표는 아래와 같이 선언해주면 된다.
위 그림의 x,y 좌표.

1
2
3
4
5
6
7
GLfloat vCoord[8= {
         1.0f, 1.0f,
         0.0f, 1.0f,
         0.0f, 0.0f,
         1.0f, 0.0f
        };
 
cs

선언한 텍스쳐 좌표 vCoord값을 이제 vertex shader로 넘겨주고, 다시 fragment shader로 넘겨주어 화면에 텍스쳐를 그려주면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const GLchar vShaderStr[] =
        "attribute vec4 vVertices;\n"
        "uniform mat4 modelMatrix;\n"
        "attribute vec2 vCoord;\n"
        "varying vec2 Coord;\n"
        "void main()\n"
        "{\n"
        "    gl_Position = modelMatrix * vVertices;\n"
        "    Coord = vCoord;\n"
        "}\n";
 
const GLchar fShaderStr[] =
        "precision mediump float;\n"
        "uniform sampler2D s_texture;\n"
        "varying vec2 Coord;\n"
        "void main()\n"
        "{\n"
        "    vec4 tex = texture2D(s_texture, Coord);\n"
        "    gl_FragColor = vec4(tex.b, tex.g, tex.r, 0);\n;"
        "}\n";
 
cs

아래는 쉐이더에 "vCood"에 선언한 텍스쳐 좌표를 바인딩 시키는 코드이다.
1
2
3
glBindAttribLocation(ad->program, COORD_IDX, "vCoord");
glVertexAttribPointer(COORD_IDX, 2, GL_FLOAT, GL_FALSE, 0, vCoord);
glEnableVertexAttribArray(COORD_IDX);
cs

- glBindAttribLocation(): shader내의 "vCoord"위치값을 COORD_IDX(2) 인덱스와 연결시킨다.
- glVertexAttribPointer(): 바인드 된 COORD_IDX(2)값에 선언한 vCoord(텍스쳐 좌표)값을 넘겨준다.(버텍스 쉐이더로 전달)
- glEnableVertexAttribArray : Enable Vertex IDX


전달된 vCoord값은 fragment shader로 전달되어 아래와 같이 텍스쳐 좌표를 만들게 된다.

1
vec4 tex = texture2D(s_texture, Coord);
cs

s_texture로 넘어오는 실제 텍스쳐 이미지는 아래 gl call들에 의해 업로드 된다.

1
2
3
4
5
6
7
8
9
10
11
    glGenTextures(1&(ad->textureID));
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, ad->textureID);
 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, image_surface->pixels);
 
cs

- glGenTextures() : Texture ID 생성
- glActiveTexture() : Texture 활성화
- glBindTexture() : 생성한 Texture ID에 Texture 바인딩
- glTexParaperti() : 업로드 Texture 옵션값 설정
  1) GL_TEXTURE_WRAP_S/GL_TEXTURE_WRAP_T : Vertex 정점의 경계 지점을 부드럽게 보간시켜주는 GL_CLAMP_TO_EDGE와 텍스쳐를 반복시킬 GL_REPEAT를 설정
  2) GL_TEXTURE_MAG_FILTER/GL_TEXUTRE_MIN_FILTER : 축소/확대필터
   (GL_NEAREST : 인접 축소필터, GL_LINEAR : 양방향 선형 필터, GL_LINEAR_MIPMAP_LINEAR : 삼중 선형 필터링)

- glTexImage2D()
  : 텍스쳐 이미지 배열을 GPU (Shader)로 업로드

출력해보면 아래와 같이 이미지가 정상적으로 직사각형에 업로드 된걸 확인할 수 있다.

아래는 전체 소스코드

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#include <SDL.h>
#include <GL/gl.h>
SDL_Surface* surface;
 
#define VERTEX_IDX 0
#define COORD_IDX 1
#define WIDTH 512
#define HEIGHT 512
 
typedef struct AppData {
    SDL_Window* window;
    SDL_GLContext gl;
 
    GLuint program;
    GLuint textureID;
} AppData;
 
GLfloat matrix[4][4= {
    {1.0f, 0.0f, 0.0f, 0.0f},
    {0.0f, 1.0f, 0.0f, 0.0f},
    {0.0f, 0.0f, 1.0f, 0.0f},
    {0.0f, 0.0f, 0.0f, 1.0f},
};
 
GLfloat vVertices[12= {
    -1.0-1.0,
     1.0-1.0,
     1.0,  1.0,
    -1.0,  1.0
};
 
GLfloat vCoord[8= {
     1.0f, 1.0f,
     0.0f, 1.0f,
     0.0f, 0.0f,
     1.0f, 0.0f
    };
 
static const unsigned int cube_elements[] = {
     0,1,2,
     0,2,3
};
 
const GLchar vShaderStr[] =
    "attribute vec4 vVertices;\n"
    "uniform mat4 modelMatrix;\n"
    "attribute vec2 vCoord;\n"
    "varying vec2 Coord;\n"
    "void main()\n"
    "{\n"
    "     gl_Position = modelMatrix * vVertices;\n"
    "     Coord = vCoord;\n"
    "}\n";
 
const GLchar fShaderStr[] =
    "precision mediump float;\n"
    "uniform sampler2D s_texture;\n"
    "varying vec2 Coord;\n"
    "void main()\n"
    "{\n"
    "  vec4 tex = texture2D(s_texture, Coord);\n"
    "  gl_FragColor = vec4(tex.b, tex.g, tex.r, 0);\n;"
    "}\n";
 
GLuint LoadShader(const char *shaderSrc, GLenum type)
{
    GLint compiled;
    GLuint shader = glCreateShader(type);
 
    if (shader == 0)
        return 0;
    glShaderSource(shader, 1&shaderSrc, NULL);
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (!compiled)
    {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1)
        {
            char* infoLog = (char*)(malloc(sizeof(char* infoLen));
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            free(infoLog);
        }
        glDeleteShader(shader);
        return 0;
    }
    return shader;
 
}
 
GLuint compile_shaders(void)
{
    GLuint vertex_shader;
    GLuint fragment_shader;
    GLuint program;
 
 
    vertex_shader = LoadShader(vShaderStr, GL_VERTEX_SHADER);
    fragment_shader = LoadShader(fShaderStr, GL_FRAGMENT_SHADER);
 
    program = glCreateProgram();
    glAttachShader(program, vertex_shader);
 
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);
 
    glUseProgram(program);
 
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glEnable(GL_DEPTH_TEST);
 
    glDeleteShader(vertex_shader);
    glDeleteShader(fragment_shader);
 
    return program;
}
 
 
void GLInit(AppData* ad)
{
    ad->program = compile_shaders();
 
    glBindAttribLocation(ad->program, VERTEX_IDX, "vVertices");
    glBindAttribLocation(ad->program, COORD_IDX, "vCoord");
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glVertexAttribPointer(VERTEX_IDX, 2, GL_FLOAT, GL_FALSE, 0, vVertices);
    glVertexAttribPointer(COORD_IDX, 2, GL_FLOAT, GL_FALSE, 0, vCoord);
    glEnableVertexAttribArray(VERTEX_IDX);
    glEnableVertexAttribArray(COORD_IDX);
 
    glGenTextures(1&(ad->textureID));
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, ad->textureID);
 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
 
    GLuint uniformLocation = glGetUniformLocation(ad->program, "modelMatrix");
    glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, (GLfloat *)matrix);
 
    unsigned int ibo;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_elements), cube_elements, GL_STATIC_DRAW);
 
 
}
 
void drawGL(AppData* ad)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glViewport(00, WIDTH, HEIGHT);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glFinish();
}
 
 
int initGL(AppData* ad)
{
    ad->gl = SDL_GL_CreateContext(ad->window);
    if (ad->gl == NULL) {
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[SDL] GL context creation failed!");
        SDL_Quit();
        return (-1);
    }
 
    SDL_GL_MakeCurrent(ad->window, ad->gl);
    return (0);
}
 
int
main(int argc, char **argv)
{
    AppData ad = {0,};
    if (0 != SDL_Init(SDL_INIT_EVERYTHING)) {
       SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[SDL] Initialize Failed");
       return -1;
    }
    ad.window = SDL_CreateWindow(NULL20000, WIDTH, HEIGHT,    SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
    if (ad.window == 0) {
            SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[SDL] Failed to create Window");
            SDL_Quit();
            return -1;
    }
    surface = SDL_LoadBMP("./cat512.bmp");
    initGL(&ad);
 
    GLInit(&ad);
    SDL_bool isQuit = SDL_FALSE;
    while(!isQuit) {
        SDL_Event event;
        if(SDL_PollEvent(&event)) {
            switch(event.type)
            {
            case SDL_QUIT:
            isQuit = SDL_TRUE;
            break;
            }
        }
        drawGL(&ad);
        SDL_GL_SwapWindow(ad.window);
    }
    SDL_Quit();
    return 0;
}
cs



728x90

'Software Development > Graphics' 카테고리의 다른 글

프레임 드롭현상과 대책  (3) 2018.08.18
프레임과 FPS  (0) 2018.08.04
OpenGL IBO를 사용한 큐브 그리기  (0) 2018.07.22
OpenGL IBO를 사용한 직사각형 그리기  (0) 2018.07.22
OpenGL VBO로 큐브 그리기  (0) 2018.07.07