Создание Шейдеров На Основе Babylon.js И Webgl: Теория И Примеры

Во время его отчет по второму дню конференции Build 2014 Евангелисты Microsoft Стивен Гуггенхаймер и Джон Шевчук рассказали о реализации поддержки Babylon.js для Oculus Rift. Одним из ключевых моментов их демонстрации стало упоминание о разработанной нами технологии моделирования линз:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Я также присутствовал на докладе Фрэнка Оливье и Бена Констебла об использовании графика в IE с использованием Babylon.js. Эти выступления напомнили мне один вопрос, который мне часто задают о Babylon.js: «Что вы подразумеваете под шейдерамиЭ» Я решил посвятить этому вопросу целую статью, чтобы объяснить, как работают шейдеры, и привести несколько примеров их основных типов.

Этот перевод является частью серии статей для разработчиков из Microsoft. Теория Прежде чем мы начнем наши эксперименты, нам нужно понять, как все работает. При работе с 3D-графикой с аппаратным ускорением мы имеем дело с двумя разными процессорами: центральным процессором (CPU) и графическим процессором (GPU).

Графический процессор — это всего лишь тип узкоспециализированного процессора.

Графический процессор — это конечный автомат, настраиваемый ЦП.

Например, именно CPU дает GPU команду отображать линии вместо треугольников, включать прозрачность и т.д. После того, как все состояния настроены, ЦП определит, что визуализировать, на основе двух основных компонентов: геометрии, которая рассчитывается на основе списка точек или вершин (хранится в массиве, называемом буфером вершин), и списка индексы — грани или треугольники — которые хранятся в индексном буфере.

Наконец, ЦП определит, как визуализировать геометрию, и специально для этого настроит шейдеры для графического процессора.

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

Вершина — это своего рода точка в 3D-пространстве (в отличие от точки в 2D-пространстве).

Существует два типа шейдеров: вершинные и пиксельные (фрагментные).

Графический конвейер Прежде чем перейти непосредственно к шейдерам, сделаем еще одно небольшое отступление.

Для отображения пикселей графический процессор получает данные геометрии от процессора.

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

Каждая запись в индексном буфере соответствует номеру вершины в буфере вершин (это позволяет избежать дублирования вершин).

Например, индексный буфер в примере ниже представляет собой список из двух граней: [1 2 3 1 3 4].

Первая грань содержит вершины 1, 2 и 3. Вторая грань содержит вершины 1, 3 и 4. Итак, в этом случае геометрия состоит из четырех вершин:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Вертекс — верх Буфер вершин — Буфер вершин Индексный буфер — Индексный буфер Лицо – Край
Вершинный шейдер выполняется в каждой вершине треугольника.

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



Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Используя эти 3 пикселя (которые определяют 2D-треугольник на экране), графический процессор проанализирует все значения, связанные с пикселем (по крайней мере, его положение), и применит пиксельный шейдер для генерации цвета для каждого пикселя этого треугольника.



Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

То же самое делается для всех лиц в индексном буфере.

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

ГЛСЛ Как упоминалось ранее, для рендеринга треугольников графическому процессору потребуются два шейдера: вершинный и пиксельный.

Оба написаны на специальном языке GLSL (язык шейдеров графической библиотеки), который немного похож на C. Специально для Internet Explorer 11 мы разработали компилятор, преобразующий GLSL в HLSL (High Level Shader Language) — язык шейдеров DirectX 11. Это позволило нам повысить безопасность шейдерного кода:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Вот пример простого вершинного шейдера:

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

precision highp float; // Attributes attribute vec3 position; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }

Структура вершинного шейдера Вершинный шейдер содержит следующие элементы:
  • Атрибуты : Атрибут определяет часть вершины.

    По умолчанию вершина должна иметь как минимум данные о положении (vector3:x, y, z).

    Но вы, как разработчик, можете предоставить больше данных.

    Например, в приведенном выше коде есть вектор2, называемый uv (текстурные координаты, позволяющие нам применять 2D-текстуру к 3D-объекту).

  • Однородные переменные: Определяется процессором и используется шейдером.

    Единственная универсальная переменная, которая у нас есть в этом случае, — это матрица, используемая для проецирования положения вершины (x, y, z) на экран (x, y).

  • Варьирующиеся переменные: Это значения, которые создаются вершинным шейдером и передаются в пиксельный шейдер.

    В нашем случае вершинный шейдер передаст значение vUV (простую копию uv) пиксельному шейдеру.

    Следовательно, именно здесь определяются координаты текстуры и положение пикселей.

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

  • основной: Функция main() — это код, который выполняется на графическом процессоре для каждой вершины.

    Он должен как минимум предоставить значение gl_position (положение текущей вершины на экране).

