OpenGL

OpenGL: Modern OpenGL Tutorial Text Rendering 02

FunnyPR 2014. 5. 20. 21:55

■ OpenGL: Modern OpenGL Tutorial Text Rendering 02[1]


FreeType 라이브러리로 폰트를 그리는 두번째 파트 

1. Introduction

이전 파트에서 우리는 각 폰트를 그리기 위한 텍스처를 만들어 그래픽 카드에 옵로딩 하였다. 

 - 요러한 방식이 비효율적이라고 한다.

 - 새롭게 계속 텍스쳐를 그래픽 카드에 올리는 것 보다는 전체 폰트를 그래픽 카드에 옵로드 했다가 쓰는 것이 더 효율적이라고 함.


method 1:

 - 간단한 방법으로 각 폰트에 대한 많은 텍스처를 가지는 것.

 - 즉 각 폰트에 대한 텍스처를 가지고 있다가 quad를 그릴때 텍스처만 바꾸어 주는 방식으로 간다.

 - 하지만 이와 같은 방법은 여러 폰트를 그릴때 엄청나게 많이 반복해서 quad를 그려줘야 함


method 2:

 - 덜 복잡한 방법은 왕 큰 하나의 텍스처에 모든 폰트의 값을 저장하고 있는 거지,

 - 그래서 각 폰트에 대한 텍스처 좌표와 quad의 버텍스를 매핑시키는 거야

 - 이 방법에서는 " texture atlas" 개념을 알고 있어야 한다고 함


texture atlas: 텍스처 아틀라스(atlas:지도책)

여러 이미지를 하나의 파일에 모아놓은 텍스처를 Atlas Texture라고하며, 일반적으로 툴없이

 포토샵으로 하게 될 경우 수정 및 텍스처 공간활용, 좌표 추출 등에서 많은 애를 먹게 된다.


2. texture atlas만들기 

텍스처 아틀라스는 여러 꼬맹이 이미지들이 하나의 패키지처럼 모여 있는 엄마 텍스처라고 생각하면 됨

- 꼬맹이 이미지들이 동일한 사이즈면 만들기 쉽다넹.

- 하지만 뚜둥 폰트 사이즈는 각각 달라!!!

- 텍스처 아틀라스를 만드는 많은 방법이 있지만 여기서는 간단한 방법을 사용한다고 함(휴 *_*)

- 텍스처 아틀라스를 만들기 전에 미리 폰트의 가로 세로 사이즈 등을 미리 알고 있어야 함


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FT_GlyphSlot g = face->glyph;
int w = 0;
int h = 0;
 
for(int i = 32; i < 128; i++) {
  if(FT_Load_Char(face, i, FT_LOAD_RENDER)) {
    fprintf(stderr, "Loading character %c failed!\n", i);
    continue;
  }
 
  w += g->bitmap.width;
  h = std::max(h, g->bitmap.rows);
 
  /* you might as well save this value as it is needed later on */
  int atlas_width = w;
}

텍스처 아틀라스를 만드는 코드당. FT_GlyphSlot g 변수를 통해 한 폰트에 대한 정보를 가져옴 이 폰트의 사이즈를 미리 알고 ASCII 코드 32번째 부터 쭈우우욱 값을 읽어드림

 - 또한 g에 대한 정보로 아틀라스 사이즈 w, h를 구함 

다음으로는 이 아틀라스 사이즈를 가지고 하나의 빈 텍스처를 만듬(하나씩 찍기 위함이지)


1
2
3
4
5
6
7
GLuint tex;
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);


이제 텍스처 아틀라스에 glyph images를 붙여 넣은 준비가 됬고 우리는 glTexSubImage2D()함수로 글립 이미지를 간단하게 텍스처 아틀라스에 넣을 수 있음등!!


1
2
3
4
5
6
7
8
9
10
int x = 0;
 
for(int i = 32; i < 128; i++) {
  if(FT_Load_Char(face, i, FT_LOAD_RENDER))
    continue;
 
  glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
 
  x += g->bitmap.width;
}


3. Caching glyph metrics and texture offsets

자 그럼 폰트를 아틀라스 텍스쳐에서 어떻게 가져와야 함!!!

정확한 위치에 폰트를 가지고 올라면 좌표(x,y) 그리고 x offset 그러니깐 몇행인지 그리고 폰트의 정보가 필요함

이 정보를 기억하기 위해 다음과 같은 구조체를 만듬, 이 구조체에 위 필요한 정보를 기억하여 캐쉬메모리로 사용해서 필요할때 마다 찾아 갈꺼얌


