web-dev-qa-db-ja.com

OpenGL ESでテキストを描く

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

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

何か案は?

122
shakazed

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

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

テクスチャへのテキストのレンダリングは、スプライトテキストデモの外観よりも簡単です。基本的な考え方は、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();
162
JVitela

JVitela によって投稿された答えを展開する tutorial を書きました。基本的には同じ考え方を使用しますが、各文字列をテクスチャにレンダリングする代わりに、フォントファイルのすべての文字をテクスチャにレンダリングし、それを使用して、さらに減速することなくフルダイナミックテキストレンダリングを可能にします(初期化が完了すると) 。

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

tutorial には、すべてのプロジェクトで使用できる完全なコードが含まれています:)

35
free3dom

このリンクによると:

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

任意のビューをビットマップにレンダリングできます。おそらく、必要に応じてビュー(テキスト、画像などを含む)をレイアウトし、それをビットマップにレンダリングできると想定する価値があります。

JVitelaのコード 上記 を使用すると、そのビットマップをOpenGLテクスチャとして使用できるはずです。

8
JWGS

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

CBFG- http://www.codehead.co.uk/cbfg

Androidローダー- http://www.codehead.co.uk/cbfg/TexFont.Java

7
Codehead

Spriteテキストの例を見て、そのようなタスクでは非常に複雑に見えます。テクスチャへのレンダリングも検討しましたが、パフォーマンスヒットが発生する可能性が心配です。代わりにビューを表示する必要があり、その橋を渡るときの移植を心配する必要があります:)

6
shakazed

GLSurfaceViewサンプル の「Sprite Text」サンプルを見てください。

4

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

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

ゲームのデザインでは、テキストを描くことは常に問題です。なぜなら、物を描くので、ウィジェットなどを使用して、一般的なアクティビティのルックアンドフィールを持つことができないからです。

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

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

ゲームプレイヤーはテキストを読むことを好まないので、長く動的に生成されたテキストを書かないことに注意してください。ラベルには、静的テクスチャを使用して、時間とスコアに動的テキストを残すことができます。両方とも数文字の数値です。

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

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

リモートの静的バッファを使用する場合、描画操作は高速です。

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

高いFPSレートを得るには、描画時にVBOを生成しないでください。

4
cleuton

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

3
Tal Pressman

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

  1. CBFG

  2. Androidローダー

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

3
Aetherna

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

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

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

そして、必要なものはすべて見つかります。

2
Justin

static textの場合:

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

長いテキストの場合、時々更新する必要があります:

  • Androidにビットマップキャンバスに描画させます(JVitelaのソリューション)。
  • これを飛行機のマテリアルとしてロードします。
  • Wordごとに異なるテクスチャ座標を使用します。

a number(フォーマット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 を参照してください(ただし、テクスチャが間違っています)

1
Pete