渲染到(或读取?)帧缓冲区在移动 Safari 中不起作用

Posted

技术标签:

【中文标题】渲染到(或读取?)帧缓冲区在移动 Safari 中不起作用【英文标题】:Rendering to (or reading from?) framebuffer doesn't work in mobile Safari 【发布时间】:2020-08-26 13:29:21 【问题描述】:

由于某种原因,渲染到帧缓冲区,然后从缓冲区到屏幕似乎不适用于移动 Safari。我没有机会查看桌面版 Safari,但我的 Firefox 很好地呈现了 Microsoft 徽标,在我的手机上我只看到了边框:

const dmnsn = [800, 600],
  glCtx = twgl.getContext(document.createElement("canvas")),
  imgLc =
    "https://upload.wikimedia.org/wikipedia/commons/b/b6/Microsoft_logo_%281987%29.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>

我知道绘制 SVG 有点实验性,但这似乎在移动 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) 中的错误:uniform sampler2D u_texture 引用的纹理单元 0 上的纹理 WebGLTexture("colorAttach") 不可渲染:纹理的宽度(800)不是 2 和高度的幂( 600) 不是 2 的幂,但 TEXTURE_WRAP_S (REPEAT) 不是 CLAMP_TO_EDGE 并且 TEXTURE_WRAP_T (REPEAT) 不是 CLAMP_TO_EDGE。 使用 WebGLProgram("unnamed") 作为当前程序 使用默认的顶点数组绑定

问题是 twgl.getContext 在 Chrome/Firefox 上返回 WebGL2,但在 Safari 上仅返回 WebGL1(尚不支持 WebGL2)

我添加了这个 webgl-helper 来禁用 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_%281987%29.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>

【讨论】:

非常感谢您的回答!我现在记得我已经知道 twgl.getContext 默认为 WebGL2,但不知何故我忘记了!我想,因为我使用的是 WebGL 代码,所以我假设上下文也必须是 WebGL,但是 WebGL2 上下文当然可以处理 WebGL 代码!

以上是关于渲染到(或读取?)帧缓冲区在移动 Safari 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

iOS 离屏渲染

读取 WebGLTexture 中的像素(将 WebGL 渲染到纹理)

从一个帧缓冲区读取到另一个帧缓冲区时出现问题

OpenGL:读取颜色缓冲区

从帧缓冲区对象读取时,OpenGL 纹理坐标是相反的

图形 - 多重缓冲:排队或最后完成?