Гайд Клиент с нуля | Текст, шрифты, Атлас

PoC Life
Пользователь
Статус
Оффлайн
Регистрация
22 Авг 2022
Сообщения
331
Реакции[?]
47
Поинты[?]
37K

Перед прочтением основного контента ниже, пожалуйста, обратите внимание на обновление внутри секции Майна на нашем форуме. У нас появились:

  • бесплатные читы для Майнкрафт — любое использование на свой страх и риск;
  • маркетплейс Майнкрафт — абсолютно любая коммерция, связанная с игрой, за исключением продажи читов (аккаунты, предоставления услуг, поиск кодеров читов и так далее);
  • приватные читы для Minecraft — в этом разделе только платные хаки для игры, покупайте группу "Продавец" и выставляйте на продажу свой софт;
  • обсуждения и гайды — всё тот же раздел с вопросами, но теперь модернизированный: поиск нужных хаков, пати с игроками-читерами и другая полезная информация.

Спасибо!

Список:
1. Создаём главный класс + снимаем ограничения - https://yougame.biz/threads/325113
2. Автобус евентов - https://yougame.biz/threads/325114
3. OpenGL + Шейдеры + Рендер - https://yougame.biz/threads/325115
4. Аим - https://yougame.biz/threads/325188
5. GlowESP aka OpenGL Framebuffer - https://yougame.biz/threads/325211/
6. Текст, шрифты, атлас - https://yougame.biz/threads/325292

