Rysowanie tekstu w OpenGL ES

Obecnie rozwijam małą grę OpenGL na platformę Android i zastanawiam się, czy jest łatwy sposób na renderowanie tekstu na górze renderowanej ramki (jak HUD z wynikami graczy itp.). Tekst musiałby również użyć niestandardowej czcionki.

Widziałem przykład wykorzystujący Widok jako nakładkę, ale nie wiem, czy chcę to zrobić, ponieważ mogę chcieć przenieść grę na inne platformy później.

Jakieś pomysły?

Author: Peter O., 2009-08-27

13 answers

Android SDK nie ma łatwego sposobu rysowania tekstu na widokach OpenGL. Pozostawiając Ci następujące opcje.

  1. Umieść widok tekstu na widoku SurfaceView.To jest powolne i złe, ale najbardziej bezpośrednie podejście.
  2. Renderuj typowe ciągi znaków do tekstur i po prostu rysuj te tekstury.Jest to zdecydowanie najprostszy i najszybszy, ale najmniej elastyczny.
  3. własny kod renderujący tekst oparty na sprite ' u. prawdopodobnie drugi najlepszy wybór jeśli 2 nie wchodzi w grę. Dobry sposób na zmoczenie stóp, ale pamiętaj, że chociaż wydaje się to proste( a podstawowe funkcje są), staje się trudniejsze i trudniejsze, ponieważ dodajesz więcej funkcji (wyrównanie tekstur, radzenie sobie z łamaniem linii, czcionkami o zmiennej szerokości itp.) - jeśli pojedziesz tą trasą, zrób to tak prosto, jak tylko możesz!
  4. Użyj biblioteki open-source. jest kilka wokół, jeśli polujesz na Google, trudnym bitem jest ich zintegrowanie i działanie. Ale przynajmniej, gdy to zrobisz, będziesz mieć pełną elastyczność i dojrzałość, jaką zapewniają.
 104
Author: Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-27 18:35:54

Renderowanie tekstu do tekstury jest prostsze niż to, jak wygląda demo tekstu Sprite, podstawową ideą jest użycie klasy Canvas do renderowania bitmapy, a następnie przekazanie bitmapy do tekstury 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();
 168
Author: JVitela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-02 15:35:44

Napisałem tutorial rozszerzający odpowiedź dodaną przez JVitela. Zasadniczo używa tego samego pomysłu, ale zamiast renderować każdy ciąg znaków do tekstury, renderuje wszystkie znaki z pliku czcionki do tekstury i używa tego, aby umożliwić pełne dynamiczne renderowanie tekstu bez dalszych spowolnień(po zakończeniu inicjalizacji).

Główną zaletą mojej metody, w porównaniu do różnych generatorów atlasu czcionek, jest to, że można wysyłać małe pliki czcionek (.ttf .otf) z projektem zamiast wysyłać duże bitmapy dla każdej odmiany i rozmiaru czcionki. Może generować czcionki doskonałej jakości w dowolnej rozdzielczości, używając tylko pliku czcionki:)

Tutorial zawiera pełny kod, który można wykorzystać w dowolnym projekcie:)

 36
Author: free3dom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:24

Zgodnie z tym linkiem:

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

Możesz renderować dowolny Widok na bitmapę. Prawdopodobnie warto założyć, że można układać widok zgodnie z potrzebami (w tym tekst, obrazy itp.) , a następnie renderować go na bitmapę.

Używając kodu JVitela powyżej powinieneś być w stanie użyć tej bitmapy jako tekstury OpenGL.

 8
Author: JWGS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:36

Spójrz na cbfg i Port Androida kodu ładowania/renderowania. Powinieneś być w stanie upuścić kod do swojego projektu i użyć go od razu.

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

Android loader- http://www.codehead.co.uk/cbfg/TexFont.java

 7
Author: Codehead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-18 22:20:14

Spojrzałem na przykład tekst sprite ' a i wygląda to strasznie skomplikowanie jak na takie zadanie, rozważałem renderowanie do tekstury, ale martwię się o wydajność, która może spowodować. Może będę musiał zamiast tego iść z widokiem i martwić się o portowanie, gdy nadejdzie czas, aby przejść przez ten most:)

 6
Author: shakazed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-27 14:46:50

Spójrz na próbkę "Sprite Text" w próbkach GLSurfaceView .

 4
Author: Thomas Zoechling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-27 06:18:12

IMHO są trzy powody, dla których warto używać OpenGL ES w grze:

  1. unikaj różnic między platformami ruchomymi, używając otwartego standardu;
  2. aby mieć większą kontrolę nad procesem renderowania;
  3. aby korzystać z przetwarzania równoległego GPU;

Rysowanie tekstu jest zawsze problemem w projektowaniu gier, ponieważ rysujesz rzeczy, więc nie możesz mieć wyglądu i stylu wspólnej aktywności, z widżetami i tak dalej.

