Гайд Клиент с нуля | Рендер + OpenGL + Шейдеры

PoC Life
Пользователь
Пользователь
Статус
Оффлайн
Регистрация
22 Авг 2022
Сообщения
676
Реакции
68

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

  • бесплатные читы для Майнкрафт — любое использование на свой страх и риск;
  • маркетплейс Майнкрафт — абсолютно любая коммерция, связанная с игрой, за исключением продажи читов (аккаунты, предоставления услуг, поиск кодеров читов и так далее);
  • приватные читы для 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

OpenGL
Конкретно в рендере мы остановимся по подробнее. Майнкрафт использует для рендера API OpenGL, считайте, чтобы напрямую не обращаться к видеокарте мы используем движок для отрисовки.

У данного API есть своя особенность, у него есть так скажем "переключатели". Каждый переключатель за что-то отвечает.
Java:
Expand Collapse Copy
GL11.glEnable(константа); // Включить переключатель
GL11.glDisable(константа); // Выключить переключатель
Сейчас расскажу про каждый.

Переключатель глубины (Depth test)
Константа: GL11.GL_DEPTH_TEST
Выкл:
Pasted image 20240705011107.png

Вкл:
Pasted image 20240705011123.png


Отсечение граней
Константа: GL11.GL_CULL_FACE
Если переключатель включен, то отрисовка будет только с одной стороны.

Вкл:
С одной стороны:
Pasted image 20240705010705.png

С другой:
5hn9dkU.png

Выкл - означает, что рисует всегда.

Blend (Смешивание)
Константа: GL11.GL_BLEND
Самая важный переключатель для нас, он позволяет смешивать цвета при наложении. Аргументов там много, можете посмотреть их в интернете, но мы будем использовать такие:
Java:
Expand Collapse Copy
GL11.blendFuncSeparate(GL11.SRC_ALPHA, GL11.ONE_MINUS_SRC_ALPHA, GL11.ONE, GL11.ZERO);
Вкл:
LlluH1h.png

Выкл:
vZIewDd.png


Отрисовка примитивов
Будем продолжать изучение OpenGL постепенно. Раньше чтобы отрисовывать примитивы нужно было использовать glBegin и glEnd, но это максимально не оптимизированно и начиная с хуй пойми какой версии OpenGL 3 их убрали полностью, поэтому стоит использовать либо glDrawArrays, либо glDrawElements. Но за нас уже это сделали разработчики майнкрафта в удобном классе Tessellator и BufferBuilder.

Для начала нужно получить их:
Java:
Expand Collapse Copy
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Далее выбрать примитив и формат вершин (максимально странное название):
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
Вот все примитивы:
  • GL_POINTS — каждая вершина задает точку
  • GL_LINES — каждая отдельная пара вершин задает линию
  • GL_LINE_STRIP — каждая пара вершин задает линию (т.е. конец предыдущей линии является началом следующей)
  • GL_LINE_LOOP — аналогично предыдущему за исключением того, что последняя вершина соединяется с первой и получается замкнутая фигура
  • GL_TRIANGLES — каждая отдельная тройка вершин задает треугольник
  • GL_TRIANGLE_STRIP — каждая следующая вершина задает треугольник вместе с двумя предыдущими (получается лента из треугольников)
  • GL_TRIANGLE_FAN — каждый треугольник задается первой вершиной и последующими парами (т.е. треугольники строятся вокруг первой вершины, образуя нечто похожее на диафрагму)
  • GL_QUADS — каждые четыре вершины образуют четырехугольник
  • GL_QUAD_STRIP — каждая следующая пара вершин образует четырехугольник вместе с парой предыдущих
  • GL_POLYGON — задает многоугольник с количеством углов равным количеству заданных вершин

И форматы вершин:
  • POSITION - все вершины имеют один цвет
  • POSITION_COLOR - каждая вершина имеет свой цвет
  • POSITION_TEX - каждая вершина имеет свою позицию текстуры (UV)
  • POSITION_COLOR_TEX - каждая вершина имеет свой цвет, который смешивается с текстурой
Взято с
Пожалуйста, авторизуйтесь для просмотра ссылки.
(noad)

