=================================
=================================
=================================
출처: https://libsora.so/posts/opengl-text-library/
OpenGL Text Library
OpenGL 자체에는 텍스트 출력 관련된 함수가 없다. 근데 개발과정, 디버깅의 용도로 텍스트 출력이 있었으면 한다. glut기반으로 개발하고 있다면 glutStrokeString / glutBitmapString 를 사용하면 된다. 하지만 나는 glut를 안쓴다. 혹은 glut를 사용할수 없는 환경(예를 들면 OpenGLES)의 경우에는 어떻게 하면될까? 이 때 선택할 수 있는 방법과 내가 선택한 방법 및 간단하게 만들어본 라이브러리에 대해서 써봣다.
요구사항
- 될수있는한 간단하게 만든다
- 소스를 복붙하는것만으로도 다른 프로젝트에 사용할 수 있으면 좋겠다
- cpp/h 딱 2개의 파일만 있으면 좋겟다. 파일이 많아지면 복붙해서 관리하기 귀찮다
- 외부 라이브러리 의존성을 최소한으로 줄인다.
- 개발/디버깅 용도라면 엔진의 밑바닥에 가까운 레이어이다. 낮은 레이어 주제에 외부 라이브러리에 너무 많이 의존하면 다른 프로젝트에 복붙만으로 재사용하기 어렵다.
- 외부라이브러기 붙으면 linking 신경쓰기 귀찮다
- ASCII만 잘 나오면 된다
- 개발용 텍스트 라이브러리에 한글출력은 사치다. 그건 나중에..
선으로 글자를 그리기
펜으로 글자를 쓰듯이 글자를 그리는거다. 대충 아래 같은 느낌이다.
장점은 구조가 단순하다는거다. 그냥 GL에서 선 렌더링하는게 전부니까.
단점은 글자가 못생겼다는 점(…)과 글자를 그리느라 생각보다 많은 성능을 까먹을 가능성이 있다.
구현 참고 자료
글자를 그리기 위한 데이터로 FreeGLUT를 사용해서 구현해본 적이 있다. 아래 3개의 소스를 잘 복붙하면 만들 수 있다. 나한테는 위의 단점이 크게 느껴져서 한번 해보고 버린 방법이다.
TTF
FTGLES같은 라이브러리를 통해 TTF폰트를 로딩해서 적절히 렌더링한다.
장점은 어떤크기의 폰트라도 대응할수 있다는 점과 영어 이외의 문자(한글, 일본어, 한자…)도 문제없이 처리할 수 있다는거다.
단점은 외부 라이브러리를 붙여야된다는 점과 폰트파일이 필요하다는거다.
개발초기의 간단한 도구로써 사용하기(예를 들면 FPS/카메라 위치/객체위치 같은 디버깅 정보출력)에는 좀 무리가 있을거 같아서 방법은 채택하지 않았다.
Bitmap Font (BMP / PNG / ..)
비트맵 폰트 이미지 파일을 텍스쳐로 로딩하고 이것을 적절히 렌더링하는 방법이다. 자세한 설명은 NEHE Bitmap Fonts를 참고하자.
장점은 ttf로딩보다는 구조가 단순하다는거다.
단점은 이미지 로딩 라이브러리와 비트맵 폰트 이미지가 필요하다는거다. 경로설정 잘못하고 실행하면 이미지파일 못찾는다고 궁시렁대니까 좀 귀찮다. 더 단순한 구조면 좋겟다.
C Array
이미지를 매우 간단하게 생각하면 픽셀 데이터 배열이다. 그렇다면 이를 C배열로 만들어서 코드에 때려박으면 추가적인 의존성 없이 그냥 갖다쓸 수 있지 않을까? 이런 생각을 가지고 예전에 찾은 헤더파일 하나로 사용할 수 있는 라이센스가 자유로운 폰트로 Vincent Font라는게 있더라.
Vincent Font는 대충 이런느낌으로 생겼다.
작년부터 이 폰트를 가지고 잘 썻는데 폰트의 크기가 8*8 밖에 안되서 너무 작은거같더라. 게다가 폰트가 은근히 가독성이 떨어지고..
그래서 며칠전부터 다른 대안을 찾던 중에 AntTweakBar라는걸 발견했다. 소스를 까보니까 폰트 배열이 있더라. 그리고 가독성이 괜찮더라. 그래서 AntTweakBar 라이브러리에서 폰트부분만 잘라내고 API를 적당히 손봐서 간단한 라이브러리를 만들어봤다.
AntTweakBar 작은 폰트
AntTweakBar 중간 폰트
AntTweakBar 큰 폰트
AntTweakBar 고정폭
OpenGL Text Library
적당히 만들어본 라이브러리이다.
데모 소스는 대충 이런 느낌이다. (나중에 소스를 고칠수도 있다. Repo의 실행예제인 main.cpp를 참고하는게 더 나을거다)
// 왼쪽아래를 기준점으로 잡고 렌더링 하는거 g_fontDevice->Set2D(kCoordLeftBottom); { TextLabel label; std::string msg1("This is Some Message!"); TextLabelConfig cfg; label.BuildText(msg1, g_DefaultNormalFont.get(), cfg); g_fontDevice->DrawText(&label, 0, 0, COLOR32_WHITE, COLOR32_WHITE); } //원점이 오른쪽위 { TextLabel label; std::string msg1("Left-Top Position Move(100, 50)"); TextLabelConfig cfg; cfg.coord = kCoordLeftTop; label.BuildText(msg1, normal_font.get(), cfg); g_fontDevice->DrawText(&label, 100, 50, COLOR32_WHITE, COLOR32_RED); } { TextLabel label; std::array<std::string, 2> lines; lines[0] = std::string("1.Multi---"); lines[1] = std::string("2.Line---"); TextLabelConfig cfg; cfg.coord = kCoordLeftTop; color32 line_colors[] = { COLOR32_BLUE, COLOR32_RED }; color32 bg_colors[] = { COLOR32_RED, COLOR32_GREEN }; cfg.line_colors = line_colors; cfg.line_bg_colors = bg_colors; cfg.bg_width = 100; cfg.sep = 10; label.BuildText(lines, g_DefaultFixed1Font.get(), cfg); g_fontDevice->DrawText(&label, 100, 100, 0, 0); }
Reference & etc
=================================
=================================
=================================
출처: http://winnerz.tistory.com/entry/%EA%B0%95%EC%A2%8C-opengl%EC%97%90%EC%84%9C-2d%EB%82%98-3d%ED%85%8D%EC%8A%A4%ED%8A%B8-%ED%91%9C%EC%8B%9C%ED%95%98%EA%B8%B0%EC%9E%85%EB%8B%88%EB%8B%A4
opengl이나 다이렉트x에서 텍스트를 표시할때는 주로 글씨자체를 텍스쳐화 시켜서 표현하고는 합니다.
일반적으로 텍스트를 그냥 그리는것은 그자리에서 이미지를 만들고 렌더링을 하기때문에 속도도 느려지고
어느부분은 지원을 안해주는곳도 있습니다. 여기서 이미지를 만들고 렌더링하는 부분을 따로 만드는것이
이번 테크닉의 핵심입니다. 물론 퍼포먼스 자체도 텍스쳐화 하는것이 더 좋습니다. 처음에 한번 이미지를
만들고 나중엔 렌더링만 하기 때문이죠.
클래스에서 다른부분은 제끼고 텍스쳐생성 부분만 설명하겠습니다. 다른부분은 조금만 공부해도
알수있는 부분입니다.
public class Text3d {
private IntBuffer mVertexBuffer;
private IntBuffer mColorBuffer;
private ByteBuffer mIndexBuffer;
private FloatBuffer mfTexBuffer;
private int mTextureID;
public Text3d(){
int one = 50;
int vertices[] = {
0, one, 0,
one, one, 0,
0, 0, 0,
one, 0, 0,
};
float tex[] = {
0.0f ,0.0f,
1.0f,0.0f,
0.0f ,1.0f,
1.0f,1.0f,
};
one = 100000;
int colors[] = {
one, one, one, one,
one, one, one, one,
one, one, one, one,
one, one, one, one,
};
byte indices[] = {
0, 1, 2,
1, 3, 2,
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asIntBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
ByteBuffer tbb = ByteBuffer.allocateDirect(tex.length*4);
tbb.order(ByteOrder.nativeOrder());
mfTexBuffer = tbb.asFloatBuffer();
mfTexBuffer.put(tex);
mfTexBuffer.position(0);
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asIntBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);
}
public void TexCreate(GL10 gl,Context mContext)
{
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
mTextureID = textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,
GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
Bitmap mBitmap;
Canvas mCanvas;
Bitmap.Config config = Bitmap.Config.ARGB_8888;
mBitmap = Bitmap.createBitmap(128, 128, config);
mCanvas = new Canvas(mBitmap);
mBitmap.eraseColor(0);
mCanvas.drawColor(0x00ffffff);
Paint Pnt = new Paint();
Pnt.setColor(0xff00ff00);
Pnt.setTextSize(128);
Pnt.setAntiAlias(false);
Pnt.setTextScaleX(1);
mCanvas.drawText("B", 0, 128, Pnt);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0);
mBitmap.recycle();
}
public void draw(GL10 gl)
{
gl.glFrontFace(GL10.GL_CW);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glColor4f(1f, 1f, 1f, 1f);
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_MODULATE);
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mfTexBuffer);
gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);
gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
}
}
위 클래스의 TexCreate함수를 가져왔습니다. 여기서 하나하나 설명 하겠습니다.
public void TexCreate(GL10 gl,Context mContext)
{
여기는 텍스쳐를 생성하고 id에 연결하는 부분입니다. 그렇게 어렵지는 않습니다. 패스!
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
mTextureID = textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,
GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
여기서 부터가 핵심입니다.
Bitmap mBitmap; 비트맵입니다
Canvas mCanvas; 켄버스입니다.
Bitmap.Config config = Bitmap.Config.ARGB_8888; 비트맵을 설정해줍니다 ARGB_8888
mBitmap = Bitmap.createBitmap(128, 128, config); 128x128 ARGB_8888 비트맵을 생성합니다
mCanvas = new Canvas(mBitmap); 비트맵으로 켄버스를 생성합니다. 켄버스에
그려지는 그림은 비트맵에 그려지게 됩니다.
비트맵은 2의 승수로 만드시길 추천합니다.
잘못하면 안나오는수가 있습니다.
mBitmap.eraseColor(0); 비트맵을 지정색으로 지워줍니다
mCanvas.drawColor(0x00ffffff); 켄버스에 드로우컬러를 설정해줍니다. 글씨로 그려지는
나머지 부분이 그려집니다. 투명하게 하고싶으면 알파값을
빼줍시다...
Paint Pnt = new Paint(); 페인트 객체를 생성합니다.
Pnt.setColor(0xff00ff00); 페인트의 색을 결정해 줍시다. 즉 글씨의 색이 됩니다. 지금은 녹색!
Pnt.setTextSize(128); 텍스트의 사이즈를 정해줍니다.128짜리 사이즈니까 꽉차게 되겠죠!
Pnt.setAntiAlias(false); 안티얼라이즈 취향에 맞게 선택해 줍시다.. true로 하면 조금
부드러운 글씨가 나옵니다. 하지만 텍스트 사이즈가 작으면 작
으면 작을수록 형체를 알아보기 힘듭니다. 사이즈가 클때 추천!
Pnt.setTextScaleX(1); 장평을 설정해줍니다. 1이니까 그대로 나옵니다.
mCanvas.drawText("B", 0, 128, Pnt); 드디어 대망에 그리기입니다! 비트맵에 B가 찍혀서 나옵니다.
여기서 원하시는 단어나 문장을 입력하시면 되겠죠..
지금은 텍스쳐에 글씨를 꽉 차게 그립니다. 문장을 쓰시려면
텍스트 사이즈를 작게 잡거나 비트맵을 크게 잡으셔야 합니다.
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0); 비트맵을 텍스쳐로 보내줍니다..
mBitmap.recycle(); 비트맵을 해재시켜줍니다...
}
이제 만들어진 텍스쳐를 위에 draw함수처럼 폴리곤에 적용만 시켜주면 화면에 B가 찍혀나오는것을
볼수있습니다. 테크닉 자체가 3d를 쓰려면 필수이긴하지만 어느정도 난이도가 있는 테크닉입니다.
출처: http://winnerz.tistory.com/entry/강좌-opengl에서-2d나-3d텍스트-표시하기입니다 [조...좋은 개발자다!]
=================================
=================================
=================================
'프로그래밍 관련 > 3D,2D DRAW 관련' 카테고리의 다른 글
OpenGL D3D 그래픽 용어 정리 관련 (0) | 2017.05.30 |
---|---|
OpenGL 중 glew.h 를 include하면 에러가 나는데 어떻게 해결해야 할까요? (0) | 2017.05.17 |
OpenGL 오픈지엘 에서 SDL, FreeImage 등등 라이브러리등등 을 이용하여 이미지 로드하기 관련 (0) | 2017.05.10 |
SDL 개발환경 셋팅 관련 (0) | 2017.05.10 |
OpenGL 오픈지엘 glew 관련 외부 기호를 확인할 수 없습니다. 에러 관련 (0) | 2017.04.25 |
댓글 영역