1
2
3
4
5
6
7
8
9
10
11
12
struct character_info {
  float ax; // advance.x
  float ay; // advance.y
 
  float bw; // bitmap.width;
  float bh; // bitmap.rows;
 
  float bl; // bitmap_left;
  float bt; // bitmap_top;
 
  float tx; // x offset of glyph in texture coordinates
} c[128];


텍스처에 glyph images를 넣는 루프에서 다음과 같이 수행해서 구조체에 정보를 쏘옥~

1
2
3
4
5
6
7
8
9
10
  c[*p].ax = g->advance.x >> 6;
  c[*p].ay = g->advance.y >> 6;
 
  c[*p].bw = g->bitmap.width;
  c[*p].bh = g->bitmap.rows;
 
  c[*p].bl = g->bitmap_left;
  c[*p].bt = g->bitmap_top;
 
  c[*p].tx = (float)x / w;


4. Rendering lines of text using the atlas

실제 텍스쳐 그리는 소스는 다음과 같다구웃!

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
void render_text(const char *text, atlas * a, float x, float y, float sx, float sy) {
    const uint8_t *p;
 
    /* Use the texture containing the atlas */
    glBindTexture(GL_TEXTURE_2D, a->tex);
    glUniform1i(uniform_tex, 0);
 
    /* Set up the VBO for our vertex data */
    glEnableVertexAttribArray(attribute_coord);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(attribute_coord, 4, GL_FLOAT, GL_FALSE, 0, 0);
 
    point coords[6 * strlen(text)];
    int c = 0;
 
    /* Loop through all characters */
    for (p = (const uint8_t *)text; *p; p++) {
        /* Calculate the vertex and texture coordinates */
        float x2 = x + a->c[*p].bl * sx;
        float y2 = -y - a->c[*p].bt * sy;
        float w = a->c[*p].bw * sx;
        float h = a->c[*p].bh * sy;
 
        /* Advance the cursor to the start of the next character */
        x += a->c[*p].ax * sx;
        y += a->c[*p].ay * sy;
 
        /* Skip glyphs that have no pixels */
        if (!w || !h)
            continue;
 
        coords[c++] = (point) {
        x2, -y2, a->c[*p].tx, a->c[*p].ty};
        coords[c++] = (point) {
        x2 + w, -y2, a->c[*p].tx + a->c[*p].bw / a->w, a->c[*p].ty};
        coords[c++] = (point) {
        x2, -y2 - h, a->c[*p].tx, a->c[*p].ty + a->c[*p].bh / a->h};
        coords[c++] = (point) {
        x2 + w, -y2, a->c[*p].tx + a->c[*p].bw / a->w, a->c[*p].ty};
        coords[c++] = (point) {
        x2, -y2 - h, a->c[*p].tx, a->c[*p].ty + a->c[*p].bh / a->h};
        coords[c++] = (point) {
        x2 + w, -y2 - h, a->c[*p].tx + a->c[*p].bw / a->w, a->c[*p].ty + a->c[*p].bh / a->h};
    }
 
    /* Draw all the character on the screen in one go */
    glBufferData(GL_ARRAY_BUFFER, sizeof coords, coords, GL_DYNAMIC_DRAW);
    glDrawArrays(GL_TRIANGLES, 0, c);
 
    glDisableVertexAttribArray(attribute_coord);
}


................\wikibooks-opengl-modern-tutorials\text02_atlas

위 폴더에서 text.cpp 파일을 실행시키면 되는데 

point coords[6 * strlen(text)]; 아이에서 배열 상수식 선언 에러 나올꺼야. 또 

coords[c++] 요라인도 

고칠라면 point coords 배열을 동적배열로 사용하고 

구조체 접근 또한 coords[c].x = x2; 요러한 형식으로 진행해야 하지 


최종 실행 결과


2번째 싸이트 방문해 보면 텍스쳐 아틀라스를 만들어 사용하는 것을 확인할 수 있다. 

3번째 싸이트 가면 텍스쳐를 여러개 만들어서 사용하는 방법이다. 

2번째 싸이트 보니 하... 그래서 3번째 싸이트 내용을 가지고 또 연습해 보자. 


[1] http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02

[2] http://lazyfoo.net/tutorials/OpenGL/23_freetype_fonts/index.php

[3] http://www.sccs.swarthmore.edu/users/03/sven/freetype_tut/