Далее обозначить вершины и матрицу (вот только попробуйте забыть про матрицу, я потом покажу зачем это и какие приколы можно делать):
Java:
Expand Collapse Copy
buffer.pos(matrix, 0, 0, 0).color(255, 0, 0, 255).endVertex();
buffer.pos(matrix, 0, 50, 0).color(0, 255, 0, 255).endVertex();
buffer.pos(matrix, 100, 50, 0).color(0, 0, 255, 255).endVertex();
buffer.pos(matrix, 100, 0, 0).color(255, 255, 255, 255).endVertex();
И отрисовать:
tessellator.draw();

Получается сейчас мы должны увидеть прямоугольник с 4 цветами, но мы идём нахуй:
T6n1e1y.png

А всё потому, что помимо blend нужно включить ещё и shade, для создания градиента:
Java:
Expand Collapse Copy
// Включить
GL11.glShadeModel(GL11.GL_SMOOTH);

// Отрисовать

// Отключить
GL11.glShadeModel(GL11.GL_FLAT);
0i1wodz.png

Вот теперь красота

Шейдеры
В коде, который вы видели ранее мы использовали "форматы вершин", в которых выбирали, что же конкретно нам рендерить, цвет, текстуру или всё сразу. На самом деле они просто переключали шейдер, вы даже можете посмотреть их код открыв .jar файл версии через архиватор и посмотреть в ассетах файлы с расширениями .fsh и .vsh. Но что же это и зачем нам это нужно?

Представим ситуацию, вы хотите нарисовать круг. Сейчас вы думаете, вроде всё логично, я буду рисовать линии (GL_LINE_STRIP) а точки по формуле круга (x = cos(radians(angle)) * radius, y = sin(radians(angle)) * radius). Я сделал это за вас, чтобы показать проблему:
HTZQ38j.png

P.S. Если не видите проблемы - приблизьтесь к монитору

Проблема заключается в том, что на элементы не накладывается сглаживание (antialiasing) и создаётся эффект "пиксельности". Очень давно был придуман лайфхак в котором нужно было рисовать точками (GL_POINTS) и включить GL_POINT_SMOOTH, но при настройках жирности как на моём скриншоте он всё равно также пиксельный.
UPD: Поправочка, пиксельность и означает "алиасинг", когда в процессе растеризации вектор проходит сквозь несколько пикселей, процесс фикса этого - "анти" (алиасинг)

Для этого приходится использовать шейдер. Шейдеры бывают двух типов: Фрагментные - под каждый пиксель на экране вызывается функция, которая должна установить цвет данному пикселю и Вершинный - шейдер, который определяет позицию пикселю, это нужно, например: чтобы быстро домножать точки на матрицу.

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

P.S. Данный способ работает на версиях 1.20
Давайте напишем наш первый шейдер, который будет просто красить пиксель в указанный цвет:
Код:
Expand Collapse Copy
#version 330 core // Версия шейдера, 330 означает OpenGL 3.3 с профилем core

uniform vec4 col; // Юниформа цвета, это параметр, который передаётся из кода в шейдер

out vec4 fragColor; // Переменная, в которую мы запишем цвет пикселя

void main() { // Основная функция
    fragColor = col; // Устанавливаем цвет пикселя на uniform
}

Теперь нужно шейдер создать, для этого воспользуемся уже существующими методами создания из майнкрафта, а именно классом ShaderInstance. Данный класс требует, чтобы мы создали дополнительные 2 папки: shaders и внутри core. Внутри папки core создадим .json файл с названием шейдера. Внутри него вставляем шаблон (вы можете найти шаблон сами в шейдерах майнкрафта):
JSON:
Expand Collapse Copy
{
    "blend": {
        "func": "add",
        "srcrgb": "srcalpha",
        "dstrgb": "1-srcalpha"
    },
    "vertex": "",
    "fragment": "",
    "attributes": [
    ],
    "samplers": [
    ],
    "uniforms": [
        { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
        { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }
    ]
}
Сейчас расскажу, что к чему. Как вы могли догадаться - blend это аргументы для функции blendFuncSeparate. Далее идёт vertex и fragment - это название файлов для вершинного (vertex) и фрагментного (fragment) шейдера. Туда указываете свои названия, кстати, вершинный шейдер вам явно не придётся изменять, так что также возьмите шаблон:
Код:
Expand Collapse Copy
#version 330 core

in vec3 Position; // Позиция вершины

uniform mat4 ModelViewMat; // Матрица model * view
uniform mat4 ProjMat; // Матрица проекции

void main() {
    mat4 mvp = ModelViewMat * ProjMat; // Матрица modelViewProjection.
    gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0);
}
Подробнее об этом можно ознакомится здесь (noad):
Пожалуйста, авторизуйтесь для просмотра ссылки.


