OpenGL ES에서 텍스트 그리기


131

현재 Android 플랫폼을위한 작은 OpenGL 게임을 개발 중이며 렌더링 된 프레임 위에 텍스트를 렌더링하는 쉬운 방법이 있는지 궁금합니다 (플레이어 점수 등의 HUD 등). 텍스트도 사용자 정의 글꼴을 사용해야합니다.

View를 오버레이로 사용하는 예를 보았지만 나중에 다른 플랫폼으로 게임을 이식하고 싶을 수도 있으므로 알지 모르겠습니다.

어떤 아이디어?


이 프로젝트를 살펴보십시오 : code.google.com/p/rokon
whunmr

libgdx가 비트 맵 글꼴을 통해이를 수행하는 방식을 살펴보십시오.
Robert Massaioli

답변:


103

Android SDK에는 OpenGL보기에서 텍스트를 그릴 수있는 쉬운 방법이 없습니다. 다음과 같은 옵션이 있습니다.

  1. SurfaceView 위에 TextView를 배치하십시오. 이것은 느리고 나쁘지만 가장 직접적인 접근 방식입니다.
  2. 일반적인 문자열을 텍스처로 렌더링하고 간단히 그 텍스처를 그립니다.이것은 가장 단순하고 빠르지 만 유연성이 가장 낮습니다.
  3. 스프라이트를 기반으로 한 사용자 고유의 텍스트 렌더링 코드. 2가 옵션이 아닌 경우 아마도 두 번째 최선의 선택입니다. 발을 젖게하는 좋은 방법이지만 단순 해 보이고 (기본 기능은 있지만) 더 많은 기능 (텍스처 정렬, 줄 바꿈, 가변 폭 글꼴 등)을 추가할수록 어려워지고 더 어려워집니다. )-이 경로를 이용하면 도망 갈 수있는 한 간단하게 만드세요!
  4. 상용 / 오픈 소스 라이브러리를 사용하십시오. 구글에서 사냥을 할 경우 몇 가지가 있지만 까다로운 부분은 통합하여 실행하는 것입니다. 그러나 적어도 일단 그렇게하면 그들이 제공하는 모든 유연성과 성숙도를 얻게됩니다.

3
GLView를 통해 뷰를 추가하기로 결정했지만 가장 효율적인 방법은 아니지만 뷰가 자주 업데이트되지 않으며 원하는 글꼴을 추가 할 수있는 유연성을 제공합니다. 모든 답장을 보내 주셔서 감사합니다!
2009 년

1
일반적인 문자열을 텍스처에 렌더링하고 해당 텍스처를 간단히 그리는 방법은 무엇입니까? 감사.
VansFannel

1
VansFannel : 페인트 프로그램을 사용하여 모든 문자열을 하나의 이미지에 넣은 다음 앱에서 오프셋을 사용하여 원하는 문자열이 포함 된 이미지 부분 만 렌더링합니다.
Dave

2
또는이를 달성하기위한보다 프로그래밍 방식은 아래 JVitela의 답변을 참조하십시오.
Dave

4
JVitela의 답변이 더 좋습니다. 그것이 내가 현재 사용하고있는 것입니다. 표준 안드로이드 vier + canvas에서 opengl로 전환하는 이유는 (다른 것들 중에서) 속도 때문입니다. opengl 종류에 텍스트 상자를 추가하면 무효화됩니다.
Shivan Dragon

166

텍스처에 텍스트를 렌더링하는 것은 Sprite Text 데모가 만드는 것보다 간단합니다. 기본 아이디어는 Canvas 클래스를 사용하여 비트 맵에 렌더링 한 다음 비트 맵을 OpenGL 텍스처에 전달하는 것입니다.

// Create an empty, mutable bitmap
Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_4444);
// get a canvas to paint over the bitmap
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);

// get a background image from resources
// note the image format must match the bitmap format
Drawable background = context.getResources().getDrawable(R.drawable.background);
background.setBounds(0, 0, 256, 256);
background.draw(canvas); // draw the background to our bitmap

// Draw the text
Paint textPaint = new Paint();
textPaint.setTextSize(32);
textPaint.setAntiAlias(true);
textPaint.setARGB(0xff, 0x00, 0x00, 0x00);
// draw the text centered
canvas.drawText("Hello World", 16,112, textPaint);

//Generate one texture pointer...
gl.glGenTextures(1, textures, 0);
//...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

