Пример черно-белого шейдера на GLSL

Пример черно-белого шейдера на GLSL

Версия(и) Minecraft
1.7.10
Привет всем. Когда я писал мод на скиллы, я прочитал статью про шейдеры, и про то, как их использовать в майнкрафте вот из этого тутора Алекса. И там не было конкретного примера, и так как некоторые просили его, я решился его сделать сам, и слить код с объяснениями, который я написал для того мода на скиллы. Поэтому перед прочтением данного примера, рекомендую прочитать те две статьи Алекса

Для начала, что бы использовать шейдеры, надо создать специальный класс для их удобной подгрузки из ресурсов и использования. И статический класс для хранения подгруженных шейдеров и их регистрации
Класс-обертка для нашего шейдера назовем ShaderProgram
Java:
public class ShaderProgram {
    private int programID; // индекс нашей шейдерной программы

    public ShaderProgram(){
        programID = ARBShaderObjects.glCreateProgramObjectARB(); // генерируем индекс и задаем его
    }

    public ShaderProgram addFragment(String path){
        return add(path, ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
    }

    public ShaderProgram addVertex(String path){
        return add(path, ARBVertexShader.GL_VERTEX_SHADER_ARB);
    }

    // метод для добавления в шейдерную программу какого-либо шейдера
    public ShaderProgram add(String path, int shaderType){
        int shaderID = ARBShaderObjects.glCreateShaderObjectARB(shaderType); // генерируем индекс нового шейдера
        ARBShaderObjects.glShaderSourceARB(shaderID, readFile(path)); // привязываем код шейдера к его индексу
        ARBShaderObjects.glCompileShaderARB(shaderID); // компилируем шейдер

        // проверяем компиляцию на ошибки. Если что-то пойдет не так, будет краш с ошибкой
        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.glAttachObjectARB(programID, shaderID);
        return this;
    }

    // метод для компиляции всей нашей программы
    public ShaderProgram compile(){
        ARBShaderObjects.glLinkProgramARB(programID);
        return this;
    }

    // включает шейдерную программу
    public void start(){
        ARBShaderObjects.glUseProgramObjectARB(programID);
    }

    // выключает шейдерную программу
    public void stop(){
        ARBShaderObjects.glUseProgramObjectARB(0);
    }

    // метод возвращает индекс юниформы из программы, по её имени в шейдере
    public int getUniform(String name) {
        return ARBShaderObjects.glGetUniformLocationARB(programID, name);
    }

    // метод для считывания файла из ресурса
    private String readFile(String path){
        try {
            StringBuilder builder = new StringBuilder();
            BufferedReader reader = new BufferedReader(new InputStreamReader(Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation(Reference.MOD_ID, path)).getInputStream(), "UTF-8"));
            String str;
            while ((str = reader.readLine()) != null)
                builder.append(str).append("\n");

            return builder.toString();

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

А класс для хранения и регистрации всех шейдеров будет называться ModShaders
Java:
public class ModShaders {
    public static ShaderProgram blackWhite;

    // этот метод мы вызываем при инициализации мода, что бы зарегестрировать все наши шейдеры
    public static void register(){
        // путь к файлам шейдера мы указываем относительно нашей папки в ассетах
        blackWhite = new ShaderProgram()
                .addFragment("shaders/black-white.frag") // добавляем фрагментный
                .addVertex("shaders/texture_color.vert") // добавляем вертексный
                .compile(); // после добавления надо вызывать обязательно компиляцию
    }
}

После написания этих двух классов надо написать сами шейдеры. Один из них будет вертексный, и он будет передавать во фрагментный данные о цвете и координате точки на текстуре, что бы адекватно наложить эту текстуру на полигон
Для этого создаем два файла в ресурсах, вертексный шейдер я назову "texture_color.vert", а фрагментный "black-white.frag". И положу их в папку "shaders" для удобства хранения

В вертексном шейдере, как я уже говорил, задаем цвет вершины и текстурную координату, а так же позицию самой вершины. Переменная с модификатором varying значит, что значение этой переменной будет передано всем последующим шейдерам. В нашем случае эти значения мы получим далее во фрагментном шейдере
C-like:
#version 120 // версия шейдера
varying vec2 texcoord; // текстурная координата
varying vec4 color; // цвет вершины

void main() {
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; // находим позицию вертекса, перемножая его на матрицу проекции
    texcoord = vec2(gl_MultiTexCoord0);
    color = gl_Color;
}

А вот и код фрагментного шейдера, где мы обесцвечиваем пиксель, оставляя только его яркость с прозрачностью
C-like:
#version 120
varying vec2 texcoord;
varying vec4 color;
uniform sampler2D texture; // текстура полигона
uniform float procent; // процент, который показывает какую часть полигона сделать черно-белой

void main() {
    vec4 original = texture2D(texture, texcoord); // получаем оригинальный цвет текстуры
    float d = (original.r + original.b + original.g)/3.0; // вычисляем яркость
    if (procent < texcoord.y) // если координата пикселя больше, чем наш процент, то делаем его черно белым
        gl_FragColor = vec4(d, d, d, color.a);
    else
        gl_FragColor = original * color;
}

Дальше осталось дело за малым, а именно написать рендер иконки скилла, применив к нему шейдер и подставив в юниформу фрагментного шейдера процент отката нашего скилла, что бы со временем отката, черно-белая часть иконки уменьшалась. Вот кусок кода рендера иконки с шейдерами. Рендрил я её на экране в эвенте
Java:
float procentOfSkillCD = skill.GetCD(); // получаю откат скилла в процентах. Соответственно со временем процент будет уменьшатся
Minecraft.getMinecraft().getTextureManager().bindTexture(skill.GetIcon()); // задаем текстуру нашей иконки
ModShaders.blackWhite.start(); // включаем черно-белый шейдер
ARBShaderObjects.glUniform1fARB(ModShaders.blackWhite.getUniform("procent"), procentOfSkillCD); // устанавливаем значение юниформе procent нашему шейдеру
Minecraft.getMinecraft().ingameGUI.drawTexturedModalRect(x, y, 0, 0, width, height); // отрисовываем иконку скилла, в нужной позиции и с нужным размером
ModShaders.blackWhite.stop(); // если мы ничего далее не собираемся рисовать с этим шейдером, то выключаем его

Вот и всё, когда кастую скилл, получаю вот такую картину. По степенно, линяя, разделяющая цветную и черно-белую часть начинает опускаться, пока скилл не восстановится, и не уберется черно-белая часть
Шакально, т.к. иконка у меня маленькая, но всё же эффект виден
Безымянный.png

Это конец! Еще раз спасибо @AlexSocol за его гайд
  • Like
Реакции: JustAGod, Eifel и CMTV
Автор
Minebot
Просмотры
3,575
Первый выпуск
Обновление
Оценка
4.00 звёзд 2 оценок

Последние рецензии

Неплохо для новичка, но вертексный шейдер не нужен от слова вообще. Есть такая фигня емнип под названием bgl_RenderedTexture в которой хранится сама текстура и gl_Color, в которой цвет текущего пикселя. Также вместо ARB лучше использовать GL20, вот готовый класс для этого (и классы-наследники не совсем хороший вариант, т.к. хранят лишь ID)
https://bitbucket.org/AlexSocol/alfheim/src/master/src/main/java/alexsocol/asjlib/ASJShaderHelper.java
Minebot
Minebot
Окей. А почему ты сам в гайде использовал ARB? Я как раз и написал это с использованием твоего гайда
Спасибо, как раз разбираю шейдеры и любой пример на вес золота)
Сверху