Далее по желанию вы можете указать юниформы, у нас это цвет:
JSON:
Expand Collapse Copy
{ "name": "color", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
И атрибуты:
JSON:
Expand Collapse Copy
"attributes": [
    "Color"
]

Приступаем к коду на джаве. Создадим ShaderInstance:
Java:
Expand Collapse Copy
ShaderInstance shader = new ShaderInstance(Minecraft.getInstance().getResourceManager(), new ResourceLocation("название шейдера без .json"), DefaultVertexFormat.POSITION); // Подсказка: Если вы в дальнейшем будете писать мод, то у forge свой менеджер ресурсов
Рисуем прямоугольник как обычно, но есть нюанс, перед begin следует включить шейдер, а после отрисовки выключить.
На версиях 1.20 и выше это делается с помощью:
RenderSystem.setShader(() -> shader);
На версиях ниже используется:
GL20.glUseProgram(shader.getProgram());

До включения шейдера требуется установить значения юниформам (если они есть).

1.20+:
shader.getUniform("название").set(1.0f, 0.0f, 0.0f, 1.0f);
Ниже 1.17:
glUniform4f(glGetUniformLocation(shader.getProgram(), "название"), 1.0f, 0.0f, 0.0f, 1.0f);

Ну и ниже 1.17 после отрисовки следует отключить шейдер (на версиях выше это происходит автоматически):
GL20.glUseProgram(0);

Ну вот и всё, давайте проверим (кстати тестировал на forge 1.20.1):
RmGw2Wz.png

Да, действительно всё работает. Всё же, мы хотели круг, гуглим: circle sdf shadertoy и натыкаемся на первую ссылочку (noad):
Пожалуйста, авторизуйтесь для просмотра ссылки.
Переносим на язык glsl (отличий очень мало) и получаем результат:
oZB0qPe.png

Вот вам круг, немного я проебался, но и хуй с ним :)
 
Последнее редактирование:
Благодарю, вот это нормальная темка, для закрепление пойдет )
 
я добавил нумерацию к каждой теме. следующие темы будут чуть позже, мб через час
Такими темами, югейм станет онли селфкодом, ну если кто то реально захочет разобраться, ладно не буду мешать, жду новенькую тему ?
 
Список:
1. Создаём главный класс + снимаем ограничения - https://yougame.biz/threads/325113
2. Автобус евентов - https://yougame.biz/threads/325114
3. OpenGL + Шейдеры + Рендер - https://yougame.biz/threads/325115

OpenGL
Конкретно в рендере мы остановимся по подробнее. Майнкрафт использует для рендера API OpenGL, считайте, чтобы напрямую не обращаться к видеокарте мы используем движок для отрисовки.

У данного API есть своя особенность, у него есть так скажем "переключатели". Каждый переключатель за что-то отвечает.
Java:
Expand Collapse Copy
GL11.glEnable(константа); // Включить переключатель
GL11.glDisable(константа); // Выключить переключатель
Сейчас расскажу про каждый.

Переключатель глубины (Depth test)
Константа: GL11.GL_DEPTH_TEST
Выкл:
Посмотреть вложение 281649
Вкл:
Посмотреть вложение 281650

Отсечение граней
Константа: GL11.GL_CULL_FACE
Если переключатель включен, то отрисовка будет только с одной стороны.

Вкл:
С одной стороны:
Посмотреть вложение 281651
С другой:
5hn9dkU.png

