Поддержка WebGL1 + WebGL2

У меня есть определенная библиотека, которая использует WebGL1 для рендеринга. Он активно использует текстуры с плавающей запятой и инстансный рендеринг.

В настоящее время кажется, что поддержка WebGL1 довольно странная, поскольку некоторые устройства поддерживают, например, WebGL2, где эти расширения являются основными, но не поддерживают WebGL1 или поддерживают его, но не расширения.

В то же время поддержка WebGL2 не впечатляет. Может быть, когда-нибудь так и будет, но ведь это не так.

Я начал смотреть, что потребуется для поддержки обеих версий.

Что касается шейдеров, я думаю, что в основном я могу обойтись #define с вещами. Например, #define texture2D texture и другие подобные вещи.

Когда дело доходит до расширений, это становится более проблематичным, поскольку объекты расширения больше не существуют. В качестве эксперимента я попытался скопировать свойства расширения в объект контекста, например gl.drawArraysInstanced = (...args) => ext.drawArraysInstancedANGLE(...args).

Когда дело доходит до текстур, менять особо нечего, возможно, добавить что-то вроде gl.RGBA8 = gl.RGBA при работе в WebGL1, таким образом, он будет «просто работать» при работе в WebGL2.

Тогда возникает вопрос ... Кто-нибудь пробовал это? Меня беспокоит, что это повлияет на производительность, особенно из-за дополнительной косвенности для вызовов функций. Это также сделает чтение кода менее очевидным, если предполагается, что он может работать в WebGL1. В конце концов, ни в одном контексте WebGL1 нет drawArraysInstanced или RGBA8. Это также мешает набирать TypeScript и другие мелочи.

