Вопрос

В настоящее время я разрабатываю небольшую игру OpenGL для платформы Android, и мне интересно, есть ли простой способ отрисовки текста поверх отрисованного фрейма (например, HUD с оценкой игроков и т.д.).В тексте также должен был бы использоваться пользовательский шрифт.

Я видел пример использования представления в качестве наложения, но я не знаю, хочу ли я это делать, поскольку позже, возможно, захочу портировать игру на другие платформы.

Есть какие-нибудь идеи?

Это было полезно?

Решение

В Android SDK нет простого способа рисования текста в представлениях OpenGL.Оставляя вам следующие варианты.

  1. Поместите TextView поверх SurfaceView. Это медленный и плохой, но самый прямой подход.
  2. Преобразуйте общие строки в текстуры и просто рисуйте эти текстуры. Это, безусловно, самый простой и быстрый, но наименее гибкий вариант.
  3. Создайте собственный код рендеринга текста на основе спрайта. Вероятно, второй лучший выбор, если 2 не вариант.Хороший способ намочить ноги, но учтите, что, хотя он кажется простым (а базовые функции таковыми), он становится все сложнее и сложнее по мере добавления дополнительных функций (выравнивание текстур, работа с разрывами строк, шрифты переменной ширины и т. д.). ) — если вы выберете этот путь, сделайте его настолько простым, насколько это возможно!
  4. Используйте готовую библиотеку с открытым исходным кодом. Их несколько, если вы ищете в Google, сложнее всего их интегрировать и запустить.Но, по крайней мере, как только вы это сделаете, у вас появится вся гибкость и зрелость, которые они предоставляют.

Другие советы

Рендеринг текста в текстуру проще, чем то, как это выглядит в демонстрации 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();

Я написал книгу Учебник это дополняет ответ, опубликованный Живителя.По сути, он использует ту же идею, но вместо рендеринга каждой строки в текстуру, он рендерит все символы из файла шрифта в текстуру и использует это для обеспечения полного динамического рендеринга текста без дальнейших замедлений (после завершения инициализации).

Основное преимущество моего метода по сравнению с различными генераторами font atlas заключается в том, что вы можете отправлять файлы небольших шрифтов (.ttf .otf) в свой проект вместо того, чтобы отправлять большие растровые изображения для каждого варианта шрифта и размера.Он может генерировать шрифты идеального качества в любом разрешении, используя только файл шрифта :)

Тот Самый Учебник включает полный код, который может быть использован в любом проекте :)

По этой ссылке:

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

Вы можете визуализировать любой вид в растровое изображение.Вероятно, стоит предположить, что вы можете расположить представление по своему усмотрению (включая текст, изображения и т. д.), а затем преобразовать его в растровое изображение.

Использование кода JVitela выше вы сможете использовать это растровое изображение в качестве текстуры OpenGL.

Взгляните на CBFG и Android-порт кода загрузки/рендеринга.У вас должна быть возможность добавить код в свой проект и сразу же использовать его.

КБФГ - http://www.codehead.co.uk/cbfg

Андроид-загрузчик - http://www.codehead.co.uk/cbfg/TexFont.java

Я посмотрел на пример текста спрайта, и он выглядит ужасно сложным для такой задачи. Я тоже рассматривал возможность рендеринга в текстуру, но меня беспокоит снижение производительности, которое может вызвать.Вместо этого мне, возможно, придется просто заняться просмотром и беспокоиться о портировании, когда придет время пересечь этот мост :)

Посмотрите на образец «Текст спрайта» в Примеры GLSurfaceView.

ИМХО есть три причины использовать OpenGL ES в игре:

  1. Избегайте различий между мобильными платформами, используя открытый стандарт;
  2. Чтобы иметь больше контроля над процессом рендеринга;
  3. Чтобы получить выгоду от параллельной обработки графического процессора;

Рисование текста всегда является проблемой в игровом дизайне, потому что вы рисуете вещи, поэтому у вас не может быть внешнего вида обычного действия с виджетами и так далее.

Вы можете использовать платформу для создания растровых шрифтов из шрифтов TrueType и их рендеринга.Все фреймворки, которые я видел, работают одинаково:генерировать координаты вершин и текстур для текста во время отрисовки.Это не самое эффективное использование OpenGL.

Лучший способ — выделить удаленные буферы (объекты буфера вершин — VBO) для вершин и текстур на ранних этапах кода, избегая ленивых операций передачи памяти во время отрисовки.

Имейте в виду, что игроки не любят читать текст, поэтому вы не будете писать длинный динамически генерируемый текст.Для меток вы можете использовать статические текстуры, оставляя динамический текст для времени и оценки, и оба они являются числовыми и имеют длину несколько символов.

Итак, мое решение простое:

  1. Создайте текстуру для общих надписей и предупреждений;
  2. Создайте текстуру для цифр 0–9, «:», «+» и «-».Одна текстура для каждого персонажа;
  3. Сгенерируйте удаленные VBO для всех позиций на экране.Я могу отображать статический или динамический текст в этих позициях, но VBO статичны;
  4. Создайте только одну текстуру VBO, поскольку текст всегда отображается в одном направлении;
  5. Во время рисования я визуализирую статический текст;
  6. Для динамического текста я могу посмотреть позицию VBO, получить текстуру персонажа и нарисовать ее по одному символу.

Операции рисования выполняются быстро, если вы используете удаленные статические буферы.

Я создаю XML-файл с позициями экрана (в зависимости от процента диагонали экрана) и текстурами (статическими и символьными), а затем загружаю этот XML перед рендерингом.

Чтобы получить высокий показатель FPS, вам следует избегать создания VBO во время отрисовки.

Если вы настаиваете на использовании GL, вы можете визуализировать текст в текстурах.Предполагая, что большая часть HUD относительно статична, вам не придется слишком часто загружать текстуры в текстурную память.

Взгляните на CBFG и порт Android для загрузки / рендеринга кода.Вы должны быть в состоянии поместить код в свой проект и сразу же использовать его .

  1. CBFG

  2. Загрузчик для Android

У меня есть проблемы с этой реализацией.Он отображает только один символ, когда я пытаюсь изменить размер растрового изображения шрифта (мне нужны специальные буквы), весь отрисовка завершается неудачей:(

Я искал это несколько часов, это была первая статья, на которую я наткнулся, и хотя в ней есть лучший ответ, самые популярные ответы, я думаю, не соответствуют действительности.Конечно, для того, что мне было нужно.Ответы weichsel и shakazed были точными, но немного неясными в статьях.Чтобы познакомить вас с проектом.Здесь:Просто создайте новый проект Android на основе существующего образца.Выберите ApiDemos:

Посмотрите в исходной папке

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

И вы найдете все, что вам нужно.

Для статический текст:

  • Создайте изображение со всеми словами, используемыми на вашем компьютере (например, с помощью 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;
    
    }
    

Приведенный выше код работает для атласа текстур, где числа начинаются с 0 в 7-м столбце 2-й строки атласа шрифтов (текстуры).

Ссылаться на https://www.shadertoy.com/view/Xl23Dw для демонстрации (правда, с неправильной текстурой)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top