Докуметация Cтарт Статьи Форум Лента Вход
Не официальное русскоязычное сообщество

JME3 и шейдеры

Опубликованно: 20.05.2017, 0:03
Последняя редакция, Andry: 04.06.2017 17:55

Шейдеры основы

Шейдеры — это наборы инструкций, которые выполняются на графическом процессоре. Они используются, чтобы воспользоваться аппаратным ускорением, доступным на графическом процессоре для целей визуализации.

В этой статье рассматриваются только шейдеры Вершинный(Vertex) и Фрагментный(Fragment), потому что они единственные, поддерживаемые JME3 на данный момент. Имейте в виду, что есть некоторые другие типы шейдеров (геометрия, тесселяция, …).

Существует несколько часто используемых языков, которые вы можете встретить в кодах шейдеров, но поскольку JME3 основан на OpenGL, шейдеры в JME используют GLSL, и любой пример в этой статье будет написан в GLSL.

Как это работает?

Для простоты: Вершинный-шейдер выполняется один раз для каждой вершины в представлении, затем Fragment shader (также называемый Pixel shader) выполняется один раз для каждого пикселя на экране.

Основная цель Вершинного шейдера — вычислить координату экрана вершины (где эта вершина будет отображаться на экране), а основная цель Фрагментного шейдера — вычислить цвет пикселя.
Это очень упрощенная схема для описания стека вызовов:

jme3andshaders

Основная программа посылает данные сетки вершинному шейдеру (положение вершины в пространстве объектов, нормали, касательные и.т.д.). Вершинный шейдер вычисляет положение экрана на экране и отправляет его в фрагментный шейдер. Фрагментный шейдер вычисляет цвет, и результат отображается на экране или в текстуре.

Область переменных

Существуют разные типы переменных для шейдеров:

  • uniform: пользовательские переменные, передаваемые основной программой вершинному и фрагментному шейдеру , эти переменные являются глобальными для заданного исполнения шейдера.
  • attribute: Per-vertex переменные, передаваемые движком шейдеру, такие как полжение, нормали и.т.д. (Mesh-данные в графике)
  • varying: переменные, передаваемые от вершинного шейдера к фрагментному шейдеру.

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

Пространства и матрицы

Чтобы понять грядущий пример, вы должны знать о различных пространствах в 3D компьютерной графике и матрицах, используемых для перевода координат из одного пространства в другое.

jme3andshaders-1

Движок передает координаты пространства объектов в вершинный шейдер. Нам нужно вычислить его положение в проекционном пространстве. Для этого мы преобразуем позицию объекта в WorldViewProjectionMatrix, которая представляет собой комбинацию матриц World, View, Projection (кто бы мог подумать?).

Простой пример: рендеринг сплошного цвета на объекте

Вот самое простое приложение для шейдеров, отображающее сплошной цвет.

Вершинный шейдер:

//глобальная юниформа World view projection matrix
//(более подробно о глобальном юниформе ниже)
uniform mat4 g_WorldViewProjectionMatrix;
//The attribute inPosition is the Object space position of the vertex
attribute vec3 inPosition;
void main(){
    //Преобразование координат пространственного объекта в координаты 
    //проекционного пространства.
    //- gl_Position стандартная переменная GLSL, которая хранит положение в проекционном
    //пространстве. Он должен быть задана в вершинном шейдере
    //- Для преобразования положения мы умножаем worldViewProjectionMatrix на 
    //вектор положения.
    //Умножение должно быть выполнено в этом порядке.
    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
}

Фрагментный шейдер:

void main(){
    //Возвращает цвет пикселя (здесь сплошной синий)
    //- gl_FragColor стандартная переменная GLSL, которая хранит цвет пикселя.
    // Она должен быть задана в Фрагментном шейдере.
    gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
}

Например, применение этого шейдера к сфере выдаст на экране сплошную синюю сферу.

Как использовать шейдеры в JME3