Есть много вариаций как делать эти шрифты, сглаживать их, например: MisterL_43123 рассказал о MSDF (https://yougame.biz/threads/301776). Лично я опять же расскажу самый популярный способ в читах на текущий момент.

Начнём с того, что мы будем использовать java.awt - библиотека, входящая в jdk, отвечающая за рендер. У неё также есть свой враппер javax.swing, но его мы не затрагиваем.

Текстурный атлас
Начнём с того, что шрифт - грубо говоря это текстура. Но не могу же я генерировать триллиард текстур под каждую буковку, шрифт с опред. размером? Поэтому мы будем использовать одну огромную картинку - текстурный атлас, где одна картинка поделена на подкартинки, которые в дальнейшем можно извлекать используя UV. UV - это обычные нормализованные координаты (от 0 до 1).

Допустим есть такая картинка:

Давайте попробуем нарисовать не всю её, а только нижнюю левую "плиточку". Для этого высчитаем UV:
u = x / размер картинки
v = y / размер картинки
И затем отрисуем (не хочу дублировать код, поэтому читайте ниже, там будет где я использую uv)

Практика
Делать такие атласы мы естественно будем автоматом, для этого у нас есть BufferedImage и Graphics2D для отрисовки.

Но для начала нужно получить шрифт (обратите внимание, что я использовал конструкцию try для автоматического закрытия InputStream):
Java:
try (FileInputStream inputStream = new FileInputStream("путь/до/шрифта")) {
    Font font = Font.createFont(Font.PLAIN, inputStream);
    font = font.deriveFont(Font.PLAIN, размер);
}
После этого создаём картинку:
Java:
BufferedImage image = new BufferedImage(512, 512, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
graphics.setColor(new Color(0, 0, 0, 0));
graphics.fillRect(0, 0, 512, 512);

graphics.dispose();
Чтобы шрифты не были пиксельными (alias), нужно использовать antialiasing, он уже идёт из коробки:
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Также можно включить
Пожалуйста, авторизуйтесь для просмотра ссылки.
.
Далее я создам класс Glyph с информацией о символе где я сохраню uv и размер:
Java:
public class Glyph {

    private final int width, height;
    private final float u, v;
     
    public Glyph(int width, int height, float u, float v) {
        this.width = width;
        this.height = height;
        this.u = u;
        this.v = v;
    }
     
    public int getWidth() {
        return width;
    }
     
    public int getHeight() {
        return height;
    }
     
    public float getU() {
        return u;
    }
     
    public float getV() {
        return v;
    }
}
Далее наносим необходимые символы на картинку:
Java:
Glyph glyphs[1300];

FontMetrics fontMetrics = graphics.getFontMetrics(); // Получаем информацию о metrics
int x = 0;
int y = 0;
for (int i = 0; i < glyphs.length; i++) {
    char character = (char) i;
    // Фильтрация ненужных символов
    if (character < '!')
        continue;

    if (character > '~' && character < 1040) // 1040 - Русская А
        continue;
     
    Rectangle2D size = fontMetrics.getStringBounds(String.valueOf(character), graphics); // Получаем размеры символа
    int width = size.getBounds().width + 5; // 5 - расстояние между символами, а то чё они впритык ебать будут
    int height = size.getBounds().height;
     
    if (x + width >= 512) { // Если x вышел за пределы размера картинки
        x = 0;
        y += height + fontMetrics.getDescent(); // То перемещаемся ниже
    }
     
    Glyph glyph = new Glyph(width, height, x / 512.0f, y / 512.0f);
    glyphs[i] = glyph; // Устанавливаем информацию для символа в массив
     
    graphics.drawString(String.valueOf(character), x, y + fontMetrics.getAscent()); // Рисуем символ (в данном случае целую строку из одного символа)
    x += glyph.getWidth() + 5; // Между символами создадим небольшой отступ
}
Про asecnt и descent можно увидеть здесь:

В итоге можно посмотреть результат через ImageIO.write:

Далее необходимо создать OpenGL текстуру и записать туда информацию о цветах всех пикселей:
Java:
int[] pixels = image.getRGB(0, 0, 512, 512, null, 0, 512);
ByteBuffer byteBuffer = BufferUtils.createByteBuffer(pixels.length * 4); // Создаём буффер цветов rgba
for (int color : pixels) {
    byteBuffer.put((byte) ((color >> 16) & 0xFF)); // r
    byteBuffer.put((byte) ((color >> 8) & 0xFF)); // g
    byteBuffer.put((byte) (color & 0xFF)); // b
    byteBuffer.put((byte) ((color >> 24) & 0xFF)); // b
}
byteBuffer.flip();

int textureID = GlStateManager.genTexture(); // Создаём новую текстуру
GlStateManager.bindTexture(textureID);
GlStateManager.texParameter(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR);
GlStateManager.texParameter(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_LINEAR);
GL30.glTexImage2D(GL30.GL_TEXTURE_2D, 0, GL30.GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, byteBuffer); // Заполняем текстуру информацией о цветах
GlStateManager.bindTexture(0);
Теперь у нас есть OpenGL текстура и информация о каждом символе. Давайте создадим метод отрисовки строки:
Java:
GlStateManager.bindTexture(textureID);

for (char character : text.toCharArray()) {
    if (character >= glyphs.length) // Если вдруг случайно захотели нарисовать экзотический символ, которого нету в массиве :)
        continue;
     
    Glyph glyph = glyphs[character];
    float u = glyph.getU();
    float v = glyph.getV();
     
    Tessellator tessellator = Tessellator.getInstance();
    BufferBuilder builder = tessellator.getBuffer();
    builder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
    builder.pos(x, y, 0).tex(u, v).endVertex();
    builder.pos(x, y + glyph.getHeight(), 0).tex(u, v + glyph.getHeight() / 512.0f).endVertex();
    builder.pos(x + glyph.getWidth(), y + glyph.getHeight(), 0).tex(u + glyph.getWidth() / 512.0f, v + glyph.getHeight() / 512.0f).endVertex();
    builder.pos(x + glyph.getWidth(), y, 0).tex(u + glyph.getWidth() / 512.0f, v).endVertex();
    tessellator.draw();
     
    x += glyph.getWidth() - 5; // Не забываем вычесть отступ
}

GlStateManager.bindTexture(0);
Усё:
Pasted image 20240721010953.png
 
Последнее редактирование:
(◣_◢)
Участник
Статус
Оффлайн
Регистрация
1 Фев 2021
Сообщения
447
Реакции[?]
152
Поинты[?]
71K
судя по количеству комментариев, этот гайд(как, впрочем, и остальные) нахуй никому не всрался :tearsofjoy:
потому что все всёравно идут качять эксп :tearsofjoy:
Я не с целью оскорбить понимаеш бро, тут просто поход людям не по интеллекту такие гайды(pon)
 
PoC Life
Пользователь
Статус
Оффлайн
Регистрация
22 Авг 2022
Сообщения
331
Реакции[?]
47
Поинты[?]
37K
судя по количеству комментариев, этот гайд(как, впрочем, и остальные) нахуй никому не всрался :tearsofjoy:
потому что все всёравно идут качять эксп :tearsofjoy:
Я не с целью оскорбить понимаеш бро, тут просто поход людям не по интеллекту такие гайды(pon)
мне похуй я оставляю свою частичку знаний
 
Сверху Снизу