Możesz użyć frameworka do generowania czcionek bitmapowych z czcionek TrueType i renderować je. Wszystkie frameworki, które widziałem, działają w ten sam sposób: generują współrzędne wierzchołków i tekstur dla tekstu w czasie rysowania. Nie jest to najbardziej efektywne wykorzystanie OpenGL.

Najlepszym sposobem jest przydzielanie zdalnych buforów (obiektów bufora wierzchołków-VBOs) dla wierzchołków i tekstur na początku kodu, unikając leniwych operacji transferu pamięci w czasie rysowania.

Pamiętaj, że gracze nie lubią czytać tekstu, więc nie napiszesz długiego dynamicznie wygenerowany tekst. W przypadku etykiet można używać tekstur statycznych, pozostawiając tekst dynamiczny dla czasu i partytury, a oba są numeryczne z kilkoma znakami.

Więc moje rozwiązanie jest proste:

  1. Tworzenie tekstur dla wspólnych etykiet i ostrzeżeń;
  2. Tworzenie tekstury dla liczb 0-9, ":", "+", i" -". Jedna Tekstura dla każdego znaku;
  3. Wygeneruj zdalne VBO dla wszystkich pozycji na ekranie. Mogę renderować tekst statyczny lub dynamiczny w tych pozycjach, ale VBO są static;
  4. Wygeneruj tylko jedną teksturę VBO, ponieważ tekst jest zawsze renderowany w jedną stronę;
  5. w czasie rysowania renderuję statyczny tekst;
  6. dla tekstu dynamicznego, mogę zerknąć na pozycję VBO, uzyskać teksturę znaku i narysować go, znak na raz.

Operacje rysowania są szybkie, jeśli używasz zdalnych buforów statycznych.

Tworzę plik XML z pozycjami ekranu (na podstawie procentu przekątnej ekranu) i teksturami (statycznymi i znakami), a następnie Ładuję ten XML przed rendering.

Aby uzyskać wysoką szybkość FPS, należy unikać generowania VBO w czasie losowania.

 4
Author: cleuton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-02 14:45:35

Jeśli nalegasz na użycie GL, możesz renderować tekst na tekstury. Zakładając, że większość HUD jest względnie statyczna, nie powinieneś zbyt często ładować tekstur do pamięci tekstur.

 3
Author: Tal Pressman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-27 06:15:30

Spójrz na CBFG i port Androida ładowania/renderowania kod. Powinieneś być w stanie upuścić kod do swojego projektu i użyć go natychmiast.

  1. CBFG

  2. Android loader

Mam problemy z tą implementacją. Wyświetla tylko jeden znak, Kiedy próbuję zmienić rozmiar bitmapy czcionki (potrzebuję specjalnych liter) całe rysowanie nie powiodło się: (

 3
Author: Aetherna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-09 12:49:35

Szukałem tego od kilku godzin, był to pierwszy artykuł, który przeczytałem i chociaż ma najlepszą odpowiedź, najpopularniejsze odpowiedzi, które myślę, są poza zasięgiem. Oczywiście za to, czego potrzebowałem. odpowiedzi weichsela i shakazeda były dokładnie na przycisku, ale trochę zaciemnione w artykułach. Żeby cię skierować do projektu. Proszę.: Wystarczy utworzyć nowy projekt Android na podstawie istniejącej próbki. Wybierz ApiDemos:

Zajrzyj pod folder źródłowy

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

I ty znajdziesz wszystko, czego potrzebujesz.

 2
Author: Justin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-09 12:51:26

Dla statyczny tekst :

  • Wygeneruj obraz ze wszystkimi słowami używanymi na komputerze (na przykład w GIMP).
  • załaduj to jako teksturę i użyj jako materiału na płaszczyznę.

Dla długiego tekstu , który trzeba aktualizować raz na jakiś czas:

  • NIECH android rysuje na płótnie bitmapowym (rozwiązanie JVitela).
  • załaduj to jako materiał na samolot.
  • Użyj różnych współrzędnych tekstury dla każdego słowa.

Dla liczby (sformatowany 00.0):

  • Wygeneruj obrazek ze wszystkimi cyframi i kropką.
  • załaduj to jako materiał na samolot.
  • Użyj poniżej shader.
  • W zdarzeniu onDraw zaktualizuj tylko zmienną value wysłaną do shadera.

    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;
    
    }
    

Powyższy kod działa dla atlasu tekstur, gdzie cyfry zaczynają się od 0 w 7. kolumnie 2. wiersza atlasu czcionek (Tekstura).

Zobacz https://www.shadertoy.com/view/Xl23Dw do demonstracji (z błędem tekstury)

 1
Author: Pete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-01 21:37:41

W OpenGL ES 2.0 / 3.0 można również połączyć widok OGL i UI-elements Androida:

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);
        ...
    }
}

Utwórz layout 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>

Aby zaktualizować elementy z wątku renderowania, można użyć funkcji Handler/Looper.

 0
Author: alexrnov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-16 10:54:20