Obecnie tworzę małą grę OpenGL na platformę Android i zastanawiam się, czy istnieje łatwy sposób renderowania tekstu na wyrenderowanej ramce (jak HUD z wynikiem gracza itp.). Tekst musiałby również używać niestandardowej czcionki.
Widziałem przykład używający widoku jako nakładki, ale nie wiem, czy chcę to zrobić, ponieważ mógłbym później przenieść grę na inne platformy.
Jakieś pomysły?
android
opengl-es
text-rendering
wstrząśnięty
źródło
źródło
Odpowiedzi:
Android SDK nie zapewnia łatwego sposobu rysowania tekstu w widokach OpenGL. Pozostawiając Ci następujące opcje.
źródło
Renderowanie tekstu do tekstury jest prostsze niż to, jak wygląda demo Sprite Text, podstawową ideą jest użycie klasy Canvas do renderowania do mapy bitowej, a następnie przekazanie mapy bitowej 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();
źródło
Napisałem samouczek, który rozszerza odpowiedź opublikowaną przez JVitela . Zasadniczo używa tego samego pomysłu, ale zamiast renderować każdy ciąg do tekstury, renderuje wszystkie znaki z pliku czcionki na teksturę i wykorzystuje to, aby umożliwić pełne dynamiczne renderowanie tekstu bez dalszych spowolnień (po zakończeniu inicjalizacji) .
Główną zaletą mojej metody, w porównaniu z różnymi generatorami atlasów czcionek, jest to, że możesz dostarczać z projektem małe pliki czcionek (.ttf .otf), zamiast wysyłać duże mapy bitowe dla każdej odmiany i rozmiaru czcionki. Potrafi generować czcionki doskonałej jakości w dowolnej rozdzielczości, używając tylko pliku czcionki :)
Poradnik zawiera pełny kod, który może być używany w każdym projekcie :)
źródło
Zgodnie z tym linkiem:
http://code.neenbedankt.com/how-to-render-an-android-view-to-a-bitmap
Możesz renderować dowolny widok do mapy bitowej. Prawdopodobnie warto założyć, że możesz ułożyć widok zgodnie z wymaganiami (w tym tekst, obrazy itp.), A następnie wyrenderować go na mapę bitową.
Przy użyciu kodu JVitela za powyżej powinny być w stanie używać tej bitmapy jako OpenGL tekstury.
źródło
Spójrz na CBFG i port Androida w kodzie ładowania / renderowania. Powinieneś móc upuścić kod do projektu i od razu go używać.
CBFG - http://www.codehead.co.uk/cbfg
Program ładujący dla Androida - http://www.codehead.co.uk/cbfg/TexFont.java
źródło
Przyjrzałem się przykładowi tekstu sprite'a i wygląda to na strasznie skomplikowane jak na takie zadanie, rozważałem również renderowanie do tekstury, ale martwię się o spadek wydajności, który może spowodować. Być może będę musiał po prostu pójść z widokiem i martwić się o przeniesienie, kiedy nadejdzie czas na przekroczenie tego mostu :)
źródło
Spójrz na przykład „Sprite Text” w przykładach GLSurfaceView .
źródło
IMHO Istnieją trzy powody, dla których warto używać OpenGL ES w grze:
Rysowanie tekstu jest zawsze problemem w projektowaniu gier, ponieważ rysujesz rzeczy, więc nie możesz mieć wyglądu i stylu typowego działania, z widżetami i tak dalej.
Możesz użyć struktury do generowania czcionek bitmapowych z czcionek TrueType i renderowania ich. Wszystkie struktury, które widziałem, działają w ten sam sposób: generuj współrzędne wierzchołka i tekstury dla tekstu w czasie rysowania. Nie jest to najbardziej efektywne wykorzystanie OpenGL.
Najlepszym sposobem jest przydzielenie zdalnych buforów (obiektów buforów wierzchołków - VBO) dla wierzchołków i tekstur na wczesnym etapie 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 generowanego tekstu. W przypadku etykiet można używać tekstur statycznych, pozostawiając tekst dynamiczny na czas i punktację, a oba są numeryczne i składają się z kilku znaków.
Moje rozwiązanie jest więc proste:
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 (statyczne i znaki), a następnie ładuję ten XML przed renderowaniem.
Aby uzyskać wysoki współczynnik FPS, należy unikać generowania VBO w czasie rysowania.
źródło
Jeśli nalegasz na używanie GL, możesz renderować tekst na teksturach. Zakładając, że większość HUD jest stosunkowo statyczna, nie powinieneś zbyt często ładować tekstur do pamięci tekstur.
źródło
Spójrz na
CBFG
port Androida w kodzie ładowania / renderowania. Powinieneś móc upuścić kod do projektu i od razu go używać.CBFG
Moduł ładujący Androida
Mam problemy z tą realizacją. Wyświetla tylko jeden znak, kiedy próbuję zmienić rozmiar bitmapy czcionki (potrzebuję specjalnych liter), całe rysowanie kończy się niepowodzeniem :(
źródło
Szukałem tego od kilku godzin, był to pierwszy artykuł, z którym się spotkałem i chociaż zawiera najlepszą odpowiedź, najpopularniejsze odpowiedzi, moim zdaniem, są chybione. Z pewnością na to, czego potrzebowałem. Odpowiedzi weichsel i shakazed były tuż przy przycisku, ale w artykułach były nieco niejasne. Aby skierować Cię prosto do projektu. Tutaj: Po prostu utwórz nowy projekt systemu Android na podstawie istniejącej próbki. Wybierz ApiDemos:
Zajrzyj do folderu źródłowego
ApiDemos/src/com/example/android/apis/graphics/spritetext
Znajdziesz wszystko, czego potrzebujesz.
źródło
W przypadku tekstu statycznego :
W przypadku długich tekstów, które wymagają od czasu do czasu aktualizacji:
W przypadku liczby (w formacie 00.0):
W zdarzeniu onDraw aktualizuj tylko zmienną wartość wysłaną do modułu cieniującego.
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, w którym liczby zaczynają się od 0 w siódmej kolumnie drugiego rzędu atlasu czcionek (tekstury).
Zapoznaj się z https://www.shadertoy.com/view/Xl23Dw, aby uzyskać demonstrację (chociaż z niewłaściwą teksturą)
źródło
W OpenGL ES 2.0 / 3.0 możesz także łączyć OGL View i elementy UI 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 układ 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żesz użyć Handler / Looper.
źródło