Как видно из примера выше, в вершинном шейдере нет ничего сложного.

Он генерирует системную переменную (начиная с gl_) под названием gl_position для определения положения конкретного пикселя, а также устанавливает изменяющуюся переменную под названием vUV. Магия матриц Матрица в нашем шейдере называется worldViewProjection. Он проецирует положение вершины в переменную gl_position. Но как нам получить значение этой матрицы? Поскольку это универсальная переменная, нам необходимо определить ее на стороне процессора (с помощью JavaScript).

Это сложный для понимания аспект работы с 3D-графикой.

Вам нужно хорошо разбираться в сложных математических вычислениях (или использовать 3D-движок, например Babylon.js, о котором мы поговорим позже).

Матрица worldViewProjection состоит из трех отдельных матриц:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

В результате получается матрица, позволяющая конвертировать 3D-вершины в 2D-пиксели с учетом положения точки обзора и всего, что связано с положением, масштабом и вращением текущего объекта.

Задача 3D-дизайнера — создать эту матрицу и поддерживать ее данные в актуальном состоянии.

И снова шейдеры После выполнения вершинного шейдера на каждой вершине (то есть 3 раза) мы получим 3 пикселя с правильным значением vUV и gl_position. Затем графический процессор передаст эти значения каждому пикселю внутри треугольника, образованного тремя основными пикселями.

Затем к каждому пикселю будет применен пиксельный шейдер:

precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = texture2D(textureSampler, vUV); }

Структура пиксельного (или фрагментного) шейдера Структура пиксельного шейдера аналогична вершинному шейдеру:

  • Варьирующиеся переменные: Это значения, которые создаются вершинным шейдером и передаются в пиксельный шейдер.

    В нашем случае пиксельный шейдер получит значение vUV от вершинного шейдера.

  • Однородные переменные: Определяется процессором и используется шейдером.

    Единственная юниформ-переменная, которая у нас есть в этом случае, — это сэмплер, который нужен для чтения цветов текстуры.

  • основной: Основная функция — это код, который выполняется на графическом процессоре для каждого пикселя.

    Он должен как минимум предоставить значение gl_FragColor (цвет текущего пикселя).

Этот пиксельный шейдер очень прост: он считывает цвет текстуры, используя координаты текстуры из вершинного шейдера (который, в свою очередь, получил их из вершины).

Вот что произошло в конце.

Рендеринг осуществляется в реальном времени; вы можете перемещать сферу с помощью мыши.

Чтобы получить такой результат, вам нужно хорошенько поработать с кодом WebGL. Конечно, WebGL — очень мощный API. Но он низкоуровневый, поэтому все придется делать самому: от создания буфера до определения структуры вершин.

Вам также придется много заниматься математикой, настраивать состояния, управлять загрузкой текстур и так далее.

Слишком сложно? BABYLON.ShaderMaterial спешит на помощь Я знаю, о чем вы думаете: «Шейдеры — это круто, но я не хочу разбираться во всех тонкостях WebGL и самому выполнять все вычисления».

Без проблем! Вот почему мы создали Babylon.js. Вот как выглядит код той же сферы в Babylon.js. Для начала вам понадобится простая веб-страница:

<!DOCTYPE html> <html> <head> <title>Babylon.js</title> <script src="Babylon.js"></script> <script type="application/vertexShader" id="vertexShaderCode"> precision highp float; // Attributes attribute vec3 position; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Normal varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; } </script> <script type="application/fragmentShader" id="fragmentShaderCode"> precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = texture2D(textureSampler, vUV); } </script> <script src="index.js"></script> <style> html, body { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden; margin: 0px; overflow: hidden; } #renderCanvas { width: 100%; height: 100%; touch-action: none; -ms-touch-action: none; } </style> </head> <body> <canvas id="renderCanvas"></canvas> </body> </html>

Шейдеры здесь указываются с помощью тегов скриптов.

В Babylon.js их также можно указать в отдельных файлах .

fx. Babylon.js доступен для скачивания перейдите по ссылке здесь или в нашем репозитории на GitHub. Для получения доступа к объекту BABYLON.StandardMaterial необходима версия 1.11 и выше.

Наконец, основной код JavaScript выглядит следующим образом: «используйте строгий»;