//Create Nearest Filtered Texture
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);

//Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

//Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

//Clean up
bitmap.recycle();

5
덕분에 디버깅을 위해 앱에 작은 fps 카운터로 표시하는 데 도움이되었습니다.
stealthcopter

2
이를 사용하여 다른 텍스처를로드 한 다음이를 조합하여 단어 나 숫자를 형성 할 때 모든 문자와 숫자를 텍스처로 생성 할 수 있습니다. 그러면 다른 gl 텍스처보다 효율적이지 않습니다.
twDuke

9
속도가 매우 느리므로 항상 변경되는 텍스트 (점수 등)에 대해 게임에서 fps를 제거하지만 반 정적 항목 (플레이어 이름, 레벨 이름 등)에는 적합합니다.
led42

3
이 답변의 코드는 아마도 데모 일 뿐이며 최적화 된 것은 아닙니다. 자신의 방식으로 최적화 / 캐시하십시오.
셰리프 elKhatib

1
이것을 OpenGL ES 2.0에 제공 할 수 있습니까?
무제한

36

에 의해 게시 된 답변을 확장 하는 자습서 를 작성 했습니다JVitela가 . 기본적으로 동일한 아이디어를 사용하지만 각 문자열을 텍스처로 렌더링하는 대신 글꼴 파일에서 텍스처로 모든 문자를 렌더링하고 더 느린 속도로 전체 동적 텍스트 렌더링을 허용하는 데 사용합니다 (초기화가 완료되면) .

다양한 글꼴 아틀라스 생성기와 비교하여 내 방법의 주요 장점은 모든 글꼴 변형 및 크기에 대해 큰 비트 맵을 제공하지 않고 작은 글꼴 파일 (.ttf .otf)을 프로젝트와 함께 제공 할 수 있다는 것입니다. 글꼴 파일 만 사용하여 모든 해상도에서 완벽한 품질의 글꼴을 생성 할 수 있습니다 :)

튜토리얼은 모든 프로젝트에서 사용할 수있는 전체 코드가 포함되어 있습니다 :


현재이 솔루션을 조사 중이며 정시에 답변을 찾을 것이라고 확신하지만 구현에 런타임 할당이 사용됩니까?
Nick Hartung

@Nick-글꼴 인스턴스를 만들 때 모든 할당 (텍스처, 정점 버퍼 등)이 수행되며, 글꼴 인스턴스를 사용하여 문자열을 렌더링 할 때는 추가 할당이 필요 없습니다. 따라서 런타임시 추가 할당없이 "로드 시간"에 글꼴을 만들 수 있습니다.
free3dom

와, 대단해! 이것은 텍스트가 자주 바뀌는 경우에 특히 유용합니다.
mdiener

이것은 런타임에 텍스트를 자주 변경해야하는 응용 프로그램에 가장 적합한 성능 측면의 대답입니다. 안드로이드에 대해 이것만큼 좋은 것을 보지 못했습니다. 그러나 OpenGL ES에 포트를 사용할 수 있습니다.
greeble31

1
텍스트 정렬, 줄 바꿈 등을 처리하지 않으려면 TextView를 사용할 수 있습니다. TextView를 캔버스로 쉽게 렌더링 할 수 있습니다. 성능이 현명한 방법보다 무겁지 않아야합니다. 필요한 모든 텍스트를 렌더링하려면 하나의 TextView 인스턴스 만 있으면됩니다. 이렇게하면 간단한 HTML 형식도 무료로 사용할 수 있습니다.
Gena Batsyan

8

이 링크에 따르면 :

http://code.neenbedankt.com/how-to-render-an-android-view-to-a-bitmap

모든 뷰 를 비트 맵으로 렌더링 수 있습니다 . 텍스트, 이미지 등을 포함하여 필요에 따라보기를 레이아웃 한 다음이를 비트 맵으로 렌더링 할 수 있다고 가정하는 것이 좋습니다.

의 JVitela 코드 를 사용하면 해당 비트 맵을 OpenGL 텍스처로 사용할 수 있습니다.


예, 게임에서 MapView로 그것을하고 gl 텍스처에 바인딩 했으므로 맵과 opengl을 결합하십시오. 예, 모든 뷰에는 onDraw (Canvas c)가 있으며 모든 캔버스를 전달하고 모든 비트 맵에 캔버스를 바인딩 할 수 있습니다.
HaMMeReD


6

