OpenGL ESでテキストを描画する


131

私は現在、Androidプラットフォーム用の小さなOpenGLゲームを開発しています。レンダリングされたフレームの上にテキストを簡単にレンダリングする方法(プレーヤーのスコアのあるHUDなど)があるかどうか疑問に思っています。テキストもカスタムフォントを使用する必要があります。

ビューをオーバーレイとして使用する例を見てきましたが、後で他のプラットフォームにゲームを移植する可能性があるため、それを実行するかどうかはわかりません。

何か案は?


このプロジェクトを見てみましょう:code.google.com/p/rokon
whunmr

libgdxがビットマップフォントを通じてこれを行う方法を見てください。
ロバートマサイオリ

回答:


103

Android SDKには、OpenGLビューでテキストを描画する簡単な方法はありません。次のオプションをご利用いただけます。

  1. TextViewをSurfaceViewの上に配置します。これは遅くて悪いですが、最も直接的なアプローチです。
  2. 一般的な文字列をテクスチャにレンダリングし、単純にそれらのテクスチャを描画します。これははるかに単純で高速ですが、柔軟性が最も低くなります。
  3. スプライトに基づく独自のテキストレンダリングコードをロールします。2がオプションでない場合は、おそらく2番目に良い選択です。足を濡らすには良い方法ですが、シンプルなように見えますが(基本的な機能は簡単です)、機能を追加するとテクスチャが整列し、改行、可変幅のフォントなどを処理するのが難しくなるので注意してください。 )-このルートを使用する場合は、できるだけ簡単にしてください!
  4. 既成のオープンソースライブラリを使用します。あなたがグーグルで狩りをするならば、周りにいくつかあります、トリッキーなビットはそれらを統合して走らせることです。しかし、少なくとも、それを実行すると、柔軟性と成熟度がすべて得られます。

3
私はGLViewの上にビューを追加することにしました。これは、最も効率的な方法ではないかもしれませんが、ビューが頻繁に更新されることはありません。さらに、好きなフォントを柔軟に追加できます。すべての返信をありがとう!
2009

1
一般的な文字列をテクスチャにレンダリングし、それらのテクスチャを単に描画するにはどうすればよいですか?ありがとう。
VansFannel 2011年

1
VansFannel:ペイントプログラムを使用してすべての文字列を1つの画像に配置し、アプリでオフセットを使用して、必要な文字列を含む画像の部分のみをレンダリングします。表示されます。
Dave

2
または、これを達成するためのよりプログラム的な方法については、以下のJVitelaの回答を参照してください。
Dave

4
JVitelaの回答の方が優れています。それは私が現在使用しているものです。標準のandroid 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カウンターに表示するのに役立ちました、ありがとう!
ステルスコプター、2011年

2
これを使用して、他のテクスチャをロードするときにすべての文字と数字をテクスチャとして生成し、それらを組み合わせて単語または数字を形成できます。次に、他のglテクスチャと同じくらい効率的です。
twDuke

9
これは非常に遅く、常に変化するテキスト(スコアなど)のゲームではfpsが強制終了されますが、半静的なもの(プレイヤー名、レベル名など)にはうまく機能します。
led42

3
この回答のコードはおそらくデモにすぎず、まったく最適化されていないことに注意してください。独自の方法で最適化/キャッシュしてください。
Sherif elKhatib 2013年

1
これをOpenGL ES 2.0に提供できますか?
無制限

36

JVitelaが投稿した回答を拡張するチュートリアルを作成しました。基本的には同じアイデアを使用しますが、各文字列をテクスチャにレンダリングするのではなく、フォントファイルからテクスチャにすべての文字をレンダリングし、それを使用して(初期化が完了すると)それ以上の速度低下なしに完全なダイナミックテキストレンダリングを可能にします。 。

さまざまなフォントアトラスジェネレーターと比較した私の方法の主な利点は、フォントのバリエーションやサイズごとに大きなビットマップを出荷する必要がなく、プロジェクトに小さなフォントファイル(.ttf .otf)を出荷できることです。フォントファイルのみを使用して、あらゆる解像度で完璧な品質のフォントを生成できます。

チュートリアルでは、任意のプロジェクトで使用することができ、完全なコードが含まれています:)


私は現在このソリューションを調査しており、答えをすぐに見つけることができると確信していますが、実装ではランタイム割り当てを使用していますか?
Nick Hartung

@Nick-フォントインスタンスの作成時にすべての割り当て(テクスチャ、頂点バッファーなど)が行われます。フォントインスタンスを使用して文字列をレンダリングする場合、それ以上の割り当ては必要ありません。したがって、実行時にそれ以上の割り当てを行うことなく、「ロード時」にフォントを作成できます。
free3dom 2013年

わあ、すごい!これは、テキストが頻繁に変更される場合に特に役立ちます。
mdiener 2015

