- Версия(и) Minecraft
- любая
Доброго времени суток! о/
В этой части я расскажу, как добавить в игру созданный в предыдущей части вершинный шейдер. Если вы не читали её и вообще понятия не имеете, что такое шейдер, вам сюда: Шейдеры GLSL [ч.1 - Fragment Shaders]
Итак, без лишних слов, начинаем!
Сразу скажу: все программы, директивы и прочее для OpenGL хранится в виде числовых идентификаторов. И всё, что нам нужно - получить этот идентификатор. Да, вот настолько просто создать и добавить собственный шейдер. Но обо всём по порядку.
Для начала нам нужно загрузить в наш мод код шейдера. Это не более чем строка, так что да, шейдеры можно писать прямо внутри программы в переменной типа String, однако по мне это дурной тон.
Как вы это будете делать значения абсолютно не имеет, а чтение из файла базовый навык, и расписывать то, что все и так знают не вижу смысла.
В результате получим код шейдера в строке
Теперь самое время загрузить его!
Создадим переменную-идентификатор нашего будущего шейдера и попросим OpenGL присвоить ей новое значение. Поскольку это фрагментный шейдер, в параметрах функции мы так и напишем:
Закрепим за нашим ID исходный код шейдера (String shaderCode, описанный выше)
И скомпилируем его!
Готово! ID можно смело использовать для получения нашего шейдера.
Теперь создадим саму программу (ведь Шейдер - это программа, которая выполняется в цикле рендеринга графики... Ну вы помните):
Прицепим к ней наш фрагментный шейдер..:
И слинкуем (объединим программу и шейдер)!
Всё, теперь ваш programID и будет тем самым числовым идентификатором, который будет впоследствии вызываться вашим рендером.
Ну что, проверим как это всё выглядит в игре?
Опять же, не стану объяснять, как рендерить блок/предмет/моба/ещё что, ибо раз вы взялись за написание шейдеров, предполагается, что простейший рендер вам уже подвластен.
Как же вызвать наш шейдер в рендере? Всего-то одна строка:
Важно: вызывать эту строку нужно два раза:
Перед отрисовкой объекта (например,
После отрисовки, но вместо programID поставить
Всё. Теперь ваш шейдер работает и накладывается на каждый полигон вашей модельки!
И да, запомните:
Примечание: если вы хотите передать в шейдер какой-нибудь uniform, делайте так:
Например, время:
Браво! Теперь вы тоже можете в шейдеры! :Dдаже раньше, чем Польша в космос :D
Ошибки:
Иногда у вас могут возникнуть проблемы при создании, сборке или линковке шейдера, поэтому эти случаи нужно уметь обрабатывать.
1. Первое, что может произойти -
2. Ошибка компиляции
Не забудьте удалить неверно скомпилированный объект:
3. Ошибка линковки
4. Валидация программы и обработка ошибки:
Информация: GitHub.
Всего хорошего!
В этой части я расскажу, как добавить в игру созданный в предыдущей части вершинный шейдер. Если вы не читали её и вообще понятия не имеете, что такое шейдер, вам сюда: Шейдеры GLSL [ч.1 - Fragment Shaders]
Итак, без лишних слов, начинаем!
Сразу скажу: все программы, директивы и прочее для OpenGL хранится в виде числовых идентификаторов. И всё, что нам нужно - получить этот идентификатор. Да, вот настолько просто создать и добавить собственный шейдер. Но обо всём по порядку.
Для начала нам нужно загрузить в наш мод код шейдера. Это не более чем строка, так что да, шейдеры можно писать прямо внутри программы в переменной типа String, однако по мне это дурной тон.
Как вы это будете делать значения абсолютно не имеет, а чтение из файла базовый навык, и расписывать то, что все и так знают не вижу смысла.
В таком случае совет:
Как по мне, лучше всего использовать java.io.BufferedReader#readLine для чтения и java.lang.StringBuilder#append для объединения строчек файла.
Как по мне, лучше всего использовать java.io.BufferedReader#readLine для чтения и java.lang.StringBuilder#append для объединения строчек файла.
В результате получим код шейдера в строке
String shaderCode
Теперь самое время загрузить его!
Создадим переменную-идентификатор нашего будущего шейдера и попросим OpenGL присвоить ей новое значение. Поскольку это фрагментный шейдер, в параметрах функции мы так и напишем:
int shaderID = ARBShaderObjects.glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
Закрепим за нашим ID исходный код шейдера (String shaderCode, описанный выше)
ARBShaderObjects.glShaderSourceARB(shaderID, shaderCode);
И скомпилируем его!
ARBShaderObjects.glCompileShaderARB(shaderID);
Готово! ID можно смело использовать для получения нашего шейдера.
Теперь создадим саму программу (ведь Шейдер - это программа, которая выполняется в цикле рендеринга графики... Ну вы помните):
int programID = ARBShaderObjects.glCreateProgramObjectARB();
Прицепим к ней наш фрагментный шейдер..:
ARBShaderObjects.glAttachObjectARB(programID, shaderID);
И слинкуем (объединим программу и шейдер)!
ARBShaderObjects.glLinkProgramARB(programID);
Всё, теперь ваш programID и будет тем самым числовым идентификатором, который будет впоследствии вызываться вашим рендером.
Ну что, проверим как это всё выглядит в игре?
Опять же, не стану объяснять, как рендерить блок/предмет/моба/ещё что, ибо раз вы взялись за написание шейдеров, предполагается, что простейший рендер вам уже подвластен.
Как же вызвать наш шейдер в рендере? Всего-то одна строка:
ARBShaderObjects.glUseProgramObjectARB(programID);
Важно: вызывать эту строку нужно два раза:
Перед отрисовкой объекта (например,
model.renderAll()
)После отрисовки, но вместо programID поставить
0
.Всё. Теперь ваш шейдер работает и накладывается на каждый полигон вашей модельки!
И да, запомните:
Примечание: если вы хотите передать в шейдер какой-нибудь uniform, делайте так:
Java:
// Получите ID необходимой переменной с модификатором uniform по её имени в программе:
int uniformID = ARBShaderObjects.glGetUniformLocationARB(proramID, "uniformName");
// И присвойте необходимое значение ПО ТИПУ
ARBShaderObjects.glUniform%%ARB(uniformID, VALUE);
// Вместо %% - необходимый вам тип; все типы смотрите по crtl+пробелу
Например, время:
Java:
// В шейдере:
uniform float time;
// В рендере:
int timeID = ARBShaderObjects.glGetUniformLocationARB(proramID, "time");
ARBShaderObjects.glUniform1fARB(timeID, Minecraft.getMinecraft().theWorld.getTotalWorldTime() / 20.0F);
Браво! Теперь вы тоже можете в шейдеры! :D
Ошибки:
Иногда у вас могут возникнуть проблемы при создании, сборке или линковке шейдера, поэтому эти случаи нужно уметь обрабатывать.
1. Первое, что может произойти -
.glCreateShaderObjectARB()
и/или .glCreateProgramObjectARB()
вернёт ноль. Тогда дальнейшая линковка и прочее не будут иметь смысла, так что проверяйте if (shaderID != 0)
2. Ошибка компиляции
.glCompileShaderARB()
. Обрабатывается примерно так:
Java:
if (ARBShaderObjects.glGetObjectParameteriARB(shaderID,
ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE)
throw new RuntimeException("Shader compilation error!\n" +
ARBShaderObjects.glGetInfoLogARB(shaderID, ARBShaderObjects.
glGetObjectParameteriARB(shaderID, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB)));
ARBShaderObjects.glDeleteObjectARB(shaderID);
3. Ошибка линковки
.glLinkProgramARB()
Java:
if(ARBShaderObjects.glGetObjectParameteriARB(programID,
ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) // exception
// Информация через .glGetInfoLogARB
4. Валидация программы и обработка ошибки:
Java:
// Проверяем полученную программу
ARBShaderObjects.glValidateProgramARB(programID);
// Если в ней ошибка - выбрасываем исключение
if (ARBShaderObjects.glGetObjectParameteriARB(programID,
ARBShaderObjects.GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE) // exception
Информация: GitHub.
Всего хорошего!