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

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

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

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

WebGL основан на OpenGL ES 2.0, который, в свою очередь, является специальной версией для работы на мобильных устройствах. Спецификация WebGL была выпущена в 2011 году, разрабатывается и поддерживается некоммерческой организацией Kronos Group, сайт которой частенько лежит, что ещё более усложняет изучение. Известно, что в настоящее время идёт разработка спецификации версии 2.0.


Статистика поддержки WebGL разными браузерами с сайта caniuse.com

WebGL доступен в большинстве современных браузеров и поддерживается у 83% пользователей. Приятным бонусом разработки на WebGL является то, что вы будете поддерживать только современные браузеры и забудете о кошмарах ECMAScript 3.

Если вы думаете, что WebGL рисует 3D, вы ошибаетесь. WebGL ничего не знает о 3D, это скорее низкоуровневый 2D API, и всё что он умеет делать, это рисовать треугольники. Но он умеет рисовать их очень много и очень быстро.

Хотите нарисовать квадрат? Пожалуйста, соедините два треугольника. Нужна линия? Без проблем, всего лишь несколько последовательно соединенных треугольников.

Как нарисовать треугольник


Поскольку все фигуры в WebGL состоят из треугольников, поэтапно разберём, как отобразить один треугольник.

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

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

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

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

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

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

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


Если вершинный шейдер определяет геометрию фигуры, то фрагментный — её цвет

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

Пример вершинного шейдера:
attribute vec3 a_position;
attribute vec3 a_color;
uniform vec3 u_position;
varying vec3 v_color;

void main(void) {
    v_color = a_color;
    gl_Position = vec4(u_position + a_position, 1.0);
}

Пример фрагментного шейдера:
precision mediump float;
varying vec3 v_сolor;

void main(void) {
    gl_FragColor = vec4(v_color.rgb, 1.0);
}

Код состоит из переменных и главной функции, возвращающей основной результат работы шейдера: gl_Position передаёт координаты, а gl_FragColor устанавливает цвет.

Шейдеры имеют три типа переменных, которые передаются из основной программы:
  1. attributes — доступны только в вершинном шейдере, разные для каждой из вершин;
  2. uniforms — доступны в обоих шейдерах и одинаковы для всех вызовов шейдера;
  3. varying — служат для передачи информации от вершинного шейдера к фрагментному.

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


Значения varying переменных внутри треугольника вычисляются
на основе значений этих переменных в вершинах


Попробуем инициализировать данные шейдеры. Для начала получим контекст WebGL:
var gl = canvas.getContext(‘webgl’);

Код шейдеров представляется обычной строкой и для использования его нужно скомпилировать:
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, document.getElementById('vertexShader').text);
gl.compileShader(vertexShader);

var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, document.getElementById('fragmentShader').text);
gl.compileShader(fragmentShader);

Для связывания двух типов шейдеров вместе используется шейдерная программа:
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

Если uniform-переменные связываются напрямую с переменными из js, то для атрибутов нужно использовать ещё одну сущность — буферы. Данные буферов хранятся в памяти видеокарты, что даёт значительный прирост в скорости рендеринга.

В нашем случае нам понадобятся:
  1. буфер вершин, который хранит всю информацию о расположению вершин геометрии;
  2. буфер цветов с информацией о цвете вершин.

Зададим буфер вершин:

Геометрия нашего треугольника

Вершины имеют координаты:
  • (0, 0, 0);
  • (0.5, 1, 0);
  • (1, 0, 0).

Стоит отметить, что при работе с буферами следует учитывать несколько особенностей:
  1. данные в буфер передаются одним массивом без вложенности, в случае нашего треугольника данные будут выглядеть следующим образом: [0, 0, 0, 0.5, 1, 0, 1, 0, 0];
  2. передаваться должны только типизированные массивы;
  3. прежде чем передать данные, вы должны точно указать, какой буфер будет использоваться, методом gl.bindBuffer.

Как это будет выглядеть в программе:
var vertexBuffer = gl.createBuffer();
var vertices = [0, 0, 0, 0.5, 1, 0, 1, 0, 0];
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),  gl.STATIC_DRAW);