Другой вариант - иметь ветки по всему коду. Две версии шейдеров (или #ifdef обман), множество разветвлений для каждого места, где требуются форматы текстур, и для каждого места, где выполняется создание экземпляров. Иметь что-то вроде того, что следует повсюду, довольно некрасиво:

if (version === 1) {
  instancedArrays.vertexAttribDivisorANGLE(m0, 1);
  instancedArrays.vertexAttribDivisorANGLE(m1, 1);
  instancedArrays.vertexAttribDivisorANGLE(m2, 1);
  instancedArrays.vertexAttribDivisorANGLE(m3, 1);
} else {
  gl.vertexAttribDivisor(m0, 1);
  gl.vertexAttribDivisor(m1, 1);
  gl.vertexAttribDivisor(m2, 1);
  gl.vertexAttribDivisor(m3, 1);
}

Наконец, может быть, есть третий способ, о котором я не подумал.

Есть рекомендации?


person user2503048    schedule 26.12.2019    source источник


Ответы (2)


К сожалению, я думаю, что большинство ответов будет в первую очередь основано на мнении.

Первый вопрос: зачем поддерживать оба? Если ваша идея отлично работает на WebGL1, просто используйте WebGL1. Если вам абсолютно необходимы функции WebGL2, используйте WebGL2 и поймите, что многие устройства не поддерживают WebGL2 и что Safari еще не поддерживает WebGL2 (сентябрь 2020 г.) и может никогда не поддерживать WebGL2, хотя, похоже, наконец прибыть

Если вы намереваетесь это сделать, twgl попытается упростить задачу с помощью с функцией, которая копирует все расширения WebGL1 в их позиции API WebGL2. Как вы упомянули, вместо

ext = gl.getExtension('ANGLE_instanced_arrays');
ext.drawArraysInstancedANGLE(...)

Вы вместо этого делаете

twgl.addExtensionsToContext(gl);
gl.drawArraysInstanced(...);

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

Дело не в том, чтобы поддерживать одновременно WebGL1 и WebGL2. Скорее это просто для того, чтобы способ написания кода был одинаковым для обоих API.

Тем не менее, в двух API есть реальные различия. Например, чтобы использовать текстуру FLOAT RGBA в WebGL1, вы используете

gl.texImage2D(target, level, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, ...)

В WebGL2 это

gl.texImage2D(target, level, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, ...)

WebGL2 потерпит неудачу, если вы попытаетесь в этом случае назвать его так же, как WebGL1. Есть и другие отличия.

Будет отлично работать в WebGL1 и WebGL2. В спецификации конкретно говорится, что комбинация приводит к RGBA8 на WebGL2.

Обратите внимание, что ваш пример необходимости RGBA8 неверен.

gl.texImage2D(target, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, ...)

Однако самая большая разница в том, что нет причин использовать WebGL2, если вы можете обойтись с WebGL1. Или, наоборот, если вам нужен WebGL2, вы, вероятно, не сможете легко вернуться к WebGL1.

Например, вы упомянули об использовании определений для шейдеров, но что вы собираетесь делать с функциями в WebGL2, которых нет в WebGL1. Такие функции, как textureFetch или оператор mod %, целочисленные атрибуты и т. Д. Если вам нужны эти функции, вам в основном нужно написать шейдер только для WebGL2. Если вам не нужны эти функции, тогда вообще не было смысла использовать WebGL2.

Конечно, если вы действительно хотите пойти на это, возможно, вы захотите создать более изящный рендерер, если у пользователя есть WebGL2, и вернуться к более простому, если WebGL1.

TD; LR IMO Выберите тот или иной

person gman    schedule 26.12.2019
comment
Если быть более точным - я не имею в виду фактическое использование специфических для WebGL2 функций. Если бы поддержка была больше, то наверняка, но сейчас она, кажется, колеблется в районе 50%, что ужасно. Скорее я хочу, чтобы мой текущий код работал на WebGL2. Обычно это не имело бы особого смысла, однако, как я уже сказал, я встречал сообщения от пользователей, в которых говорилось о странных вещах, например о наличии поддержки WebGL2, а не WebGL1. Это просто для поддержки таких устройств, не более того. Хотя меня не интересует вспомогательный код WebGL, я проверю, как twgl внедряет расширения, может быть, у них код лучше, чем у меня - person user2503048; 26.12.2019
comment
Насчет производительности - это правда, что это не должно меня беспокоить, использование таких функций, как установка экземпляров атрибутов, обычно составляет <0,3% моего кода, в то время как большая часть времени тратится на клиентские вещи, такие как обновление иерархий узлов. Может, я слишком педантичен. - person user2503048; 26.12.2019
comment
Как побочный комментарий, WebGL2 фактически отклонил мои существующие вызовы texImage2D с использованием RGBA, но я также особо не изучал это. В любом случае это может быть поддержано, например, определение RGBA8 = RGBA и RGBA32F = RGBA для контекста WebGL1 - person user2503048; 26.12.2019
comment
Не должно быть никаких конфигураций, поддерживающих WebGL2, но не WebGL1. Сообщайте об ошибке всякий раз, когда вы это видите. Исключением являются некоторые расширения, которые не поддерживаются в WebGL1, но доступны в WebGL2 в тех же системах, например, draw_buffers и shader_texture_lod. - person Jeff Gilbert; 27.12.2019
comment
В таком случае код действительно не будет работать. Например, я также использую shader_texture_lod, поэтому в такой реализации работа на WebGL2 была бы единственным решением с моей стороны. Человек сообщил на github о наличии контекста WebGL2, но не контекста WebGL1. Иди на фиг - person user2503048; 27.12.2019

Я обнаружил этот вопрос при написании документации по моей библиотеке, которая преследует множество целей, но одна из них именно такая - одновременная поддержка WebGL1 и WebGL2 для более высокой совместимости между устройствами.

https://xemantic.github.io/shader-web-background/

Например, с помощью BrowserStack я обнаружил, что телефоны Samsung не поддерживают рендеринг текстур с плавающей запятой в WebGL1, в то время как он идеально подходит для них в WebGL2. В то же время WebGL2 никогда не появится на устройствах Apple, но рендеринг половинных текстур с плавающей запятой поддерживается довольно хорошо.

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

https://github.com/xemantic/shader-web-background/blob/main/src/main/js/webgl-utils.js.

Поэтому, чтобы ответить на ваш вопрос, это выполнимо и желательно, но сделать это полностью универсальным способом для каждой функции WebGL может быть довольно сложно. Думаю, первый вопрос, который нужно задать: какой будет общий знаменатель? с точки зрения поддерживаемых расширений.

person morisil    schedule 27.01.2021