document.addEventListener("DOMContentLoaded", startGame, false); function startGame() { if (BABYLON.Engine.isSupported()) { var canvas = document.getElementById("renderCanvas"); var engine = new BABYLON.Engine(canvas, false); var scene = new BABYLON.Scene(engine); var camera = new BABYLON.ArcRotateCamera("Camera", 0, Math.PI / 2, 10, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas); // Creating sphere var sphere = BABYLON.Mesh.CreateSphere("Sphere", 16, 5, scene); var amigaMaterial = new BABYLON.ShaderMaterial("amiga", scene, { vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode", }, { attributes: ["position", "uv"], uniforms: ["worldViewProjection"] }); amigaMaterial.setTexture("textureSampler", new BABYLON.Texture("amiga.jpg", scene)); sphere.material = amigaMaterial; engine.runRenderLoop(function () { sphere.rotation.y += 0.05; scene.render(); }); } };

Как видите, я использую BABYLON.ShaderMaterial, чтобы исключить необходимость компилировать, связывать или управлять шейдерами.

При создании объекта BABYLON.ShaderMaterial необходимо указать элемент DOM, используемый для хранения шейдеров, или базовое имя файлов, в которых расположены шейдеры.

Для второго варианта вам также потребуется создать файл для каждого шейдера, используя следующий принцип именования: basename.vertex.fx и basename.fragment.fx. Тогда вам нужно будет создать такой материал:

var cloudMaterial = new BABYLON.ShaderMaterial("cloud", scene, ".

/myShader", { attributes: ["position", "uv"], uniforms: ["worldViewProjection"] });

Вы также должны указать имена всех используемых атрибутов и универсальных переменных.

Затем вы можете напрямую установить значения униформ и сэмплеров с помощью функций setTexture, setFloat, setFloats, setColor3, setColor4, setVector2, setVector3, setVector4 и setMatrix. Довольно просто, правда? Помните матрицу worldViewProjection? С Babylon.js и BABYLON.ShaderMaterial вам не придется об этом беспокоиться.

Объект BABYLON.ShaderMaterial все рассчитает автоматически, поскольку мы объявляем матрицу в списке юниформ-переменных.

Объект BABYLON.ShaderMaterial может независимо управлять следующими матрицами:

  • мир;
  • вид;
  • проекция;
  • мирвидение;
  • WorldViewProjection.
Никаких сложных расчетов.

Например, каждый раз, когда выполняется сфера.

rotation.y += 0,05, мировая матрица данной сферы генерируется и отправляется в графический процессор.

CYOS: создайте свой собственный шейдер Мы пойдем еще дальше и создадим страницу, на которой мы сможем динамически редактировать шейдеры и видеть результаты в реальном времени.

Для этого нам понадобится предыдущий код, а также объект BABYLON.ShaderMaterial для компиляции и запуска сгенерированных шейдеров.

я использовал для КИОС редактор кода под названием ACE. Он невероятно удобен для пользователя и имеет подсветку синтаксиса.

В поле «Шаблоны» вы можете выбрать предустановленные шейдеры, о них мы поговорим чуть позже.

Вы также можете изменить 3D-объект, используемый для предварительного просмотра шейдеров, в поле «Сетки».

Кнопка «Компилировать» используется для создания нового объекта BABYLON.ShaderMaterial из шейдеров.

Вот его код:

// Compile shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, { vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode", }, { attributes: ["position", "normal", "uv"], uniforms: ["world", "worldView", "worldViewProjection"] }); var refTexture = new BABYLON.Texture("ref.jpg", scene); refTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE; refTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE; var amigaTexture = new BABYLON.Texture("amiga.jpg", scene); shaderMaterial.setTexture("textureSampler", amigaTexture); shaderMaterial.setTexture("refSampler", refTexture); shaderMaterial.setFloat("time", 0); shaderMaterial.setVector3("cameraPosition", BABYLON.Vector3.Zero()); shaderMaterial.backFaceCulling = false; mesh.material = shaderMaterial;

Подозрительно просто, правда? Итак, осталось получить 3 заранее рассчитанные матрицы: world, worldView и worldViewProjection. Данные вершин будут содержать значения координат положения, нормали и текстуры.

Также будут загружены следующие 2 текстуры:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

амига.

jpg

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

ссылка.

jpg А это renderLoop, где я обновляю две юниформ-переменные:

  • переменная времени — для получения забавной анимации;
  • переменная cameraPosition — для получения данных о положении камеры в шейдерах (что очень полезно при расчете освещения);


engine.runRenderLoop(function () { mesh.rotation.y += 0.001; if (shaderMaterial) { shaderMaterial.setFloat("time", time); time += 0.02; shaderMaterial.setVector3("cameraPosition", camera.position); } scene.render(); });

Кроме того, CYOS теперь доступен для Windows Phone благодаря работе, которую мы проделали для Windows Phone 8.1:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Базовый Начнем с базового шейдера в CYOS. Мы уже рассматривали нечто подобное.

Этот шейдер вычисляет gl_position и использует координаты текстуры, чтобы получить цвет для каждого пикселя.

Для расчета положения пикселя вам понадобится матрица worldViewProjection и положение вершины:

precision highp float; // Attributes attribute vec3 position; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }

Координаты текстуры (uv) передаются в пиксельный шейдер без изменений.

Обратите внимание на первую строку: точность mediump float; – его необходимо добавить в вершинный и пиксельный шейдер, чтобы он корректно работал в Chrome. Он отвечает за то, чтобы числа высокой точности не использовались для повышения производительности.

С пиксельным шейдером все еще проще: вам просто нужно использовать координаты текстуры и получить цвет текстуры:

precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = texture2D(textureSampler, vUV); }

Как было показано ранее, юниформ-переменная текстуры Sampler заполнена текстурой Amiga, поэтому результат выглядит следующим образом:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Черное и белое Перейдем ко второму шейдеру, черно-белому.

Он будет использовать параметры предыдущего, но в режиме черно-белого рендеринга.

Оставим прежние настройки вершинного шейдера и внесем небольшие изменения в код пикселя.

Самый простой способ добиться такого эффекта — использовать всего один компонент, например, как показано ниже:

precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = vec4(texture2D(textureSampler, vUV).

ggg, 1.0); }

Мы использовали .

ggg вместо .

rgb (в компьютерной графике это называется swizzle).

Но если вы хотите получить настоящий черно-белый эффект, лучше всего рассчитать относительную яркость, учитывающую все составляющие цвета:

precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { float luminance = dot(texture2D(textureSampler, vUV).

rgb, vec3(0.3, 0.59, 0.11)); gl_FragColor = vec4(luminance, luminance, luminance, 1.0); }

Скалярное произведение рассчитывается следующим образом:

result = v0.x * v1.x + v0.y * v1.y + v0.z * v1.z

В нашем случае:

luminance = r * 0.3 + g * 0.59 + b * 0.11

(эти значения рассчитаны с учетом того, что человеческий глаз более чувствителен к зеленому цвету)

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Затенение ячеек Далее по списку идет шейдер заполнения ячеек, он немного сложнее.

В этом случае нам нужно будет добавить положение вершины и нормаль вершины в пиксельный шейдер.

Вершинный шейдер будет выглядеть так:

precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 world; uniform mat4 worldViewProjection; // Varying varying vec3 vPositionW; varying vec3 vNormalW; varying vec2 vUV; void main(void) { vec4 outPosition = worldViewProjection * vec4(position, 1.0); gl_Position = outPosition; vPositionW = vec3(world * vec4(position, 1.0)); vNormalW = normalize(vec3(world * vec4(normal, 0.0))); vUV = uv; }

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

Вот как будет выглядеть пиксельный шейдер:

precision highp float; // Lights varying vec3 vPositionW; varying vec3 vNormalW; varying vec2 vUV; // Refs uniform sampler2D textureSampler; void main(void) { float ToonThresholds[4]; ToonThresholds[0] = 0.95; ToonThresholds[1] = 0.5; ToonThresholds[2] = 0.2; ToonThresholds[3] = 0.03; float ToonBrightnessLevels[5]; ToonBrightnessLevels[0] = 1.0; ToonBrightnessLevels[1] = 0.8; ToonBrightnessLevels[2] = 0.6; ToonBrightnessLevels[3] = 0.35; ToonBrightnessLevels[4] = 0.2; vec3 vLightPosition = vec3(0, 20, 10); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); // diffuse float ndl = max(0., dot(vNormalW, lightVectorW)); vec3 color = texture2D(textureSampler, vUV).

rgb; if (ndl > ToonThresholds[0]) { color *= ToonBrightnessLevels[0]; } else if (ndl > ToonThresholds[1]) { color *= ToonBrightnessLevels[1]; } else if (ndl > ToonThresholds[2]) { color *= ToonBrightnessLevels[2]; } else if (ndl > ToonThresholds[3]) { color *= ToonBrightnessLevels[3]; } else { color *= ToonBrightnessLevels[4]; } gl_FragColor = vec4(color, 1.); }

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