Создадим аналогичным образом буфер цветов. Цвет указываем для каждой из вершин в формате RGB, где каждая компонента цвета от 0 до 1:
var colorBuffer = gl.createBuffer();
var colors = [1, 0, 0, 0, 1, 0, 0, 0, 1];
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

Всё, что нам осталось, чтобы нарисовать треугольник, — это связать данные с переменными шейдерной программы и вызвать методы отрисовки. Для этого:
// Получим местоположение переменных в программе шейдеров
var uPosition = gl.getUniformLocation(program, 'u_position');
var aPosition = gl.getAttribLocation(program, 'a_position');
var aColor = gl.getAttribLocation(program, 'a_color');

// Укажем какую шейдерную программу мы намерены далее использовать
gl.useProgram(program);

// Передаем в uniform-переменную положение треугольника
gl.uniform3fv(uPosition, [0, 0, 0]);

// Связываем данные цветов
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.enableVertexAttribArray(aColor);
// Вторым аргументом передаём размерность, RGB имеет 3 компоненты
gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0);

// И вершин
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);

// Очищаем сцену, закрашивая её в белый цвет
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

// Рисуем треугольник
// Третьим аргументом передаём количество вершин геометрии
gl.drawArrays(gl.TRIANGLES, 0, 3);

Наш треугольник готов:

Полный код примера можно
посмотреть здесь


Как я и говорил, цвет пикселей внутри треугольника линейно интерполируется между разноцветными вершинами. Мы смогли нарисовать самую простейшую фигуру с помощью WebGL и познакомились с шейдерами и буферами. Перейдём к следующему этапу.

Как нарисовать куб и заставить его вращаться


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



Нам придётся прописать каждую вершину каждого треугольника. Есть способы использовать более короткую запись, но для начала сделаем по-простому:
Это — просто
var vertexBuffer = gl.createBuffer();
var vertices = [
    // Передняя грань
    -1, -1, -1,
    1, -1, -1,
    -1, -1, 1,

    1, -1, 1,
    -1, -1, 1,
    1, -1, -1,

    // Задняя грань
    -1, 1, -1,
    -1, 1, 1,
    1, 1, -1,

    1, 1, 1,
    1, 1, -1,
    -1, 1, 1,

    // Нижняя грань
    -1, -1, -1,
    -1, 1, -1,
    1, -1, -1,

    1, 1, -1,
    1, -1, -1,
    -1, 1, -1,

    // Верхняя грань
    -1, -1, 1,
    1, -1, 1,
    -1, 1, 1,

    1, 1, 1,
    -1, 1, 1,
    1, -1, 1,

    // Левая грань
    -1, -1, -1,
    -1, -1, 1,
    -1, 1, -1,

    -1, 1, 1,
    -1, 1, -1,
    -1, -1, 1,

    // Правая грань
    1, -1, -1,
    1, 1, -1,
    1, -1, 1,

    1, 1, 1,
    1, -1, 1,
    1, 1, -1
];
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);


Аналогично запишем буфер цветов, раскрасив грани куба в три цвета:
  1. (1, 0.5, 0.5)
  2. (0.5, 0.7, 1)
  3. (0.3, 1, 0.3)

Тоже простой пример
var colorBuffer = gl.createBuffer();
var colors = [
    // Передняя грань
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,

    // Задняя грань
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,
    1, 0.5, 0.5,

    // Нижняя грань
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,

    // Верхняя грань
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,
    0.5, 0.7, 1,

    // Левая грань
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3,

    // Правая грань
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3,
    0.3, 1, 0.3
];
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);


Положение треугольника в пространстве задавалось с помощью вектора размерности три. Но фигура может не только менять положение, она может ещё вращаться и масштабироваться. Поэтому в трёхмерной графике используются не вектор положения, а матрица.

Известно, что матрица поворота в трёхмерном пространстве задаётся с помощью матрицы размером 3?3. К этой матрице добавляется вектор положения, таким образом, в итоге используется матрица 4?4.

WebGL никак не помогает нам работать с матрицами, поэтому, чтобы не тратить на них много времени, будем использовать довольно известную библиотеку glMatrix. Создадим с помощью неё единичную матрицу положения:
var cubeMatrix = mat4.create();

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


Перспектива куба на экране