Выкл - означает, что рисует всегда.

Blend (Смешивание)
Константа: GL11.GL_BLEND
Самая важный переключатель для нас, он позволяет смешивать цвета при наложении. Аргументов там много, можете посмотреть их в интернете, но мы будем использовать такие:
Java:
Expand Collapse Copy
GL11.blendFuncSeparate(GL11.SRC_ALPHA, GL11.ONE_MINUS_SRC_ALPHA, GL11.ONE, GL11.ZERO);
Вкл:
LlluH1h.png

Выкл:
vZIewDd.png


Отрисовка примитивов
Будем продолжать изучение OpenGL постепенно. Раньше чтобы отрисовывать примитивы нужно было использовать glBegin и glEnd, но это максимально не оптимизированно и начиная с хуй пойми какой версии OpenGL 3 их убрали полностью, поэтому стоит использовать либо glDrawArrays, либо glDrawElements. Но за нас уже это сделали разработчики майнкрафта в удобном классе Tessellator и BufferBuilder.

Для начала нужно получить их:
Java:
Expand Collapse Copy
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Далее выбрать примитив и формат вершин (максимально странное название):
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
Вот все примитивы:
  • GL_POINTS — каждая вершина задает точку
  • GL_LINES — каждая отдельная пара вершин задает линию
  • GL_LINE_STRIP — каждая пара вершин задает линию (т.е. конец предыдущей линии является началом следующей)
  • GL_LINE_LOOP — аналогично предыдущему за исключением того, что последняя вершина соединяется с первой и получается замкнутая фигура
  • GL_TRIANGLES — каждая отдельная тройка вершин задает треугольник
  • GL_TRIANGLE_STRIP — каждая следующая вершина задает треугольник вместе с двумя предыдущими (получается лента из треугольников)
  • GL_TRIANGLE_FAN — каждый треугольник задается первой вершиной и последующими парами (т.е. треугольники строятся вокруг первой вершины, образуя нечто похожее на диафрагму)
  • GL_QUADS — каждые четыре вершины образуют четырехугольник
  • GL_QUAD_STRIP — каждая следующая пара вершин образует четырехугольник вместе с парой предыдущих
  • GL_POLYGON — задает многоугольник с количеством углов равным количеству заданных вершин

И форматы вершин:
  • POSITION - все вершины имеют один цвет
  • POSITION_COLOR - каждая вершина имеет свой цвет
  • POSITION_TEX - каждая вершина имеет свою позицию текстуры (UV)
  • POSITION_COLOR_TEX - каждая вершина имеет свой цвет, который смешивается с текстурой
Взято с
Пожалуйста, авторизуйтесь для просмотра ссылки.
(noad)

Далее обозначить вершины и матрицу (вот только попробуйте забыть про матрицу, я потом покажу зачем это и какие приколы можно делать):
Java:
Expand Collapse Copy
buffer.pos(matrix, 0, 0, 0).color(255, 0, 0, 255).endVertex();
buffer.pos(matrix, 0, 50, 0).color(0, 255, 0, 255).endVertex();
buffer.pos(matrix, 100, 50, 0).color(0, 0, 255, 255).endVertex();
buffer.pos(matrix, 100, 0, 0).color(255, 255, 255, 255).endVertex();
И отрисовать:
tessellator.draw();

Получается сейчас мы должны увидеть прямоугольник с 4 цветами, но мы идём нахуй:
T6n1e1y.png

А всё потому, что помимо blend нужно включить ещё и shade, для создания градиента:
Java:
Expand Collapse Copy
// Включить
GL11.glShadeModel(GL11.GL_SMOOTH);

// Отрисовать

// Отключить
GL11.glShadeModel(GL11.GL_FLAT);
0i1wodz.png

Вот теперь красота

Шейдеры
В коде, который вы видели ранее мы использовали "форматы вершин", в которых выбирали, что же конкретно нам рендерить, цвет, текстуру или всё сразу. На самом деле они просто переключали шейдер, вы даже можете посмотреть их код открыв .jar файл версии через архиватор и посмотреть в ассетах файлы с расширениями .fsh и .vsh. Но что же это и зачем нам это нужно?