Вы, наверное, слышали, что JME3 «ориентирован на шейдеры», но что это значит?
Обычно для использования шейдеров вы должны создать программу. Эта программа задает вершинный шейдер и фрагментный шейдер для использования.

JME3 заключает это в систему материалов. Каждый материал в JME3 использует шейдеры.

Например, давайте посмотрим на файл SolidColor.j3md:

MaterialDef Solid Color {
    //Это полный список пользовательских юниформ, которые будут использоваться в 
    //шейдерах
    MaterialParameters {
        Vector4 Color
    }
    Technique {
        //Здесь указаны файлы вершинного и фрагментного
        // шейдеров
        VertexShader GLSL100:   Common/MatDefs/Misc/SolidColor.vert
        FragmentShader GLSL100: Common/MatDefs/Misc/SolidColor.frag
        //Здесь вы указываете, какая глобальная юниформа вам нужно для ваших 
        //шейдеров
        WorldParameters {
            WorldViewProjectionMatrix
        }
    }
    Technique FixedFunc {
    }
}

Для получения дополнительной информации о системе материалов JME3, я предлагаю вам прочитать это jMonkeyEngine3 система материалов — полное разьяснение..

Глобальные юниформы(uniforms) JME3

JME3 может отображать предварительно вычисленные глобальные юниформы для ваших шейдеров. Вы должны указать ту, которая требуется для вашего шейдера в разделе WorldParameters файле описания материала (.j3md).

В шейдере названия юниформ будут иметь префикс «g_».

В приведенном выше примере WorldViewProjectionMatrix объявлен как однородный mat4 g_WorldViewProjectionMatrix в шейдере.

Полный список глобальных юниформ, которые можно использовать в JME3, можно найти UniformBinding.java.

Глобальные юниформы JME3 Освещения

JME3 использует некоторые глобальные юниформы для освещения:

  • G_LightDirection (vec4): направленный свет
    • Применение для SpotLight: x, y, z содержат вектор направления света, компонент w содержит угол косинуса угла наклона
  • G_LightColor (vec4): цвет света
  • G_LightPosition: положение света
    • Применение для SpotLight: x, y, z содержит положение света в мире, компонент w содержит 1/lightRange
    • Применение для PointLight: x, y, z содержат положение света в мире, компонент w содержит 1/lightRadius
    • Применение для DirectionalLight: как ни странно, он используется для направления света … это может измениться. Четвертый компонент содержит -1, и он используется в осветительном шейдере, чтобы знать, является ли он направленным светом или нет.
  • G_AmbientLightColor: цвет окружающего света.

Эти юниформы передаются в шейдер без объявления их в файле j3md, но вы должны указать их в technique описании LightMode MultiPass см. Lighting.j3md для получения дополнительной информации.

Атрибуты JME3

Это разные атрибуты, которые всегда передаются вашему шейдеру.

Вы можете найти полный список этих атрибутов из перечислении Type для VertexBuffer в VertexBuffer.java.

В шейдере название атрибутов будут начинаться с префикса «in».

Когда перечисление перечисляет некоторые обычные типы для каждого атрибута (например, texCoord указывается двумя float числами), то это формат, применяется всеми стандартными шейдерами JME3, которые используют этот атрибут. При написании собственных шейдеров, вы можете использовать альтернативные форматы, такие как размещение трех float в texCoord, просто объявив атрибут как vec3 в шейдере и передав 3 в качестве счетчика компонентов в вызов setBuffer mesh.

Пользователя юниформа

В какой-то момент при создании собственного шейдера вам нужно будет сдать свою собственную юниформу

Любая юниформа должна быть объявлена в файле описания материала (.j3md) в разделе «MaterialParameters».

    MaterialParameters {
        Vector4 Color
        Texture2D ColorMap
    }

Вы также можете передать некоторые описания вашим программам vertex/fragment, чтобы узнать, была ли объявлена юниформа.

Вы просто добавляете его в раздел Defines вашего метода в файле описания.

    Defines {
        COLORMAP : ColorMap
    }

Для параметров типа integer и floating point описание будет содержать заданное вами значение.

Для всех других типов параметров определяется значение 1.

Если для этого параметра не задано никакого значения, определение не объявляется в шейдере.

Эти параметры материала будут отправляться от движка к шейдеру следующим образом, есть методы setXXXX для любого типа юниформы, который вы хотите передать.

   material.setColor("Color", new ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f); // красный цвет
   material.setTexture("ColorMap", myTexture); // Связать myTexture для этого юниформ sampler

Чтобы использовать эту юниформу в шейдере, вам нужно объявить ее в файлах .frag или .vert (в зависимости от того, где вам это нужно). Вы можете использовать определения здесь и далее в коде: Обратите внимание, что m_ prefix определяет, что юниформа является параметром материала.

   uniform vec4 m_Color;
   #ifdef COLORMAP
     uniform sampler2D m_ColorMap;
   #endif

Юниформа будет заполнена во время выполнения, значением которое вы отправили.

Пример: добавление Color Keying в Lighting.j3md Описание материала

Color Keying полезен в играх с участием многих игроков. Он состоит в добавлении некоторого специфического для игрока цвета на текстурах моделей. Самый простой способ сделать это — использовать KeyMap, которая будет содержать величину цвета, который добавит в свой альфа-канал.

Здесь я буду использовать эту цветовую карту:
plasma-desktopxB2787
Для смешивания цвета на этой текстуре:
plasma-desktopbq2787

Нам нужно передать 2 новых параметра в определение Lighting.j3md, раздел MaterialParameters:

// Keying Map
Texture2D KeyMap

// Key Color
Color KeyColor

Внизу добавьте новый Define в разделе Main Technique:

KEYMAP : KeyMap

В файле Lighting.frag определите новую юниформу:

#ifdef KEYMAP
  uniform sampler2D m_KeyMap;
  uniform vec4 m_KeyColor;
#endif

Кроме того, при получении diffuseColor из текстуры DiffuseMap, проверьте, нужно ли это смешать:

    #ifdef KEYMAP
      vec4 keyColor = texture2D(m_KeyMap, newTexCoord);
      diffuseColor.rgb = (1.0-keyColor.a) * diffuseColor.rgb + keyColor.a * m_KeyColor.rgb;
    #endif

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

Вот результат:
plasma-desktopuV2787

Шаг за шагом

  • Создайте файл вершинного шейдера (.vert)
  • Создать файл фрагментного шейдера (.frag)
  • Создайте файл описания материала (j3md), в котором указаны пользовательские юниформы, путь к шейдерам и глобальные юниформы для использования
  • В вашем initSimpleApplication создайте материал, используя это описание, примените его к геометрии
  • Это оно!!
    // куб
    Box box= new Box(Vector3f.ZERO, 1f,1f,1f);
    Geometry cube = new Geometry("box", box);
    Material mat = new Material(assetManager,"Path/To/My/materialDef.j3md");
    cube.setMaterial(mat);
    rootNode.attachChild(cube);

Совместимость JME3 и OpenGL 3 и 4

GLSL 1.0 до 1.2 поставляется со встроенными атрибутами и юниформами (т.е. gl_Vertex, gl_ModelViewMatrix и.т.д.). Эти атрибуты устарели, после GLSL 1.3 (opengl 3), следовательно, и глобальные юниформы и атрибуты JME3.

Вот список устаревших атрибутов и их эквивалент в JME3

Атрибуты GLSL 1.2  Эквивалент JME3
gl_Vertex  inPosition
gl_Normal  inNormal
gl_Color  inColor
gl_MultiTexCoord0  inTexCoord
gl_ModelViewMatrix  g_WorldViewMatrix
gl_ProjectionMatrix  g_ProjectionMatrix
gl_ModelViewProjectionMatrix  g_WorldViewProjectionMatrix
gl_NormalMatrix  g_NormalMatrix

Полезные ссылки


Переведено для jmonkeyengine.ru, оригинал
Автор перевода: Andry

Добавить комментарий

jMonkeyEngine.ru © 2017. Все права сохранены.