GLSL в PlayCanvas: uniforms, attributes, varyings

GLSL (OpenGL Shading Language) является стандартом для написания шейдеров в OpenGL и WebGL, используемых для графической отрисовки на GPU. В PlayCanvas, движке для создания 3D-игр и приложений, GLSL активно используется для разработки кастомных шейдеров, позволяющих создавать визуальные эффекты и обрабатывать графику на низком уровне. Важными компонентами шейдеров являются uniforms, attributes и varyings — ключевые элементы взаимодействия между шейдером и сценой. В этой главе рассматривается их роль и применение в контексте PlayCanvas.

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

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

В PlayCanvas uniforms задаются через объекты Material и их шейдерные свойства. Для их использования в GLSL коде нужно объявить переменные как uniform. Например, передача матрицы модели или цвета материала:

uniform mat4 modelMatrix;
uniform vec4 color;

Матрицы и другие глобальные данные могут быть переданы в шейдер с использованием uniforms. Они обычно используются для таких данных, как:

  • Матрицы преобразования (модель, камера, проекция).
  • Цветовые параметры (например, цвет объекта или освещения).
  • Текстуры (передача текстурных данных через текстурные сэмплеры).

В PlayCanvas uniforms задаются через API pc.Material, например:

material.setParameter("color", new pc.Color(1, 0, 0)); // передача красного цвета

2. Attributes в GLSL

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

В GLSL атрибуты объявляются как attribute и могут быть использованы для получения данных о вершинах:

attribute vec3 vertexPosition;
attribute vec3 vertexNormal;

Каждая вершина имеет набор атрибутов, которые PlayCanvas автоматически передаёт в шейдеры через WebGL. Обычно атрибуты используются для:

  • Позиции (например, координаты вершин).
  • Нормалей (для освещения и теней).
  • Текстурных координат (для наложения текстур).
  • Цветовых атрибутов (если используются цвета, передаваемые на уровне вершин).

В PlayCanvas атрибуты передаются с помощью pc.VertexBuffer и задаются через pc.Mesh, который содержит геометрию объекта:

var vertexBuffer = new pc.VertexBuffer(graphicsDevice, pc.VertexFormat, vertexData);

3. Varyings в GLSL

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

В GLSL для передачи данных между шейдерами используется ключевое слово varying. Эти переменные вычисляются в вершинном шейдере и передаются во фрагментный:

varying vec3 vColor;

Пример вычисления и передачи цвета в вершинном шейдере:

attribute vec3 vertexColor;
varying vec3 vColor;

void main() {
    vColor = vertexColor; // передаем цвет в фрагментный шейдер
    gl_Position = modelViewProjection * vec4(vertexPosition, 1.0);
}

Во фрагментном шейдере этот цвет может быть использован для дальнейших вычислений:

varying vec3 vColor;

void main() {
    gl_FragColor = vec4(vColor, 1.0);
}

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

4. Шейдеры в PlayCanvas: Применение

PlayCanvas использует WebGL для работы с шейдерами, и шейдеры GLSL могут быть написаны напрямую через систему материалов или через создание кастомных шейдеров. Для создания кастомных шейдеров используется класс pc.Shader, который позволяет компилировать и привязывать шейдеры к материалам.

Пример создания кастомного шейдера в PlayCanvas

  1. Создаётся вершинный и фрагментный шейдеры:
var vertexShader = `
attribute vec3 vertexPosition;
attribute vec3 vertexNormal;
uniform mat4 modelViewProjection;
varying vec3 vNormal;

void main() {
    vNormal = vertexNormal;
    gl_Position = modelViewProjection * vec4(vertexPosition, 1.0);
}`;

var fragmentShader = `
precision mediump float;
varying vec3 vNormal;

void main() {
    vec3 normal = normalize(vNormal);
    float light = max(dot(normal, vec3(0.0, 0.0, 1.0)), 0.0);
    gl_FragColor = vec4(light, light, light, 1.0);
}`;
  1. Затем создается материал с этим шейдером:
var shader = new pc.Shader(graphicsDevice, {
    attributes: {
        vertexPosition: pc.SEMANTIC_POSITION,
        vertexNormal: pc.SEMANTIC_NORMAL
    },
    vshader: vertexShader,
    fshader: fragmentShader
});

var material = new pc.Material();
material.shader = shader;
  1. Применение материала к объекту:
entity.model.material = material;

5. Роль шейдеров в оптимизации производительности

Использование uniforms, attributes и varyings в PlayCanvas играет важную роль не только в создании визуальных эффектов, но и в оптимизации производительности. Применение этих элементов позволяет эффективно распределять вычисления между CPU и GPU, минимизируя лишние данные, которые передаются между компонентами рендеринга.

Оптимизация через uniforms

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

Оптимизация через attributes

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

Оптимизация через varyings

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

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