Я заметил, что некоторые разработчики OpenGL испытывают сложности с тем, как WebGL работает с прозрачностью во вторичном буфере (т.е. в canvas), поэтому я решил, что будет полезно осветить отличия в работе с прозрачностью между WebGL и OpenGL.
Самой большой разницей между OpenGL и WebGL является то, что OpenGL отрисовывает во вторичный буфер графику, которая ни с чем не смешана, а именно не содержит компонентов менеджера окон операционной системы, поэтому прозрачность здесь не важна.
WebGL смешивается браузером с веб-страницей, и по умолчанию используется
предумноженная прозрачность, как в случае с .png в теге <img>
с
прозрачностью и теге canvas 2D.
Есть несколько способов, как сделать WebGL более похожим на OpenGL.
gl = canvas.getContext("webgl", {
premultipliedAlpha: false // отключаем предумноженную прозрачность
});
Значение по умолчанию - true.
Результат, конечно, всё равно получится составной - добавится цвет фона HTML-элемента под canvas (цвет фона самого canavs, цвет фона страницы, объектов позади canvas, если z-index для canvas меньше нуля и т.д.) - другими словами, области страницы, цвет которой задан в CSS.
Действительно хороший способ узнать, есть ли у вас проблемы с прозрачностью - установить цвет фона элемента canvas в ярко красный. Вы сразу же разберётесь, что происходит.
<canvas style="background: red;"><canvas>
Также вы можете установить чёрный цвет, чтобы скрыть проблемы прозрачности.
gl = canvas.getContext("webgl", { alpha: false }};
Поведение в этом случае будет напоминать OpenGL, так как вторичный буфер будет состоять лишь из RGB. Возможно, это лучший вариант, так как хороший браузер может увидеть, что у вас нет прозрачности и сможет оптимизировать композицию WebGL. Разумеется, это означает, что у вас не будет прозрачности во вторичном буфере, поэтому если вы используете прозрачность во вторичном буфере по каким-то причинам, этот вариант может не подойти. Лишь несколько приложений из известных мне использовали прозрачность во вторичном буфере. Я думаю, что, возможно, эта опция должна быть по умолчанию.
..
renderScene();
..
// устанавливаем значение прозрачности для вторичного буфера в 1.0
gl.clearColor(1, 1, 1, 1);
gl.colorMask(false, false, false, true);
gl.clear(gl.COLOR_BUFFER_BIT);
Очистка обычно выполняется очень быстро, так как поддерживается на аппаратном уровне. Я использовал такой подход в большинстве своих демонстраций. Если бы я был умнее, я бы переключился на метод #2. Возможно, я сделаю это сразу после этой статьи. Вероятно, большинство библиотек WebGL должны использовать этот подход по умолчанию. Те несколько разработчиков, которые действительно используют прозрачность для композитных эффектов, может запросить её. Остальные получат лучшую производительность и меньше сюрпризов.
// очищаем вторичный буфер при инициализации
gl.clearColor(1,1,1,1);
gl.clear(gl.COLOR_BUFFER_BIT);
// отключаем отрисовку прозрачности
gl.colorMask(true, true, true, false);
Разумеется, если вы рендерите в собственные буферы, вам может понадобиться на время включить прозрачность, а затем снова её выключить, когда вы переключитесь обратно на рендеринг в canvas.
В своей работе я загружаю изображения с прозрачностью в WebGL. WebGL получит значения как есть из PNG-файла со значениями цветов без предумножения. В своей работе с OpenGL я использовал такой же подход, так как в нём нет потерь, в то время как предумножение происходит с потерями.
1, 0.5, 0.5, 0 // RGBA
Это допустимое значение при отсутствии предумножения, вто время как при
использовании предумножения значение становится невозможным, так как a = 0
означает, что r
, g
и b
тоже будут равны нулю.
При желании вы можете включить предумноженную прозрачность в WebGL. Этого
можно добиться установкой UNPACK_PREMULTIPLY_ALPHA_WEBGL
в значение true:
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
По умолчанию предумножение отсутствует.
Стоит отметить, что большинство (если не все) реализации Canvas 2D работают
с предумноженной прозрачностью. Это значит, что при передаче их в WebGL и
при значении UNPACK_PREMULTIPLY_ALPHA_WEBGL
равном false WebGL будет
конвертировать значение обратно в значения без предумножения.
Практически все приложения на OpenGL, которые я написал, использовали
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
Это работает в случае текстур без предумноженной прозрачности.
Если же вам захочется работать с текстурами с предумноженной прозрачностью, вам, вероятно, подойдёт
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
Вот и все методы, о которых мне известно. Если вы знаете другие - пишите в комментариях.