Например, если интенсивность находится в диапазоне от 1 (максимум) до 0,95, цвет объекта, взятый из текстуры, будет применен напрямую, без изменений.

Если интенсивность составляет от 0,95 до 0,5, к значению цвета будет применен множитель 0,8 и так далее.

В итоге процесс создания такого шейдера можно разделить на 4 этапа:

  • Сначала мы объявляем пороги яркости и константы для каждого уровня интенсивности.

  • Освещение рассчитываем на основе алгоритма Фонга (исходя из того, что источник света неподвижен).



vec3 vLightPosition = vec3(0, 20, 10); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); // diffuse float ndl = max(0., dot(vNormalW, lightVectorW));

Интенсивность света, падающего на пиксель, зависит от угла между нормалью к вершине и направлением света.

  • Получаем цвет текстуры для пикселя.

  • Проверяем порог яркости и применяем константу для соответствующего уровня интенсивности.

В результате мы получим нечто похожее на мультяшный эффект:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Фонг В предыдущем примере мы уже использовали алгоритм Фонга.

Теперь давайте рассмотрим это более подробно.

С вершинным шейдером все будет довольно просто, так как основная часть работы будет выполняться в пиксельном шейдере:

precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUV; void main(void) { vec4 outPosition = worldViewProjection * vec4(position, 1.0); gl_Position = outPosition; vUV = uv; vPosition = position; vNormal = normal; }

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