За это преобразование отвечает матрица перспективы. C glMatrix она создаётся в две строчки:
var cameraMatrix = mat4.create();
mat4.perspective(cameraMatrix, 0.785, window.innerWidth / window.innerHeight, 0.1, 1000);

Метод mat4.perspective(matrix, fov, aspect, near, far) принимает пять параметров:
  1. matrix — матрица, которую необходимо изменить;
  2. fov — угл обзора в радианах;
  3. aspect — cоотношение сторон экрана;
  4. near — минимальное расстояние до объектов, которые будут видны;
  5. far — максимальное расстояние до объектов, которые будут видны.

Чтобы изображение куба попало в камеру, сдвинем камеру по оси Z:
mat4.translate(cameraMatrix, cameraMatrix, [0, 0, -5]);

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

Вершинный шейдер:
attribute vec3 a_position;
attribute vec3 a_color;
uniform mat4 u_cube;
uniform mat4 u_camera;
varying vec3 v_color;
void main(void) {
    v_color = a_color;
    gl_Position = u_camera * u_cube * vec4(a_position, 1.0);
}

Фрагментый шейдер:
precision mediump float;
varying vec3 v_color;
void main(void) {
    gl_FragColor = vec4(v_color.rgb, 1.0);
}

Инициализация шейдеров происходит точно так же, как и в случае треугольника:
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, document.getElementById('vertexShader').text);
gl.compileShader(vertexShader);

var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, document.getElementById('fragmentShader').text);
gl.compileShader(fragmentShader);

var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

var uCube = gl.getUniformLocation(program, 'u_cube');
var uCamera = gl.getUniformLocation(program, 'u_camera');
var aPosition = gl.getAttribLocation(program, 'a_position');
var aColor = gl.getAttribLocation(program, 'a_color');

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

В отличие от других подобных методов, requestAnimationFrame вызывает переданную функцию только когда видеокарта свободна и готова к отрисовке следующего кадра.
// Создадим единичную матрицу положения куба
var cubeMatrix = mat4.create();

// Запомним время последней отрисовки кадра
var lastRenderTime = Date.now();

function render() {
    // Запрашиваем рендеринг на следующий кадр
    requestAnimationFrame(render);

    // Получаем время прошедшее с прошлого кадра
    var time = Date.now();
    var dt = lastRenderTime - time;

    // Вращаем куб относительно оси Y
    mat4.rotateY(cubeMatrix, cubeMatrix, dt / 1000);
    // Вращаем куб относительно оси Z
    mat4.rotateZ(cubeMatrix, cubeMatrix, dt / 1000);

    // Очищаем сцену, закрашивая её в белый цвет
    gl.clearColor(1.0, 1.0, 1.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // Включаем фильтр глубины
    gl.enable(gl.DEPTH_TEST);

    gl.useProgram(program);

    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.enableVertexAttribArray(aPosition);
    gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.enableVertexAttribArray(aColor);
    gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0);

    gl.uniformMatrix4fv(uCube, false, cubeMatrix);
    gl.uniformMatrix4fv(uCamera, false, cameraMatrix);

    gl.drawArrays(gl.TRIANGLES, 0, 36);

    lastRenderTime = time;
}

render();

Получаем вращающийся куб:

Полный код примера можно
посмотреть здесь


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

Как отлаживать


Поскольку при работе с WebGL часть программы исполняется на стороне видеокарты, процесс отладки значительно усложняется. Нет привычных инструментов в виде DevTools и даже console.log. В интернете есть множество статей и докладов на эту тему, здесь же приведу лишь основные способы.

Чтобы понять, что код шейдеров был написан с ошибкой, после компиляции шейдеров можно использовать следующий метод:
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
    console.log(gl.getShaderInfoLog(vertexShader));
}

if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
    console.log(gl.getShaderInfoLog(fragmentShader));
}

if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.log('Could not initialize shaders');
}

Также есть специальное расширение для браузеров WebGL-Inspector. Оно позволяет отследить загруженные шейдеры, буферы, текстуры в видеокарту, и вызовы методов WebGL.

Ещё есть Shader Editor, в Firefox DevTools этот функционал уже встроен, а для Chrome есть расширение, которое позволяет редактировать код шейдеров прямо в процессе работы приложения.

