Нарисуйте текст в OpenGL ES

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

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

Любые идеи?

Ответ 1

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

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

Ответ 2

Рендеринг текста в текстуру проще, чем то, что делает демоверсия Sprite Text, похоже, основная идея состоит в том, чтобы использовать класс Canvas для рендеринга в Bitmap, а затем передать Bitmap текстуре 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();

Ответ 3

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

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

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

Ответ 5

Согласно этой ссылке:

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

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

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

Ответ 6

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

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

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

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

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

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

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

  • Создать текстуру для общих меток и предупреждений;
  • Создать текстуру для чисел 0-9, ":", "+" и "-". Одна текстура для каждого символа;
  • Создайте удаленные VBO для всех позиций на экране. Я могу отображать статический или динамический текст в этих позициях, но VBOs являются статическими;
  • Создайте только одну текстуру VBO, так как текст всегда отображается в одном направлении;
  • Во время рисования я создаю статический текст;
  • Для динамического текста я могу заглянуть в позицию VBO, получить текстуру персонажа и нарисовать ее, персонаж за раз.

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

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

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

Ответ 11

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

Посмотрите папку с исходным кодом

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

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

Ответ 12

Для статического текста:

  • Сгенерировать изображение со всеми словами, используемыми на вашем ПК (например, с помощью GIMP).
  • Загрузите это как текстуру и используйте ее как материал для плоскости.

Для длинного текста, который нужно обновлять время от времени:

  • Пусть андроид рисует растровое изображение (решение 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-м столбце второй строки атласа шрифта (текстуры).

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