Представим ситуацию, вы хотите нарисовать круг. Сейчас вы думаете, вроде всё логично, я буду рисовать линии (GL_LINE_STRIP) а точки по формуле круга (x = cos(radians(angle)) * radius, y = sin(radians(angle)) * radius). Я сделал это за вас, чтобы показать проблему:
HTZQ38j.png

P.S. Если не видите проблемы - приблизьтесь к монитору

Проблема заключается в том, что на элементы не накладывается сглаживание (antialiasing) и создаётся эффект "пиксельности". Очень давно был придуман лайфхак в котором нужно было рисовать точками (GL_POINTS) и включить GL_POINT_SMOOTH, но при настройках жирности как на моём скриншоте он всё равно также пиксельный. Для этого приходится использовать шейдер. Шейдеры бывают двух типов: Фрагментные - под каждый пиксель на экране вызывается функция, которая должна установить цвет данному пикселю и Вершинный - шейдер, который определяет позицию пикселю, это нужно, например: чтобы быстро домножать точки на матрицу.

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

P.S. Данный способ работает на версиях 1.20
Давайте напишем наш первый шейдер, который будет просто красить пиксель в указанный цвет:
Код:
Expand Collapse Copy
#version 330 core // Версия шейдера, 330 означает OpenGL 3.3 с профилем core

uniform vec4 col; // Юниформа цвета, это параметр, который передаётся из кода в шейдер

out vec4 fragColor; // Переменная, в которую мы запишем цвет пикселя

void main() { // Основная функция
    fragColor = col; // Устанавливаем цвет пикселя на uniform
}

Теперь нужно шейдер создать, для этого воспользуемся уже существующими методами создания из майнкрафта, а именно классом ShaderInstance. Данный класс требует, чтобы мы создали дополнительные 2 папки: shaders и внутри core. Внутри папки core создадим .json файл с названием шейдера. Внутри него вставляем шаблон (вы можете найти шаблон сами в шейдерах майнкрафта):
JSON:
Expand Collapse Copy
{
    "blend": {
        "func": "add",
        "srcrgb": "srcalpha",
        "dstrgb": "1-srcalpha"
    },
    "vertex": "",
    "fragment": "",
    "attributes": [
    ],
    "samplers": [
    ],
    "uniforms": [
        { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
        { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }
    ]
}
Сейчас расскажу, что к чему. Как вы могли догадаться - blend это аргументы для функции blendFuncSeparate. Далее идёт vertex и fragment - это название файлов для вершинного (vertex) и фрагментного (fragment) шейдера. Туда указываете свои названия, кстати, вершинный шейдер вам явно не придётся изменять, так что также возьмите шаблон:
Код:
Expand Collapse Copy
#version 330 core

in vec3 Position; // Позиция вершины

uniform mat4 ModelViewMat; // Матрица model * view
uniform mat4 ProjMat; // Матрица проекции

void main() {
    mat4 mvp = ModelViewMat * ProjMat; // Матрица modelViewProjection.
    gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0);
}
Подробнее об этом можно ознакомится здесь (noad):
Пожалуйста, авторизуйтесь для просмотра ссылки.