Куда двигаться дальше


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

Напоследок — список полезных ресурсов, где можно продолжить изучение WebGL.
  • Полный код примеров с треугольником и кубом.
  • Краткая сводка WebGL с сайта Kronos Group.
  • Для более подробного изучения рекомендую пройти серию уроков WebGL Learning.
  • Бесплатный курс на Udacity по основам 3D. Хотя в курсе используется библиотека three.js, он будет полезен всем.
  • Доклад Владимира Агафонкина про WebGL и Mapbox c Frontend Dev Conf.
  • Слайды доклада Debugging and Optimizing WebGL Applications.

Комментарии (23)


  1. LinGG
    23.12.2015 11:16

    мне интересно, почему такая отличная и не новая технология настолько невостребованна? есть какие-то серьезные проекты на нем?


    1. m1el
      23.12.2015 11:25
      +8

      Google Maps, рендереры Open Street Maps, FinalMesh.


    1. kobezzza
      23.12.2015 11:57
      +2

      UnrealEngine4 и Unity5 умеют компилить свои проекты в WebGl + ASM.js


    1. Trufi
      23.12.2015 12:10
      +1

      Сложно сказать, но есть несколько предположений:

      1. Сайты, за редкими исключениями, скорее про интерфейсы, а интерфейсы это про 2D
      2. Довольно большой порог вхождения в технологию
      3. Многие компании до сих пор поддерживают IE8 из-за 0.5% пользователей, чего уж тут говорить про 17%, у которых нет WebGL


    1. gfxdevrus
      23.12.2015 12:40

      Не соглашусь, технология распространяется и интерес пользователей к ней всё время растёт. Сужу по крайней мере по тому количеству людей, которые интересуются движком Blend4Web в этом году по сравнению с тем, что было в прошлом. Просто в отличие от продуктов типа Flash или трёхмерных движков, никто особо не занимается раскруткой бренда WebGL.


      1. Lertmind
        23.12.2015 12:59

        Можно добавить сюда популярный 2D-движок pixi.js, который использует WebGL, но скатывается к canvas если что. Таких примеров наверно достаточно ещё.


    1. Aquahawk
      23.12.2015 12:43
      -2

      Потому что JS. Даже не js в мир браузеров. Там отвратительная поддержка стандарта настолько надёжно чтобы один и тот же код с вызовами апи работал одинаково. Потому что до сих пор ни одно демо которое я видел не работает и на компе в разных браузерах и на мобиле.


    1. kashey
      23.12.2015 14:18

      WebGL до сих пор не совсем стабилен и часто крешиться. Да и хорошо обоснованная разработка на нем только год как актальна — с выходом новых версий iOS и IE.


  1. Lertmind
    23.12.2015 13:21
    +1

    К чему этот странный абзац?

    Если вы думаете, что WebGL рисует 3D, вы ошибаетесь. WebGL ничего не знает о 3D, это скорее низкоуровневый 2D API, и всё что он умеет делать, это рисовать треугольники. Но он умеет рисовать их очень много и очень быстро.
    Вы же, например, сами используете ниже векторы из трёх компонент. Я к тому, что API предполагает 3D.


    1. kashey
      23.12.2015 14:15
      +3

      Там по умолчанию 4D вектора — (x,y,z,w). Но на выходе из вершинного шейдера данные уже в 2D координатах + не совсем с ними связанный(сжатый) Z. Фактически 2D.
      При этом в шейдер можно передавать что угодно — 1D, 2D, 3D, 20D. Главное что бы он такие данные переварил.


      1. Lertmind
        23.12.2015 15:12
        +1

        Но на выходе из вершинного шейдера данные уже в 2D координатах + не совсем с ними связанный(сжатый) Z. Фактически 2D.
        Этот этап в графическом pipeline называется растеризация, он очевидно нужен чтобы вывести картинку на двухмерный экран монитора, но до этого-то происходили операции вполне себе в трёхмерном пространстве.

        По вашей логике OpenGL тоже 2D API? Для меня «низкоуровневый 2D API» это обращение к пикселям на экране, а OpenGL (WebGL) уровнем выше и позволяет работать с трёхмерными объектами. Так что я придерживаюсь википедии:
        Open Graphics Library (OpenGL) is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics.
        WebGL (Web Graphics Library) is a JavaScript API for rendering interactive 3D computer graphics and 2D graphics within any compatible web browser without the use of plug-ins


        1. kashey
          23.12.2015 15:23
          +1

          Делить на 2D и 3D можно было старый OpenGL, который с fixed pipeline. Сейчас это понятие достаточно эфемерно.

          VS — программа для каждого элемента данных переданных в конвейер. PS — программа для каждого пикселя блока собранного из каких-то элементов по конвейеру выше.

          Двуногое животное без перьев, а не 3D API для рендеринга. На нем и физику считают, в крайнем случае партиклы…


  1. Ravager
    23.12.2015 13:52
    -2

    Если вы думаете, что WebGL рисует 3D, вы ошибаетесь. WebGL ничего не знает о 3D, это скорее низкоуровневый 2D API, и всё что он умеет делать, это рисовать треугольники. Но он умеет рисовать их очень много и очень быстро.

    да неужто? а как же three.js или playcanv.as? они наверное сами 3д дорисовывают, от нечего делать)


  1. pav5000
    23.12.2015 14:15

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


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


    1. kashey
      23.12.2015 14:16

      В WebGL(и GL ES) с нормальной растеризицией некоторых базовых примитивов есть проблемы. Приходятся доставать из шкафа бубен.


      1. entomolog
        24.12.2015 03:08

        У меня что-то с GL_LINES никогда проблем не было (ну кроме как толщина не везде работает). А вот GL_POINTS да, зачастую не работают.
        Да вобщем-то GL_LINES и GL_POINTS не особо то и нужны, разве что для дебага.


        1. kashey
          24.12.2015 07:35

          Основные претензии не к самому GL_LINE, но к качеству картинки и возможных настройках.


    1. Ravager
      23.12.2015 14:22

      насколько я знаю в gl есть ограничение на толщину линии. причем везде разное)


  1. stepik777
    23.12.2015 14:24
    +4

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


  1. entomolog
    24.12.2015 03:45

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

    Большинство руководств или статей перепрыгивают сразу на использование какой-нибудь библиотеки.
    Если цель не поиграться, а сделать продукт, то это весьма правильное решение.

    Если вы думаете, что WebGL рисует 3D, вы ошибаетесь. WebGL ничего не знает о 3D, это скорее низкоуровневый 2D API, и всё что он умеет делать, это рисовать треугольники.
    Посыл правильный, но вы немного перегнули палку :).

    Как по мне, если интерес представляет именно работа с графическим АПИ, то WebGL в принципе тот-же OpenGL ES 2.0, и является урезанной десктопной версией. Рассматривать WebGL как самостоятельную сущность, о которой нет документации и статей, это перегиб. Они просто не нужны, так как есть огромное число разных туториалов по OpenGL ES и OpenGL 4.*.

    В контексте Web'а, как по мне интереснее рассмотреть немного более практические примеры с использованием графических библиотек. Ведь, писать все с нуля, не имеет смысла. Точно так же как никто не пишет игры на чистом OpenGL, так же и WebGL не для этого.

    Статья хорошая, для людей, интересующихся этой темой, думаю создаст правильное представление о WebGL.


    1. Trufi
      24.12.2015 08:13
      +1

      Спасибо за замечания!

      Изучение и работа с WebGL действительно сильно облегчается за счёт большого количества документации и примеров к OpenGL.

      В контексте Web'а, как по мне интереснее рассмотреть немного более практические примеры с использованием графических библиотек. Ведь, писать все с нуля, не имеет смысла. Точно так же как никто не пишет игры на чистом OpenGL, так же и WebGL не для этого.
      Использование готовых решений сильно зависит от проекта и требований к нему. Например, мы перешли с библиотеки и работаем с собственной обёрткой над WebGL. Заточенность инструмента под наши задачи дала ряд преимуществ: гибкость, быстродействие, небольшой размер.


      1. entomolog
        24.12.2015 18:54

        Проект не open source, да? Если open source, было бы очень интересно попробовать.


        1. Trufi
          25.12.2015 07:42

          Open source только обёртка github.com/2gis/2gl, но она имеет мало смысла без самого проекта.