-
Автор темы
- #1
Список:
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 есть своя особенность, у него есть так скажем "переключатели". Каждый переключатель за что-то отвечает.
Сейчас расскажу про каждый.
Переключатель глубины (Depth test)
Константа: GL11.GL_DEPTH_TEST
Выкл:
![Pasted image 20240705011107.png Pasted image 20240705011107.png](https://yougame.biz/data/attachments/281/281498-4d9296ed453455e2057acf7fdb65f9f5.jpg)
Вкл:
![Pasted image 20240705011123.png Pasted image 20240705011123.png](https://yougame.biz/data/attachments/281/281499-6db368663000d876319761fefdb1c36e.jpg)
Отсечение граней
Константа: GL11.GL_CULL_FACE
Если переключатель включен, то отрисовка будет только с одной стороны.
Вкл:
С одной стороны:
![Pasted image 20240705010705.png Pasted image 20240705010705.png](https://yougame.biz/data/attachments/281/281500-b5cc47fba123a223a086042633a7f1ff.jpg)
С другой:
Выкл - означает, что рисует всегда.
Blend (Смешивание)
Константа: GL11.GL_BLEND
Самая важный переключатель для нас, он позволяет смешивать цвета при наложении. Аргументов там много, можете посмотреть их в интернете, но мы будем использовать такие:
Вкл:
Выкл:
Отрисовка примитивов
Будем продолжать изучение OpenGL постепенно. Раньше чтобы отрисовывать примитивы нужно было использовать glBegin и glEnd, но это максимально не оптимизированно и начиная с хуй пойми какой версии OpenGL 3 их убрали полностью, поэтому стоит использовать либо glDrawArrays, либо glDrawElements. Но за нас уже это сделали разработчики майнкрафта в удобном классе Tessellator и BufferBuilder.
Для начала нужно получить их:
Далее выбрать примитив и формат вершин (максимально странное название):
Вот все примитивы:
И форматы вершин:
Далее обозначить вершины и матрицу (вот только попробуйте забыть про матрицу, я потом покажу зачем это и какие приколы можно делать):
И отрисовать:
Получается сейчас мы должны увидеть прямоугольник с 4 цветами, но мы идём нахуй:
А всё потому, что помимо blend нужно включить ещё и shade, для создания градиента:
Вот теперь красота
Шейдеры
В коде, который вы видели ранее мы использовали "форматы вершин", в которых выбирали, что же конкретно нам рендерить, цвет, текстуру или всё сразу. На самом деле они просто переключали шейдер, вы даже можете посмотреть их код открыв .jar файл версии через архиватор и посмотреть в ассетах файлы с расширениями .fsh и .vsh. Но что же это и зачем нам это нужно?
Представим ситуацию, вы хотите нарисовать круг. Сейчас вы думаете, вроде всё логично, я буду рисовать линии (GL_LINE_STRIP) а точки по формуле круга (x = cos(radians(angle)) * radius, y = sin(radians(angle)) * radius). Я сделал это за вас, чтобы показать проблему:
P.S. Если не видите проблемы - приблизьтесь к монитору
Проблема заключается в том, что на элементы не накладывается сглаживание (antialiasing) и создаётся эффект "пиксельности". Очень давно был придуман лайфхак в котором нужно было рисовать точками (GL_POINTS) и включить GL_POINT_SMOOTH, но при настройках жирности как на моём скриншоте он всё равно также пиксельный.
UPD: Поправочка, пиксельность и означает "алиасинг", когда в процессе растеризации вектор проходит сквозь несколько пикселей, процесс фикса этого - "анти" (алиасинг)
Для этого приходится использовать шейдер. Шейдеры бывают двух типов: Фрагментные - под каждый пиксель на экране вызывается функция, которая должна установить цвет данному пикселю и Вершинный - шейдер, который определяет позицию пикселю, это нужно, например: чтобы быстро домножать точки на матрицу.
Признаюсь, я не спец по шейдерам, и для меня удивительно, как люди в зависимости от дистанции, времени могут узнать цвет пикселя, чтобы получить результат, который они хотят.
P.S. Данный способ работает на версиях 1.20
Давайте напишем наш первый шейдер, который будет просто красить пиксель в указанный цвет:
Теперь нужно шейдер создать, для этого воспользуемся уже существующими методами создания из майнкрафта, а именно классом ShaderInstance. Данный класс требует, чтобы мы создали дополнительные 2 папки: shaders и внутри core. Внутри папки core создадим .json файл с названием шейдера. Внутри него вставляем шаблон (вы можете найти шаблон сами в шейдерах майнкрафта):
Сейчас расскажу, что к чему. Как вы могли догадаться - blend это аргументы для функции blendFuncSeparate. Далее идёт vertex и fragment - это название файлов для вершинного (vertex) и фрагментного (fragment) шейдера. Туда указываете свои названия, кстати, вершинный шейдер вам явно не придётся изменять, так что также возьмите шаблон:
Подробнее об этом можно ознакомится здесь (noad):
Далее по желанию вы можете указать юниформы, у нас это цвет:
И атрибуты:
Приступаем к коду на джаве. Создадим ShaderInstance:
Рисуем прямоугольник как обычно, но есть нюанс, перед begin следует включить шейдер, а после отрисовки выключить.
На версиях 1.20 и выше это делается с помощью:
На версиях ниже используется:
До включения шейдера требуется установить значения юниформам (если они есть).
1.20+:
Ниже 1.17:
Ну и ниже 1.17 после отрисовки следует отключить шейдер (на версиях выше это происходит автоматически):
Ну вот и всё, давайте проверим (кстати тестировал на forge 1.20.1):
Да, действительно всё работает. Всё же, мы хотели круг, гуглим:
Вот вам круг, немного я проебался, но и хуй с ним :)
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:
GL11.glEnable(константа); // Включить переключатель
GL11.glDisable(константа); // Выключить переключатель
Переключатель глубины (Depth test)
Константа: GL11.GL_DEPTH_TEST
Выкл:
![Pasted image 20240705011107.png Pasted image 20240705011107.png](https://yougame.biz/data/attachments/281/281498-4d9296ed453455e2057acf7fdb65f9f5.jpg)
Вкл:
![Pasted image 20240705011123.png Pasted image 20240705011123.png](https://yougame.biz/data/attachments/281/281499-6db368663000d876319761fefdb1c36e.jpg)
Отсечение граней
Константа: GL11.GL_CULL_FACE
Если переключатель включен, то отрисовка будет только с одной стороны.
Вкл:
С одной стороны:
![Pasted image 20240705010705.png Pasted image 20240705010705.png](https://yougame.biz/data/attachments/281/281500-b5cc47fba123a223a086042633a7f1ff.jpg)
С другой:
![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2F5hn9dkU.png&hash=498d87a8ef4824418f8deb53a393989b)
Выкл - означает, что рисует всегда.
Blend (Смешивание)
Константа: GL11.GL_BLEND
Самая важный переключатель для нас, он позволяет смешивать цвета при наложении. Аргументов там много, можете посмотреть их в интернете, но мы будем использовать такие:
Java:
GL11.blendFuncSeparate(GL11.SRC_ALPHA, GL11.ONE_MINUS_SRC_ALPHA, GL11.ONE, GL11.ZERO);
![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2FLlluH1h.png&hash=00c269bc819f68697f60a10fc3e3e974)
Выкл:
![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2FvZIewDd.png&hash=7a2c91ac1e1c2ec1dee697c8f3fe74d9)
Отрисовка примитивов
Будем продолжать изучение OpenGL постепенно. Раньше чтобы отрисовывать примитивы нужно было использовать glBegin и glEnd, но это максимально не оптимизированно и начиная с хуй пойми какой версии OpenGL 3 их убрали полностью, поэтому стоит использовать либо glDrawArrays, либо glDrawElements. Но за нас уже это сделали разработчики майнкрафта в удобном классе Tessellator и BufferBuilder.
Для начала нужно получить их:
Java:
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:
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 цветами, но мы идём нахуй:
![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2FT6n1e1y.png&hash=67f119a8c1e007031211161a0c1b6743)
А всё потому, что помимо blend нужно включить ещё и shade, для создания градиента:
Java:
// Включить
GL11.glShadeModel(GL11.GL_SMOOTH);
// Отрисовать
// Отключить
GL11.glShadeModel(GL11.GL_FLAT);
![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2F0i1wodz.png&hash=2b43ef5609301a0bf69862fe0f7ce267)
Вот теперь красота
Шейдеры
В коде, который вы видели ранее мы использовали "форматы вершин", в которых выбирали, что же конкретно нам рендерить, цвет, текстуру или всё сразу. На самом деле они просто переключали шейдер, вы даже можете посмотреть их код открыв .jar файл версии через архиватор и посмотреть в ассетах файлы с расширениями .fsh и .vsh. Но что же это и зачем нам это нужно?
Представим ситуацию, вы хотите нарисовать круг. Сейчас вы думаете, вроде всё логично, я буду рисовать линии (GL_LINE_STRIP) а точки по формуле круга (x = cos(radians(angle)) * radius, y = sin(radians(angle)) * radius). Я сделал это за вас, чтобы показать проблему:
![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2FHTZQ38j.png&hash=181fe244ba791caa97c211f11261d330)
P.S. Если не видите проблемы - приблизьтесь к монитору
Проблема заключается в том, что на элементы не накладывается сглаживание (antialiasing) и создаётся эффект "пиксельности". Очень давно был придуман лайфхак в котором нужно было рисовать точками (GL_POINTS) и включить GL_POINT_SMOOTH, но при настройках жирности как на моём скриншоте он всё равно также пиксельный.
UPD: Поправочка, пиксельность и означает "алиасинг", когда в процессе растеризации вектор проходит сквозь несколько пикселей, процесс фикса этого - "анти" (алиасинг)
Для этого приходится использовать шейдер. Шейдеры бывают двух типов: Фрагментные - под каждый пиксель на экране вызывается функция, которая должна установить цвет данному пикселю и Вершинный - шейдер, который определяет позицию пикселю, это нужно, например: чтобы быстро домножать точки на матрицу.
Признаюсь, я не спец по шейдерам, и для меня удивительно, как люди в зависимости от дистанции, времени могут узнать цвет пикселя, чтобы получить результат, который они хотят.
P.S. Данный способ работает на версиях 1.20
Давайте напишем наш первый шейдер, который будет просто красить пиксель в указанный цвет:
Код:
#version 330 core // Версия шейдера, 330 означает OpenGL 3.3 с профилем core
uniform vec4 col; // Юниформа цвета, это параметр, который передаётся из кода в шейдер
out vec4 fragColor; // Переменная, в которую мы запишем цвет пикселя
void main() { // Основная функция
fragColor = col; // Устанавливаем цвет пикселя на uniform
}
JSON:
{
"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 ] }
]
}
Код:
#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);
}
Пожалуйста, авторизуйтесь для просмотра ссылки.
Далее по желанию вы можете указать юниформы, у нас это цвет:
JSON:
{ "name": "color", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
JSON:
"attributes": [
"Color"
]
Java:
ShaderInstance shader = new ShaderInstance(Minecraft.getInstance().getResourceManager(), new ResourceLocation("название шейдера без .json"), DefaultVertexFormat.POSITION); // Подсказка: Если вы в дальнейшем будете писать мод, то у forge свой менеджер ресурсов
На версиях 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):
![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2FRmGw2Wz.png&hash=169f347cd8bf65a847f3dc0c673eb7c1)
Да, действительно всё работает. Всё же, мы хотели круг, гуглим:
circle sdf shadertoy
и натыкаемся на первую ссылочку (noad):
Пожалуйста, авторизуйтесь для просмотра ссылки.
Переносим на язык glsl (отличий очень мало) и получаем результат:![](/proxy.php?image=https%3A%2F%2Fi.imgur.com%2FoZB0qPe.png&hash=7b29977e4a645c3f466efa6e2adb2e61)
Вот вам круг, немного я проебался, но и хуй с ним :)
Последнее редактирование: