Введение

Godot использует язык шейдеров, похожий на GLSL ES 3.0. Поддерживается большинство типов данных и функций. Возможно и остальные постепенно будут добавлены.

В отличие от шейдерного языка в Godot 2.x, реализация его в 3 версии намного ближе к оригиналу.

Типы шейдеров

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

Каждый шейдер должен начинаться со строки в которой указывается его тип в формате:

Доступны следующие типы:

  • «spatial»: для 3D рендеринга.
  • «canvas_item»: для 2D рендеринга.
  • «particles»: для систем частиц.

Режимы Рендеринга

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

Типы данных

Поддерживается большинство типов данных GLSL ES 3.0

Тип Описание
void Void datatype, используется только для функций, которые не возвращают значение
bool Логический тип, может содержать только «true» или «false»
bvec2 Двухкомпонентный вектор логических значений
bvec3 Трёхкомпонентный вектор логических значений
bvec4 Четырёхкомпонентный вектор логических значений
int Знаковое целое число
ivec2 Двухкомпонентный вектор знаковых целых чисел
ivec3 Трёхкомпонентный вектор знаковых целых чисел
ivec4 Четырёхкомпонентный вектор знаковых целых чисел
uint Беззнаковое скалярное целое число, не содержит отрицательных значений
uvec2 Двухкомпонентный вектор беззнаковых целых чисел
uvec3 Трёххкомпонентный вектор беззнаковых целых чисел
uvec4 Четырёхкомпонентный вектор беззнаковых целых чисел
float Число с плавающей точкой
vec2 Двухкомпонентный вектор чисел с плавающей точкой
vec3 Трёхкомпонентный вектор чисел с плавающей точкой
vec4  Четырёхкомпонентный вектор чисел с плавающей точкой
mat2 Матрица 2×2, размещённая в памяти по столбцам.
mat3 Матрица 3×3, размещённая в памяти по столбцам.
mat4 Матрица 4×4, размещённая в памяти по столбцам.
sampler2D Тип переменной для привязки 2D текстуры считываемой как число с плавающей точкой
isampler2D Тип переменной для привязки 2D текстуры считываемой как знаковое целое число
usampler2D Тип переменной для привязки 2D текстуры считываемой как беззнаковое целое число
samplerCube Тип переменной для привязки кубической текстуры считываемой как целое число

 

Приведение типов

Как и в GLSL ES 3.0, неявное приведение между скалярами и векторами одинакового размера, но разного типа не допускается. Также не допускается приведение типов разного размера. Преобразование должно выполняться явно с помощью конструкторов.

Пример:

Целочисленные константы по умолчанию знаковые, поэтому приведение всегда необходимо для преобразования в беззнаковое:

Элементы

Отдельные скалярные элементы векторных типов доступны через элементы ”x“,” y“,” z “и” w». Кроме того, использование “r”, “g”, “b” и  “a” тоже применимо и является эквивалентом. Используйте то, что лучше всего подходит для вашего случая.

Для матриц используется синтаксис индексирования m[строка][столбец] для доступа к каждому скаляру или m[idx] для доступа к вектору по индексу строки. Например, для доступа к позиции y объекта в mat 4 необходимо использовать синтаксис M[3][1].

Конструкции

Конструкции векторных типов должны всегда передавать:

Construction of matrix types requires pass vectors of same dimension as matrix. You could also build a diagonal matrix using matx(float) syntax. So the mat4(1.0) is an identity matrix.

Swizzling

It is possible to obtain any combination of them in any order, as long as the result is another vector type (or scalar). This is easier shown than explained:

Precision

It is possible to add precision modifiers to datatypes, use them for uniforms, variables, arguments and varyings:

Using lower precision for some operations can speed up the math involved (at the cost of, of course, less precision). This is rarely needed in the vertex shader (where full precision is needed most of the time), but often needed in the fragment one.

Keep in mind that some architectures (mainly mobile) benefit a lot from this, but are also restricted (conversion between precisions has a cost). Please read the relevant documentation on the target architecture to find out more. In all honesty though, mobile drivers are buggy so to stay out of trouble make simple shaders without specifying precision unless you really need to.