precision highp float; // Varying varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUV; // Uniforms uniform mat4 world; // Refs uniform vec3 cameraPosition; uniform sampler2D textureSampler; void main(void) { vec3 vLightPosition = vec3(0, 20, 10); // World values vec3 vPositionW = vec3(world * vec4(vPosition, 1.0)); vec3 vNormalW = normalize(vec3(world * vec4(vNormal, 0.0))); vec3 viewDirectionW = normalize(cameraPosition - vPositionW); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); vec3 color = texture2D(textureSampler, vUV).

rgb; // diffuse float ndl = max(0., dot(vNormalW, lightVectorW)); // Specular vec3 angleW = normalize(viewDirectionW + lightVectorW); float specComp = max(0., dot(vNormalW, angleW)); specComp = pow(specComp, max(1., 64.)) * 2.; gl_FragColor = vec4(color * ndl + vec3(specComp), 1.); }

В предыдущем примере мы использовали только диффузный компонент, поэтому осталось только добавить зеркальный компонент. та картина из статьи в Википедии объясняет, как работает шейдер:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Автор: Брэд Смит, он же Rainwarrior. Результат:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

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

Этот шейдер будет игнорировать любой некрасный пиксель, что приведет к иллюзии полого объекта.

Вершинный шейдер в этом случае будет такой же, как и для базового шейдера:

precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }

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

precision highp float; varying vec2 vUV; // Refs uniform sampler2D textureSampler; void main(void) { vec3 color = texture2D(textureSampler, vUV).

rgb; if (color.g > 0.5) { discard; } gl_FragColor = vec4(color, 1.); }

Результат выглядит довольно забавно:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Волна Наверное, мы уже достаточно наигрались с пиксельными шейдерами.

Теперь мне хотелось бы уделить больше внимания вершинным шейдерам.

Для этого примера нам понадобится пиксельный шейдер с затенением Фонга.

В вершинном шейдере мы используем универсальную переменную, называемую временем, для получения динамических значений.

Эта переменная будет генерировать волну, в которой вершины будут менять свое положение:

precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; uniform float time; // Varying varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUV; void main(void) { vec3 v = position; v.x += sin(2.0 * position.y + (time)) * 0.5; gl_Position = worldViewProjection * vec4(v, 1.0); vPosition = position; vNormal = normal; vUV = uv; }

Синус умножается на позицию.

y и дает следующий результат:

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

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

Я рекомендую проверить это самостоятельно, а затем проверить шейдер Wave в CYOS.

Создание шейдеров на основе Babylon.js и WebGL: теория и примеры

Френель И, наконец, мой любимый шейдер Френеля.

Он меняет интенсивность в зависимости от угла между направлением взгляда и нормалью к вершине.

Вершинный шейдер здесь точно такой же, как и для шейдера заполнения ячеек, и мы легко можем посчитать, что нам нужно для пиксельного шейдера.

Значение отражения Френеля (для определения направления просмотра, нормалей и данных о положении камеры Теги: #перевод #шейдеры #программирование #шейдеры #3d #JavaScript #babylon.js #webgl #microsoft #JavaScript #программирование #Разработка игр #webgl

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.