스프라이트 텍스트 예제를 보았고 그러한 작업에 끔찍하게 보입니다. 텍스처 렌더링도 고려했지만 성능 저하가 걱정 될 수 있습니다. 대신보기를 사용해야하고 다리를 건너야 할 때 이식에 대해 걱정해야 할 수도 있습니다. :)



4

IMHO 게임에서 OpenGL ES를 사용해야하는 세 가지 이유가 있습니다.

  1. 공개 표준을 사용하여 모바일 플랫폼 간의 차이를 피하십시오.
  2. 렌더 프로세스를 더 잘 제어하려면;
  3. GPU 병렬 처리의 이점을 누리려면

텍스트를 그리는 것은 게임 디자인에서 항상 문제가됩니다. 물건을 그리기 때문에 위젯 등을 사용하여 일반적인 활동의 모양과 느낌을 가질 수 없습니다.

프레임 워크를 사용하여 트루 타입 글꼴에서 비트 맵 글꼴을 생성하고 렌더링 할 수 있습니다. 내가 본 모든 프레임 워크는 같은 방식으로 작동합니다. 그리기 시간에 텍스트의 정점 및 텍스처 좌표를 생성하십시오. 이것이 OpenGL을 가장 효율적으로 사용하는 것은 아닙니다.

가장 좋은 방법은 코드 초기에 정점과 텍스처에 원격 버퍼 (정점 버퍼 객체-VBO)를 할당하여 그리기 시간에 게으른 메모리 전송 작업을 피하는 것입니다.

게임 플레이어는 텍스트를 읽는 것을 좋아하지 않으므로 동적으로 긴 텍스트를 작성하지 마십시오. 레이블의 경우 정적 텍스처를 사용하여 시간과 점수에 대한 동적 텍스트를 남길 수 있으며 둘 다 길이가 몇 자릿수입니다.

따라서 내 솔루션은 간단합니다.

  1. 일반적인 레이블 및 경고에 대한 질감을 만듭니다.
  2. 숫자 0-9, ":", "+"및 "-"에 대한 텍스처를 만듭니다. 각 캐릭터마다 하나의 질감;
  3. 화면의 모든 위치에 대해 원격 VBO를 생성하십시오. 해당 위치에서 정적 또는 동적 텍스트를 렌더링 할 수 있지만 VBO는 정적입니다.
  4. 텍스트는 항상 한 방향으로 렌더링되므로 하나의 텍스처 VBO 만 생성하십시오.
  5. 그리기 시간에는 정적 텍스트를 렌더링합니다.
  6. 동적 텍스트의 경우 VBO 위치를 들여다보고 캐릭터 텍스처를 가져 와서 한 번에 하나씩 그릴 수 있습니다.

원격 정적 버퍼를 사용하면 그리기 작업이 빠릅니다.

화면 위치 (화면의 대각선 비율을 기준으로)와 텍스처 (정적 및 문자)로 XML 파일을 만든 다음 렌더링하기 전에이 XML을로드합니다.

높은 FPS 속도를 얻으려면 그리기시 VBO 생성을 피해야합니다.


"VOB"는 "VBO"(정점 버퍼 객체)를 의미합니까?
Dan Hulme

3

GL 사용을 고집하면 텍스트를 텍스처에 렌더링 할 수 있습니다. 대부분의 HUD가 비교적 정적 인 것으로 가정하면, 텍스처를 텍스처 메모리에 너무 자주로드 할 필요는 없습니다.


3

CBFG로딩 / 렌더링 코드의 Android 포트를 살펴보십시오 . 코드를 프로젝트에 드롭하여 바로 사용할 수 있어야합니다.

  1. CBFG

  2. 안드로이드 로더