Операторы:

Шейдерный язык godot поддерживает такой же набор операторов как и GLSL ES 3.0. Ниже список данных операторов в порядке приоритета::

Приоритета Класс Оператор
1 (highest) группировка в скобках ()
2 Унарный +, -, !, ~
3 Мультипликативный /, *, %
4 additive +, —
5 bit-wise shift <<, >>
6 relational <, >, <=, >=
7 equality ==, !=
8 bit-wise and &
9 bit-wise exclusive or ^
10 bit-wise inclusive or |
11 logical and &&
12 (lowest) logical inclusive or ||

Массив — это набор элементов одного типа, следующих друг за другом, к которым можно получить доступ по соответствующим их позиции в массиве индексам.  Количество индексов,, необходимое для получения доступа к одному элементу называется  размерностью массива.

В зависимости от размерности массивы могут одномерными [ ]  и многомерными [,] [ ,, ] или [ ,,, ] и т. д..


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

Давайте объявим одномерный массив:

Тип массива будет int — только целые числа, длинна массива, то ест количество всех элементов, пусть будет равна 5-ти.

вот мы объявили одномерный массив по названием massiv1 длинной 5, длину массива мы всегда можем получить используя свойство Lenght.

Но все элементы в массиве будут равны 0, так как мы их не проинициализировали, то есть не присвоили каждому элементу массива свое значение. Проинициализировать массив можно несколькими способами, можно сделать это при объявлении, перечислив все элементы в фигурных { } скобках, количество элементов должно быть равным длине массива, иначе будет ошибка:

или сокращенно так:

Так же можно присвоить значение каждому элементу массива обращаясь к ним по индексам, очень важно запомнить, что индексы массивов начинаются с «0», то есть первому элементу массива будет соответствовать индекс «0», второму — «1» и так далее.

Обнуление массива в цикле for 


Двумерные массивы называются матрицами, для доступа к элементу в двумерном массиве необходимо два индекса, грубо говоря — один будет определять  строку в которой находится элемент, а второй положение элемента в этой строке. Объявляется и инициализируется двумерный массив следующим образом:

Данный массив будет представлять собой массив из 2 строк и 3 столбцов, состоящий из целых int чисел, чтобы обратится к элементу двумерного массива, нужно указывать два индекса:

мы присваиваем значение элементу, который находится на 1 строке (помним, что индексы начинаются с 0) на 3 позиции.

Свойство Lenght, соответственно, будет возвращать количество всех элементов массива:


Трехмерные и более массивы объявляются следующим образом:

Нетрудно догадаться, что если одномерный, двумерный и трехмерный массивы еще можно представить визуально, то массивы с большим количеством измерений уже «затруднительно» да это и не к чему, массив по своей сути не  является геометрической моделью а только математической.

Для инициализации многомерного массива необходимо в фигурных скобках указывать  значения каждого измерения

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

Интерфейс для получения от Unity информации о времени.

captureFramerate

deltaTime — возвращает время в секундах, которое потребовалось для завершения последнего кадра (только чтение). Используется для того, что бы сделать частоту кадров в игре независимой. На величину Time.deltaTime необходимо умножать значение если мы перемещаем объект каждый кадр в событии Update() для плавности движения, таким образом мы говорим программе что бы объект перемещался на 10 м. каждую секунду а не каждый кадр;

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

fixedTime — время последнего обновления FixedUpdate. Время в секундах с момента начала игры.

frameCount — общее число пройденных кадров.

 

 

Функции событий в порядке их выполнения:

Reset() — вызывается для инициализации свойств скрипта, когда он только присоединяется к объекту или когда пользователь нажимает кнопку сброса в контекстном меню инспектора. Данная функция вызывается только в режиме редактирования.

Awake() — эта функция всегда вызывается до любых функций Start и также после того, как префаб был вызван в сцену (если GameObject неактивен на момент старта, Awake не будет вызван, пока GameObject не будет активирован, или функция в каком-нибудь прикреплённом скрипте не вызовет Awake).  Используется для инициализации переменных или состояния игры перед её  началом.

