Рендеринг в (или чтение из?) фреймбуфер не работает в мобильном Safari

#webgl #mobile-safari #framebuffer

#webgl #mobile-safari #фреймбуфер

Вопрос:

По какой-то причине рендеринг в фреймбуфер, а затем из буфера на экран, похоже, не работает в мобильном Safari. У меня не было возможности проверить desktop Safari, но мой Firefox отлично отображает логотип Microsoft, на моем телефоне я просто вижу границу:

 const dmnsn = [800, 600],
  glCtx = twgl.getContext(document.createElement("canvas")),
  imgLc =
    "https://upload.wikimedia.org/wikipedia/commons/b/b6/Microsoft_logo_(1987).svg";
var bfInf = twgl.primitives.createXYQuadBufferInfo(glCtx),
  pgInf = twgl.createProgramInfo(glCtx, ["vs", "fs"]),
  imgTx = twgl.createTexture(glCtx, { src: imgLc }, tstSB),
  scnBf = twgl.createFramebufferInfo(glCtx, [{ wrap: glCtx.REPEAT}],dmnsn[0],dmnsn[1]);

function plcCv() {
  document.body.append(glCtx.canvas);
  glCtx.canvas.width = dmnsn[0];
  glCtx.canvas.height = dmnsn[1];
}
function drwTx(tx, fbi, fy) {
  twgl.bindFramebufferInfo(glCtx, fbi);
  twgl.drawObjectList(glCtx, [
    {
      programInfo: pgInf,
      bufferInfo: bfInf,
      uniforms: { u_texture: tx, u_flipy: fy }
    }
  ]);
}
function tstSB() {
  plcCv();
  drwTx(imgTx, scnBf, true);
  drwTx(scnBf.attachments[0], null, false);
}  
 html{
  height:100%;
}
body{
  margin:0;
  height:100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
canvas{
  border-style:solid;
}  
 <script id="vs" type="x-shader/x-vertex">
  
  attribute vec4 position;
  attribute vec2 texcoord;
    
  uniform bool u_flipy;
  
  varying vec2 v_texcoord;

  void main() {
    if(u_flipy) {
      v_texcoord = vec2(texcoord.x, 1.0 - texcoord.y);
    } else {
      v_texcoord = texcoord;
    }
    gl_Position = position;
  }
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D u_texture;

void main() {
  gl_FragColor=texture2D(u_texture,v_texcoord);
}
</script>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>  

Я знаю, что рисование SVGS является несколько экспериментальным, но, похоже, это отлично работает и в мобильном Safari.

Ответ №1:

Проверка ошибок в Safari, которые я вижу

WebGL: drawElements: текстура, привязанная к текстурному блоку 0, не поддается рендерингу. Возможно, он не имеет мощности 2 и имеет несовместимую фильтрацию текстур или не является «текстурным завершенным», или это тип с плавающей точкой / полуплавающей точкой с линейной фильтрацией и без соответствующего линейного расширения с плавающей точкой / полуплавающей точкой. Включено.

Чтобы выяснить, в какой текстуре возникла проблема, использовал webgl-lint и добавил эти строки

   const ext = glCtx.getExtension('GMAN_debug_helper');
  const tagObject = ext ? ext.tagObject.bind(ext) : () => {};
  tagObject(imgTx, 'imgTx');
  tagObject(scnBf.attachments[0], 'colorAttach');
  

Затем я получил эту ошибку

ошибка в drawElements(TRIANGLES, 6, UNSIGNED_SHORT, 0): текстура WebGLTexture («colorAttach») на текстурном блоке 0, на который ссылается uniform sampler2D u_texture, не поддается визуализации: ширина текстуры (800) не равна степени 2, а высота (600) не равна степени 2, но TEXTURE_WRAP_S (ПОВТОР) не является CLAMP_TO_EDGE и TEXTURE_WRAP_T (ПОВТОР) не является CLAMP_TO_EDGE. с WebGLProgram («неназванный«) в качестве текущей программы с привязкой к массиву вершин по умолчанию

Проблема в том, что twgl.getContext возвращает WebGL2 в Chrome / Firefox, но только WebGL1 в Safari (который пока не поддерживает WebGL2)

Я добавил этот webgl-помощник для отключения WebGL2, и я получаю ту же ошибку в Chrome.

Если вы просто хотите, чтобы WebGL1 всегда, тогда не используйте twgl.getContext .

В противном случае вы можете исправить это, изменив строку для twgl.createFramebufferInfo , чтобы установить параметры, совместимые с WebGL1

   scnBf = twgl.createFramebufferInfo(glCtx, [{ }],dmnsn[0],dmnsn[1]);
  

IIRC значения по умолчанию в twgl для createFramebufferInfo являются wrap: CLAMP_TO_EDGE , minMag: LINEAR

 const dmnsn = [800, 600],
  glCtx = twgl.getContext(document.createElement("canvas")),
  imgLc =
    "https://upload.wikimedia.org/wikipedia/commons/b/b6/Microsoft_logo_(1987).svg";
var bfInf = twgl.primitives.createXYQuadBufferInfo(glCtx),
  pgInf = twgl.createProgramInfo(glCtx, ["vs", "fs"]),
  imgTx = twgl.createTexture(glCtx, { src: imgLc }, tstSB),
  scnBf = twgl.createFramebufferInfo(glCtx, [{ }],dmnsn[0],dmnsn[1]);

function plcCv() {
  document.body.append(glCtx.canvas);
  glCtx.canvas.width = dmnsn[0];
  glCtx.canvas.height = dmnsn[1];
}
function drwTx(tx, fbi, fy) {
  twgl.bindFramebufferInfo(glCtx, fbi);
  twgl.drawObjectList(glCtx, [
    {
      programInfo: pgInf,
      bufferInfo: bfInf,
      uniforms: { u_texture: tx, u_flipy: fy }
    }
  ]);
}
function tstSB() {
  plcCv();
  drwTx(imgTx, scnBf, true);
  drwTx(scnBf.attachments[0], null, false);
}  
 html{
  height:100%;
}
body{
  margin:0;
  height:100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
canvas{
  border-style:solid;
}  
 <script id="vs" type="x-shader/x-vertex">
  
  attribute vec4 position;
  attribute vec2 texcoord;
    
  uniform bool u_flipy;
  
  varying vec2 v_texcoord;

  void main() {
    if(u_flipy) {
      v_texcoord = vec2(texcoord.x, 1.0 - texcoord.y);
    } else {
      v_texcoord = texcoord;
    }
    gl_Position = position;
  }
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D u_texture;

void main() {
  gl_FragColor=texture2D(u_texture,v_texcoord);
}
</script>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>  

Комментарии:

1. Большое спасибо за ответ! Теперь я вспоминаю, что я уже знал, что twgl.getContext по умолчанию используется WebGL2, но я как-то забыл! Я думаю, поскольку я работал с кодом WebGL, я предположил, что контекст также должен быть WebGL, но контекст WebGL2, конечно, может работать с кодом WebGL!