Далее по желанию вы можете указать юниформы, у нас это цвет:
JSON:
Expand Collapse Copy
{ "name": "color", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
И атрибуты:
JSON:
Expand Collapse Copy
"attributes": [
    "Color"
]

Приступаем к коду на джаве. Создадим ShaderInstance:
Java:
Expand Collapse Copy
ShaderInstance shader = new ShaderInstance(Minecraft.getInstance().getResourceManager(), new ResourceLocation("название шейдера без .json"), DefaultVertexFormat.POSITION); // Подсказка: Если вы в дальнейшем будете писать мод, то у forge свой менеджер ресурсов
Рисуем прямоугольник как обычно, но есть нюанс, перед begin следует включить шейдер, а после отрисовки выключить.
На версиях 1.20 и выше это делается с помощью:
RenderSystem.setShader(() -> shader);
На версиях ниже используется:
GL20.glUseProgram(shader.getProgram());

До включения шейдера требуется установить значения юниформам (если они есть).

1.20+:
shader.getUniform("название").set(1.0f, 0.0f, 0.0f, 1.0f);
Ниже 1.17:
glUniform4f(glGetUniformLocation(shader.getProgram(), "название"), 1.0f, 0.0f, 0.0f, 1.0f);

Ну и ниже 1.17 после отрисовки следует отключить шейдер (на версиях выше это происходит автоматически):
GL20.glUseProgram(0);

Ну вот и всё, давайте проверим (кстати тестировал на forge 1.20.1):
RmGw2Wz.png

Да, действительно всё работает. Всё же, мы хотели круг, гуглим: circle sdf shadertoy и натыкаемся на первую ссылочку (noad):
Пожалуйста, авторизуйтесь для просмотра ссылки.
Переносим на язык glsl (отличий очень мало) и получаем результат:
oZB0qPe.png

Вот вам круг, немного я проебался, но и хуй с ним :)
лучшая тема из всех трёх, которые ты написал. молодец.
иди лучше в рендер, тебе он легко даётся
 
Кстати, хочу ещё написать, то что в основном рендер поломан на амд видео картах, он там пиксельнье, даже если взять трейсера, то мы увидим большуб разницу...
 
ахахаха че ты несешь нахуй
 
Я не знаю кем надо быть чтобы не разобраться в теселятора, сука в теселятора в котором блять невозможно заблудиться. Это пиздец
 
Список:
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

OpenGL
Конкретно в рендере мы остановимся по подробнее. Майнкрафт использует для рендера API OpenGL, считайте, чтобы напрямую не обращаться к видеокарте мы используем движок для отрисовки.

У данного API есть своя особенность, у него есть так скажем "переключатели". Каждый переключатель за что-то отвечает.
Java:
Expand Collapse Copy
GL11.glEnable(константа); // Включить переключатель
GL11.glDisable(константа); // Выключить переключатель
Сейчас расскажу про каждый.

Переключатель глубины (Depth test)
Константа: GL11.GL_DEPTH_TEST
Выкл:
Посмотреть вложение 281649
Вкл:
Посмотреть вложение 281650

Отсечение граней
Константа: GL11.GL_CULL_FACE
Если переключатель включен, то отрисовка будет только с одной стороны.

Вкл:
С одной стороны:
Посмотреть вложение 281651
С другой:
5hn9dkU.png

Выкл - означает, что рисует всегда.

Blend (Смешивание)
Константа: GL11.GL_BLEND
Самая важный переключатель для нас, он позволяет смешивать цвета при наложении. Аргументов там много, можете посмотреть их в интернете, но мы будем использовать такие:
Java:
Expand Collapse Copy
GL11.blendFuncSeparate(GL11.SRC_ALPHA, GL11.ONE_MINUS_SRC_ALPHA, GL11.ONE, GL11.ZERO);
Вкл:
LlluH1h.png

Выкл:
vZIewDd.png


Отрисовка примитивов
Будем продолжать изучение OpenGL постепенно. Раньше чтобы отрисовывать примитивы нужно было использовать glBegin и glEnd, но это максимально не оптимизированно и начиная с хуй пойми какой версии OpenGL 3 их убрали полностью, поэтому стоит использовать либо glDrawArrays, либо glDrawElements. Но за нас уже это сделали разработчики майнкрафта в удобном классе Tessellator и BufferBuilder.

Для начала нужно получить их:
Java:
Expand Collapse Copy
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Далее выбрать примитив и формат вершин (максимально странное название):
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
Вот все примитивы:
  • GL_POINTS — каждая вершина задает точку
  • GL_LINES — каждая отдельная пара вершин задает линию
  • GL_LINE_STRIP — каждая пара вершин задает линию (т.е. конец предыдущей линии является началом следующей)
  • GL_LINE_LOOP — аналогично предыдущему за исключением того, что последняя вершина соединяется с первой и получается замкнутая фигура
  • GL_TRIANGLES — каждая отдельная тройка вершин задает треугольник
  • GL_TRIANGLE_STRIP — каждая следующая вершина задает треугольник вместе с двумя предыдущими (получается лента из треугольников)
  • GL_TRIANGLE_FAN — каждый треугольник задается первой вершиной и последующими парами (т.е. треугольники строятся вокруг первой вершины, образуя нечто похожее на диафрагму)
  • GL_QUADS — каждые четыре вершины образуют четырехугольник
  • GL_QUAD_STRIP — каждая следующая пара вершин образует четырехугольник вместе с парой предыдущих
  • GL_POLYGON — задает многоугольник с количеством углов равным количеству заданных вершин

И форматы вершин:
  • POSITION - все вершины имеют один цвет
  • POSITION_COLOR - каждая вершина имеет свой цвет
  • POSITION_TEX - каждая вершина имеет свою позицию текстуры (UV)
  • POSITION_COLOR_TEX - каждая вершина имеет свой цвет, который смешивается с текстурой
Взято с
Пожалуйста, авторизуйтесь для просмотра ссылки.
(noad)

Далее обозначить вершины и матрицу (вот только попробуйте забыть про матрицу, я потом покажу зачем это и какие приколы можно делать):
Java:
Expand Collapse Copy
buffer.pos(matrix, 0, 0, 0).color(255, 0, 0, 255).endVertex();
buffer.pos(matrix, 0, 50, 0).color(0, 255, 0, 255).endVertex();
buffer.pos(matrix, 100, 50, 0).color(0, 0, 255, 255).endVertex();
buffer.pos(matrix, 100, 0, 0).color(255, 255, 255, 255).endVertex();
И отрисовать:
tessellator.draw();

Получается сейчас мы должны увидеть прямоугольник с 4 цветами, но мы идём нахуй:
T6n1e1y.png

А всё потому, что помимо blend нужно включить ещё и shade, для создания градиента:
Java:
Expand Collapse Copy
// Включить
GL11.glShadeModel(GL11.GL_SMOOTH);

// Отрисовать

// Отключить
GL11.glShadeModel(GL11.GL_FLAT);
0i1wodz.png

Вот теперь красота

Шейдеры
В коде, который вы видели ранее мы использовали "форматы вершин", в которых выбирали, что же конкретно нам рендерить, цвет, текстуру или всё сразу. На самом деле они просто переключали шейдер, вы даже можете посмотреть их код открыв .jar файл версии через архиватор и посмотреть в ассетах файлы с расширениями .fsh и .vsh. Но что же это и зачем нам это нужно?

Представим ситуацию, вы хотите нарисовать круг. Сейчас вы думаете, вроде всё логично, я буду рисовать линии (GL_LINE_STRIP) а точки по формуле круга (x = cos(radians(angle)) * radius, y = sin(radians(angle)) * radius). Я сделал это за вас, чтобы показать проблему:
HTZQ38j.png

P.S. Если не видите проблемы - приблизьтесь к монитору

Проблема заключается в том, что на элементы не накладывается сглаживание (antialiasing) и создаётся эффект "пиксельности". Очень давно был придуман лайфхак в котором нужно было рисовать точками (GL_POINTS) и включить GL_POINT_SMOOTH, но при настройках жирности как на моём скриншоте он всё равно также пиксельный.
UPD: Поправочка, пиксельность и означает "алиасинг", когда в процессе растеризации вектор проходит сквозь несколько пикселей, процесс фикса этого - "анти" (алиасинг)

Для этого приходится использовать шейдер. Шейдеры бывают двух типов: Фрагментные - под каждый пиксель на экране вызывается функция, которая должна установить цвет данному пикселю и Вершинный - шейдер, который определяет позицию пикселю, это нужно, например: чтобы быстро домножать точки на матрицу.

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

P.S. Данный способ работает на версиях 1.20
Давайте напишем наш первый шейдер, который будет просто красить пиксель в указанный цвет:
Код:
Expand Collapse Copy
#version 330 core // Версия шейдера, 330 означает OpenGL 3.3 с профилем core

uniform vec4 col; // Юниформа цвета, это параметр, который передаётся из кода в шейдер

out vec4 fragColor; // Переменная, в которую мы запишем цвет пикселя

void main() { // Основная функция
    fragColor = col; // Устанавливаем цвет пикселя на uniform
}