OnEnable

Start

OnApplicationPause


FixedUpdate — вызывается через фиксированные равные промежутки времени и не зависит от fps. В данном событии происходит расчет  обновление физических данных поэтому работать с физикой,  в частности с rigidbody необходимо именно в этом событии.

Update — вызывается каждый кадр поэтому периодичность вызова зависит от fps — количества кадров в секунду, в данном событии необходимо работать с собственными перемещениями объектов;

Time.deltaTime — возвращает время которое прошло между двумя кадрами;

LateUpdate() — вызывается один раз в кадре, после завершения Update(). Любые расчеты, которые осуществляются в Update() будут завершены, при вызове LateUpdate(). Основным использованием LateUpdate() обычно является слежение за камерой от третьего лица. Если Вы осуществите движение Вашего персонажа в событии Update(), то движения камеры и расчётов её месторасположения можете вести в событии LateUpdate(). Это будет гарантировать, что персонаж прошел полностью перед камерой, и закрепил свое расположение.

 


OnPreCull

OnBecameVisible/OnBecameInvisible:

OnWillRenderObject

OnPreRender

OnRenderObject

OnPostRender

OnRenderImage

OnGUI

OnDrawGizmos


 

События coroutine


OnMouseDown — вызывается при нажатии кнопки мыши на коллайдере или элементе GUI;

OnMouseDrag  вызывается при нажатии кнопкой мыши на коллайдере или элементе GUI  и удержании кнопки нажатой, вызывается каждый кадр до тех пор пока кнопка не будет отпущена;

OnMouseEnter — вызывается при наведении курсора мыши на коллайдер или элемент GUI;

OnMouseOver — вызывается каждый кадр пока курсор мыши находится над коллайдером или элементом GUI;

OnMouseExit — вызывается когда курсор мыши покидает пределы коллайдера GUI;

OnMouseUp — вызывается при отпускании кнопки мыши после нажатия по коллайдеру или элементу GUI, данное событие вызывается даже если курсор после клика был перемещен за пределы элемента на котором было совершено нажатие;

OnMouseUpAsButton — как и OnMouseUp вызывается при отпускании кнопки но лишь при условии что курсор остался на том же элементе на котором было произведено нажатие, если курсор был перемещен после нажатия и удержании кнопки за пределы элемента — то событие не будет вызвано;

Функции OnMouse не будут вызываться если объекты принадлежат к слоям которые игнорируют Raycast — отправку «воображаемого» луча из до тех пор, пока он не встретит на пути коллайдер в сцене. Функции могут быть сопрограммами coroutine если использовать оператор yield — событие будет послано всем скриптам привязанным к коллайдеру или элементу GUI.

Пример:

OnDestroy

OnDisable

OnApplicationQuit

 

 

Компонент Rigidbody придает объекту физические свойства — у него появляется масса и на него начинает действовать сила тяжести. Благодаря данному компоненту, прикладывая различные силы к объекту, можно задавать ему движение или вращение.

Inspector-Rigidbody

Transform определяет положение (position), вращение (rotation) и масштаб (scale) объекта в сцене. У каждого объекта по умолчанию есть данный компонент.

Transform

Переменные

Position — определяет положение объекта в игровом пространстве.

localPosition — положение относительно родительского обьекта.

Значение хранится в структурах  Vector2(x,y), Vector3(x,y,z) и Vector4(x,y,z,w) 

Vector2(x,y) структура используется для определения 2D-позиций и направлений.

Vector3(x,y,z) используется для определения 3D-позиций и направлений.

Vector4(x,y,z,w)  —  используется для определения четырехкомпонентных векторов(тангенты мешей, описание шейдеров и т.д.) но  в большинстве случаев используется Vector3.

Пример использования:

eulerAngels —  определяет углы поворота (Углы Эйлера) в градусах.

localEulerAngles — определяет углы поворота относительно родительского обьекта.

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

Углы Эйлера — углы, описывающие три поворота системы. Что бы понять, что это за углы, почитайте про крен, тангаж и рысканье которые соответствуют этим трём углам.

Пример: