оглавление

WebGLFundamentals.org

WebGL - Растеризация или 3D-библиотека

Эта статья - что-то вроде отступления от серии статей о WebGL. Первая статья начинается с основ WebGL.

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

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

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

По моему мнению то, что называет себя 3D-библиотекой, должно выполнять эти вещи за вас. У вас должна быть возможность передать библиотеке 3D-данные, некоторые параметры материала, плюс освещение, и библиотека должна нарисовать всё для вас. WebGL (как и OpenGL ES 2.0+) может отрисовывать 3D, но в целом не попадает под данное выше определение.

Для сравнения, C++ не может редактировать тексты из коробки. Поэтому мы и не называем C++ текстовым редактором, хотя он и может быть написан на C++. Аналогично, WebGL не отрисовывает 3D-графику из коробки. Вы можете написать библиотеку, которая отрисует 3D-графику с помощью WebGL, но сам по себе он не может работать с 3D-графикой.

Следующий пример. Предположим, нам нужно отобразить 3D-куб с освещением.

Вот код на three.js, который делает это:

  // настраиваем WebGL
  var c = document.getElementById("c");
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(c.clientWidth, c.clientHeight);
  c.appendChild(renderer.domElement);

  // устанавливаем камеру
  camera = new THREE.PerspectiveCamera(
      70, c.clientWidth / c.clientHeight, 1, 1000);
  camera.position.z = 400;
  camera.updateProjectionMatrix();

  // создаём сцену
  scene = new THREE.Scene();

  // создаём куб
  var geometry = new THREE.BoxGeometry(200, 200, 200);

  // создаём материал
  var material = new THREE.MeshPhongMaterial({
    ambient: 0x555555,
    color: 0x555555,
    specular: 0xffffff,
    shininess: 50,
    shading: THREE.SmoothShading
  });

  // создаём меш на основании геометрии и материала
  mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  // добавляем 2 источника освещения
  light1 = new THREE.PointLight(0xff0040, 2, 0);
  light1.position.set(200, 100, 300);
  scene.add(light1);

  light2 = new THREE.PointLight(0x0040ff, 2, 0);
  light2.position.set(-200, 100, 300);
  scene.add(light2);

И вот куб уже отображается:

Вот аналогичный пример кода на OpenGL (не ES) для отображения куба с 2 источниками освещения.

  // настройка
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(70.0, width / height, 1, 1000);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glClearColor(0.0, 0.0, 0.0, 0.0);
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);
  glEnable(GL_LIGHTING);

  // задаём 2 источника освещения
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHT1);
  float light0_position[] = {  200, 100, 300, };
  float light1_position[] = { -200, 100, 300, };
  float light0_color[] = { 1, 0, 0.25, 1, };
  float light1_color[] = { 0, 0.25, 1, 1, };
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_color);
  glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
  glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
...

  // отрисовка куба
  static int count = 0;
  ++count;

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  double angle = count * 0.1;
  glTranslatef(0, 0, -400);
  glRotatef(angle, 0, 1, 0);

  glBegin(GL_TRIANGLES);
  glNormal3f(0, 0, 1);
  glVertex3f(-100, -100, 100);
  glVertex3f( 100, -100, 100);
  glVertex3f(-100,  100, 100);
  glVertex3f(-100,  100, 100);
  glVertex3f( 100, -100, 100);
  glVertex3f( 100,  100, 100);

  /*
  ...
  ... повторить для 5 других граней куба
  ...
  */

  glEnd();

Заметьте, что нам практически не нужно знаний 3D-математики для этих двух примеров. Сравните их с WebGL. Я не буду приводить код, необходимый для WebGL. Кода будет ненамного больше. Дело даже не в том, сколько строк кода необходимо. Дело в том, сколько всего нужно знать. Эти две 3D-библиотеки участвуют в 3D. Вы передаёте им положение камеры и поле зрения, пару источников света и куб. Остальное они доделывают сами. Другими словами, они являются 3D-библиотеками.

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

Весь смысл 3D-библиотек в том, что все эти знания уже встроены в библиотеку, и вам не нужно самому обладать этими знаниями, вы можете положиться на библиотеку. Это было свойственно OpenGL, пример с которым был показан выше. Это свойственно другим библиотекам вроде three.js. Но это НЕ свойственно OpenGL ES 2.0+ или WebGL.

Поэтому будет заблуждением назвать WebGL 3D-библиотекой. Пользователь придёт к WebGL с мыслью "О, 3D-библиотека. Здорово. Сейчас она сделает за меня всё 3D". А затем он понимает, что нет, всё не так просто.

Мы можем зайти ещё дальше. Вот каркас 3D-куба на Canvas:

А вот каркас 3D-куба на WebGL:

Если вы взглянете на код, вы увидите, что между ними не такая уж большая разница в плане количества знаний и даже в плане объёма кода. В конечном счёте, версия с Canvas обходит вершины, выполняет ПЕРЕДАННУЮ НАМИ математику и рисует линии в 2D. Версия с WebGL делает то же самое, с тем лишь исключением, что ПЕРЕДАННАЯ НАМИ математика выполняется на языке GLSL на видеокарте.

Цель последней демонстрации - показать, что по сути WebGL является средством растеризации, как и Canvas 2D. Конечно, WebGL включает в себя функциональные возможности, которые упрощают реализацию 3D. WebGL имеет буфер глубины, который делает сортировку по глубине значительно более простой. Также WebGL содержит множество встроенных математических функций, которые очень полезны для 3D-математики, хотя это не делает их 3D-функциями. Скорей это библиотека для математики. Вы используете их для вычисления математики хоть для 1D, хоть для 2D, хоть для 3D. Но сам WebGL - только растеризация. Вам необходимо задать координаты отсечения, которые указывают, что нужно отрисовать. Вы передаёте x,y,z,w и всё это делится на W перед рендерингом, но этого вряд ли достаточно, чтобы назвать WebGL 3D-библиотекой. В 3D-библиотеке вы указываете 3D-данные, библиотека сама вычисляет точки пространства отсечения из 3D.

Надеюсь, что, по крайней мере, вы понимаете, из чего я исхожу, когда говорю, что WebGL нельзя назвать 3D-библиотекой. Также надеюсь, что вы понимаете, что 3D-библиотека должна управлять 3D-данными. OpenGL это умеет. Three.js тоже. OpenGL ES 2.0 и WebGL - нет. Поэтому, на мой взгляд, он не принадлежит к понятию "3D-библиотека".

Цель данной статьи - дать разработчику, который хочет познакомиться с WebGL, понимание того, чем на самом деле является WebGL. Зная, что WebGL не является 3D-библиотекой, и разработчику придётся самому разбираться во многих вещах, человек сможет решить, захочет ли он сам разобраться в 3D-математике, или же он выберет 3D-библиотеку, которая сделает всё за него. Кроме того, подобное понимание снимает завесу тайны с того, как работает WebGL.

Вопросы? Спросите на stackoverflow.
Нашли ошибку? Создайте задачу на github.
comments powered by Disqus