Теперь нужно шейдер создать, для этого воспользуемся уже существующими методами создания из майнкрафта, а именно классом ShaderInstance. Данный класс требует, чтобы мы создали дополнительные 2 папки: shaders и внутри core. Внутри папки core создадим .json файл с названием шейдера. Внутри него вставляем шаблон (вы можете найти шаблон сами в шейдерах майнкрафта):
JSON:
Expand Collapse Copy
{
    "blend": {
        "func": "add",
        "srcrgb": "srcalpha",
        "dstrgb": "1-srcalpha"
    },
    "vertex": "",
    "fragment": "",
    "attributes": [
    ],
    "samplers": [
    ],
    "uniforms": [
        { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
        { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }
    ]
}
Сейчас расскажу, что к чему. Как вы могли догадаться - blend это аргументы для функции blendFuncSeparate. Далее идёт vertex и fragment - это название файлов для вершинного (vertex) и фрагментного (fragment) шейдера. Туда указываете свои названия, кстати, вершинный шейдер вам явно не придётся изменять, так что также возьмите шаблон:
Код:
Expand Collapse Copy
#version 330 core

in vec3 Position; // Позиция вершины

uniform mat4 ModelViewMat; // Матрица model * view
uniform mat4 ProjMat; // Матрица проекции

void main() {
    mat4 mvp = ModelViewMat * ProjMat; // Матрица modelViewProjection.
    gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0);
}
Подробнее об этом можно ознакомится здесь (noad):
Пожалуйста, авторизуйтесь для просмотра ссылки.


Далее по желанию вы можете указать юниформы, у нас это цвет:
JSON:
Expand Collapse Copy
{ "name": "color", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
И атрибуты:
JSON:
Expand Collapse Copy
"attributes": [
    "Color"
]

Приступаем к коду на джаве. Создадим ShaderInstance:
Java:
Expand Collapse Copy
ShaderInstance shader = new ShaderInstance(Minecraft.getInstance().getResourceManager(), new ResourceLocation("название шейдера без .json"), DefaultVertexFormat.POSITION); // Подсказка: Если вы в дальнейшем будете писать мод, то у forge свой менеджер ресурсов
Рисуем прямоугольник как обычно, но есть нюанс, перед begin следует включить шейдер, а после отрисовки выключить.
На версиях 1.20 и выше это делается с помощью:
RenderSystem.setShader(() -> shader);
На версиях ниже используется:
GL20.glUseProgram(shader.getProgram());

До включения шейдера требуется установить значения юниформам (если они есть).

1.20+:
shader.getUniform("название").set(1.0f, 0.0f, 0.0f, 1.0f);
Ниже 1.17:
glUniform4f(glGetUniformLocation(shader.getProgram(), "название"), 1.0f, 0.0f, 0.0f, 1.0f);

Ну и ниже 1.17 после отрисовки следует отключить шейдер (на версиях выше это происходит автоматически):
GL20.glUseProgram(0);

Ну вот и всё, давайте проверим (кстати тестировал на forge 1.20.1):
RmGw2Wz.png

Да, действительно всё работает. Всё же, мы хотели круг, гуглим: circle sdf shadertoy и натыкаемся на первую ссылочку (noad):
Пожалуйста, авторизуйтесь для просмотра ссылки.
Переносим на язык glsl (отличий очень мало) и получаем результат:
oZB0qPe.png

Вот вам круг, немного я проебался, но и хуй с ним :)
лучшее что видел в югейме)
 
хочу перенести один шейдер с 1.16.5 на 1.20.1, на 1.16.5 он идеально работает, но на 1.20.1, через ShaderInstance, он выводит на весь экран только 1 цвет:FeelsBadMan:
может кто помочь? или подметить где может быть ошибка. заранее спасибо

discord -> n1ckelina
 

Вложения

  • изображение_2024-11-21_205405815.png
    изображение_2024-11-21_205405815.png
    945.9 KB · Просмотры: 99
@ZDC0der думаю для тебя если у тебя рендер2д спизжен полностью с юг
 
хуета переделывай
 
Назад
Сверху Снизу