これは、実行時に頻繁にテキストを変更する必要があるアプリケーションにとって、パフォーマンスの面で最良の答えです。Androidにとってこれほど優れたものは他にありません。ただし、OpenGL ESへのポートを使用できます。
greeble31

1
テキストの配置、改行などを処理したくない場合は、TextViewを使用できます。TextViewは簡単にキャンバスにレンダリングできます。パフォーマンスに関しては、与えられたアプローチよりも重いべきではありません。必要なすべてのテキストをレンダリングするために必要なのは、1つのTextViewインスタンスだけです。このようにして、シンプルなHTMLフォーマットを無料で入手することもできます。
Gena Batsyan 2017

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

私見ゲームでOpenGL ESを使用する3つの理由があります。

  1. オープンスタンダードを使用して、モバイルプラットフォーム間の違いを避けます。
  2. レンダリングプロセスをより詳細に制御するため。
  3. GPU並列処理の恩恵を受けるため。

ゲームデザインでは、テキストを描画することは常に問題になります。描画しているため、ウィジェットなどを使用して、一般的なアクティビティのルックアンドフィールを実現することはできません。

フレームワークを使用して、TrueTypeフォントからビットマップフォントを生成し、レンダリングできます。私が見たすべてのフレームワークは同じように動作します:描画時にテキストの頂点とテクスチャ座標を生成します。これはOpenGLの最も効率的な使用法ではありません。

最善の方法は、コードの早い段階で頂点とテクスチャにリモートバッファー(頂点バッファーオブジェクト-VBO)を割り当てることで、描画時のレイジーメモリ転送操作を回避することです。

ゲームプレーヤーはテキストを読みたくないので、動的に生成された長いテキストは書きません。ラベルには、静的テクスチャを使用して、時間とスコアに動的テキストを残すことができます。どちらも数文字の数字です。

だから、私の解決策は簡単です:

  1. 一般的なラベルと警告のテクスチャを作成します。
  2. 0〜9の数字、「:」、「+」、「-」のテクスチャを作成します。キャラクターごとに1つのテクスチャ。
  3. 画面内のすべての位置のリモートVBOを生成します。その位置で静的または動的テキストをレンダリングできますが、VBOは静的です。
  4. テキストは常に一方向にレンダリングされるため、テクスチャVBOを1つだけ生成します。
  5. 描画時に、静的テキストをレンダリングします。
  6. ダイナミックテキストの場合、VBOの位置をのぞき、文字のテクスチャを取得して、一度に1文字ずつ描画できます。

リモート静的バッファーを使用すると、描画操作が高速になります。

画面の位置(画面の対角線のパーセンテージに基づく)とテクスチャー(静的および文字)を含むXMLファイルを作成し、レンダリング前にこのXMLをロードします。

高いFPSレートを得るには、描画時にVBOを生成しないようにする必要があります。


"VOB"とは、 "VBO"(頂点バッファーオブジェクト)を意味しますか?
Dan Hulme、2014年

3

GLの使用を主張する場合は、テキストをテクスチャにレンダリングできます。ほとんどのHUDが比較的静的であると想定すると、テクスチャをテクスチャメモリに頻繁にロードする必要はありません。


3

CBFG読み込み/レンダリングコードのAndroidポートを見てください。コードをプロジェクトにドロップして、すぐに使用できるはずです。

  1. CBFG

  2. Androidローダー

この実装に問題があります。フォントのビットマップのサイズを変更しようとすると、1文字しか表示されません(特殊文字が必要です)全体の描画が失敗します:(


2

私はこれを数時間探していました。これは最初に出くわした記事であり、最良の答えはありますが、最も人気のある答えはマークから外れています。確かに私が必要なもののために。weichselとshakazedの答えはボタン上で正しかったが、記事では少しあいまいであった。あなたをプロジェクトに権利を置くために。ここ:既存のサンプルに基づいて新しいAndroidプロジェクトを作成するだけです。ApiDemosを選択します。

ソースフォルダの下を見てください

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

そして、あなたはあなたが必要とするすべてを見つけるでしょう。


1

以下のための静的なテキスト

  • PCで使用されているすべての単語を含む画像を生成します(たとえば、GIMPを使用)。
  • これをテクスチャとしてロードし、平面のマテリアルとして使用します。

たまに更新する必要がある長いテキストの場合:

  • Androidにビットマップキャンバス上で描画させます(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;
    
    }

上記のコードは、フォントアトラス(テクスチャ)の2行目の7列目が0から始まるテクスチャアトラスで機能します。

デモについては、https://www.shadertoy.com/view/Xl23Dwを参照してください(ただし、テクスチャは正しくありません)。


0

OpenGL ES 2.0 / 3.0では、OGLビューと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>

レンダースレッドから要素を更新するには、ハンドラー/ルーパーを使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.