Содержание

WebGL2Fundamentals.org

Fix, Fork, Contribute

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

Этот пост является своего рода побочной темой в серии постов о WebGL. Первый начался с основ

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

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

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

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

Чтобы привести аналогию, C++ не “обрабатывает слова” из коробки. Мы не называем C++ “текстовым процессором”, даже хотя текстовые процессоры могут быть написаны на C++. Аналогично WebGL не рисует 3D графику из коробки. Вы можете написать библиотеку, которая будет рисовать 3D графику с WebGL, но сама по себе она не делает 3D графику.

Чтобы привести дальнейший пример, предположим, что мы хотим нарисовать куб в 3D с источниками света.

Вот код в three.js для отображения этого

  // Настройка.
  renderer = new THREE.WebGLRenderer({canvas: document.querySelector("#canvas")});
  c.appendChild(renderer.domElement);

  // Создаём и настраиваем камеру.
  camera = new THREE.PerspectiveCamera(70, 1, 1, 1000);
  camera.position.z = 400;

  // Создаём сцену
  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, как показано выше. Это верно для других 3D библиотек, таких как three.js. Это НЕ верно для OpenGL ES 2.0+ или WebGL.

Кажется вводящим в заблуждение называть WebGL 3D библиотекой. Пользователь, приходящий к WebGL, подумает “о, 3D библиотека. Круто. Это будет делать 3D за меня” и затем обнаружит трудным путём, что нет, это совсем не так.

Мы можем даже пойти на шаг дальше. Вот рисование 3D каркасного куба в Canvas.

И вот рисование каркасного куба в WebGL.

Если вы изучите код, вы увидите, что нет большой разницы с точки зрения количества знаний или, если на то пошло, даже кода. В конечном счёте версия Canvas перебирает вершины, делает математику, КОТОРУЮ МЫ ПОСТАВИЛИ, и рисует некоторые линии в 2D. Версия WebGL делает то же самое, кроме того, что математика, КОТОРУЮ МЫ ПОСТАВИЛИ, находится в GLSL и выполняется GPU.

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

Чтобы дать ещё несколько точек отсчёта, emscripten предоставляет эмуляцию старого OpenGL поверх WebGL. Этот код находится здесь. Если вы просмотрите код, вы увидите, что многое из этого генерирует шейдеры для эмуляции старых 3D частей OpenGL, которые были удалены в OpenGL ES 2.0. Вы можете увидеть то же самое в Regal, проект, который NVidia начала для эмуляции старого OpenGL с включённым 3D в современном OpenGL без включённого 3D. Ещё один пример, вот шейдеры, которые использует three.js для предоставления 3D. Вы можете видеть, что многое происходит во всех этих примерах. Всё это, а также код для поддержки этого, поставляется этими библиотеками, а не WebGL.

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

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

Есть предложения или замечания? Создайте issue на GitHub.
comments powered by Disqus