이 구현에 문제가 있습니다. 글꼴의 비트 맵 크기를 변경하려고하면 특수 문자가 필요합니다. 전체 그리기가 실패합니다. (


2

나는 이것을 몇 시간 동안 찾고 있었는데, 이것이 내가 처음 접한 기사 였고 가장 좋은 답변을 받았지만 가장 인기있는 답변은 내가 표시하지 않았다고 생각합니다. 확실히 내가 필요한 것에. weichsel 's와 shakazed의 답변은 버튼에 맞았지만 기사에서는 약간 가려졌습니다. 바로 프로젝트에 당신을 넣어. 여기 : 기존 샘플을 기반으로 새 Android 프로젝트를 만듭니다. ApiDemos를 선택하십시오.

소스 폴더 아래를보십시오

ApiDemos/src/com/example/android/apis/graphics/spritetext

그리고 필요한 모든 것을 찾을 수 있습니다.


1

들어 정적 텍스트 :

  • PC에 사용 된 모든 단어가 포함 된 이미지를 생성하십시오 (예 : 김프).
  • 이것을 텍스처로로드하고 평면의 재질로 사용하십시오.

대한 긴 텍스트 의 요구가 가끔 업데이트 할 것을 :

  • 안드로이드가 비트 맵 캔버스 (JVitela의 솔루션)에 그리도록하십시오.
  • 이것을 평면의 재질로로드하십시오.
  • 각 단어마다 다른 질감 좌표를 사용하십시오.

들어 다수 (00.0 포맷) :

  • 모든 숫자와 점으로 이미지를 생성하십시오.
  • 이것을 평면의 재질로로드하십시오.
  • 아래 쉐이더를 사용하십시오.
  • onDraw 이벤트에서는 셰이더로 전송 된 값 변수 만 업데이트하십시오.

    precision highp float;
    precision highp sampler2D;
    
    uniform float uTime;
    uniform float uValue;
    uniform vec3 iResolution;
    
    varying vec4 v_Color;
    varying vec2 vTextureCoord;
    uniform sampler2D s_texture;
    
    void main() {
    
    vec4 fragColor = vec4(1.0, 0.5, 0.2, 0.5);
    vec2 uv = vTextureCoord;
    
    float devisor = 10.75;
    float digit;
    float i;
    float uCol;
    float uRow;
    
    if (uv.y < 0.45) {
        if (uv.x > 0.75) {
            digit = floor(uValue*10.0);
            digit = digit - floor(digit/10.0)*10.0;
            i = 48.0 - 32.0 + digit;
            uRow = floor(i / 10.0);
            uCol = i - 10.0 * uRow;
            fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-1.5) / devisor, uRow / devisor) );
        } else if (uv.x > 0.5) {
            uCol = 4.0;
            uRow = 1.0;
            fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-1.0) / devisor, uRow / devisor) );
        } else if (uv.x > 0.25) {
            digit = floor(uValue);
            digit = digit - floor(digit/10.0)*10.0;
            i = 48.0 - 32.0 + digit;
            uRow = floor(i / 10.0);
            uCol = i - 10.0 * uRow;
            fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-0.5) / devisor, uRow / devisor) );
        } else if (uValue >= 10.0) {
            digit = floor(uValue/10.0);
            digit = digit - floor(digit/10.0)*10.0;
            i = 48.0 - 32.0 + digit;
            uRow = floor(i / 10.0);
            uCol = i - 10.0 * uRow;
            fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-0.0) / devisor, uRow / devisor) );
        } else {
            fragColor = vec4(0.0, 0.0, 0.0, 0.0);
        }
    } else {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0);
    }
    gl_FragColor = fragColor;
    
    }

위의 코드는 글꼴 아틀라스 (텍스처)의 두 번째 행의 7 번째 열에서 숫자가 0에서 시작하는 텍스처 아틀라스에 대해 작동합니다.

데모 는 https://www.shadertoy.com/view/Xl23Dw 를 참조하십시오 (잘못된 질감으로)


0

OpenGL ES 2.0 / 3.0에서는 OGL View와 Android의 UI 요소를 결합 할 수도 있습니다.

public class GameActivity extends AppCompatActivity {
    private SurfaceView surfaceView;
    @Override
    protected void onCreate(Bundle state) { 
        setContentView(R.layout.activity_gl);
        surfaceView = findViewById(R.id.oglView);
        surfaceView.init(this.getApplicationContext());
        ...
    } 
}

public class SurfaceView extends GLSurfaceView {
    private SceneRenderer renderer;
    public SurfaceView(Context context) {
        super(context);
    }

    public SurfaceView(Context context, AttributeSet attributes) {
        super(context, attributes);
    }

    public void init(Context context) {
        renderer = new SceneRenderer(context);
        setRenderer(renderer);
        ...
    }
}

레이아웃 activity_gl.xml을 만듭니다.

<?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        tools:context=".activities.GameActivity">
    <com.app.SurfaceView
        android:id="@+id/oglView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <TextView ... />
    <TextView ... />
    <TextView ... />
</androidx.constraintlayout.widget.ConstraintLayout>

렌더 스레드에서 요소를 업데이트하려면 Handler / Looper를 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.