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) |
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) |
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 |
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(0, 0, 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(NULL, 2000, 0, 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 |
'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 |