Webgl-渲染深度到纹理在Safari中返回gl.INVALID\u FRAMEBUFFER\u操作

Webgl-渲染深度到纹理在Safari中返回gl.INVALID\u FRAMEBUFFER\u操作,webgl,framebuffer,depth-buffer,render-to-texture,shadow-mapping,Webgl,Framebuffer,Depth Buffer,Render To Texture,Shadow Mapping,如果查看Safari中取自的以下代码段,您会注意到它与其他浏览器中的代码段不同,因为深度纹理始终为完全黑色: "严格使用",; 主要功能{ //获取WebGL上下文 /**@type{HTMLCanvasElement}*/ const canvas=document.getElementById'canvas'; const gl=canvas.getContext'webgl'; 如果!德国劳埃德船级社{ 回来 } const ext=gl.getExtension'WEBGL_depth

如果查看Safari中取自的以下代码段,您会注意到它与其他浏览器中的代码段不同,因为深度纹理始终为完全黑色:

"严格使用",; 主要功能{ //获取WebGL上下文 /**@type{HTMLCanvasElement}*/ const canvas=document.getElementById'canvas'; const gl=canvas.getContext'webgl'; 如果!德国劳埃德船级社{ 回来 } const ext=gl.getExtension'WEBGL_depth_texture'; 如果!分机{ 返回警报“需要WEBGL_深度_纹理”;//eslint禁用行 } //设置GLSL程序 const textureProgramInfo=webglUtils.createProgramInfogl,['3d-vertex-shader','3d fragment shader']; const colorProgramInfo=webglUtils.createProgramInfogl,['color-vertex-shader','color fragment shader']; const sphereBufferInfo=基本体。createSphereBufferInfo 德国劳埃德船级社, 1,//半径 32,//周围的细分 24,//向下细分 ; const planeBufferInfo=primitives.createPlaneBufferInfo 德国劳埃德船级社, 20,//宽度 20,//高度 1,//交叉的细分 1,//向下细分 ; const cubeBufferInfo=primitives.createCubeBufferInfo 德国劳埃德船级社, 2,//大小 ; const cubeLinesBufferInfo=webglUtils.createbufferinfofromrarraysgl{ 职位:[ -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, ], 指数:[ 0, 1, 1, 3, 3, 2, 2, 0, 4, 5, 5, 7, 7, 6, 6, 4, 0, 4, 1, 5, 3, 7, 2, 6, ], }; //制作8x8棋盘格纹理 常量棋盘纹理=gl.createTexture; gl.bindTexturegl.TEXTURE_2D,棋盘格纹理; gl.texImage2D gl.U 2D, 0,//mip级别 gl.LUMINANCE,//内部格式 8,//宽度 8,//高度 0,//边框 gl.LUMINANCE,//格式 gl.UNSIGNED_字节,//类型 新的Uint8Array[//数据 0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC, 0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF, 0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC, 0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF, 0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC, 0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF, 0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC, 0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF,0xCC,0xFF, ]; gl.GENERAEMIPMAPGL.TESTRISE_2D; gl.texParameterigl.TEXTURE_2D,gl.TEXTURE_MAG_过滤器,gl.NEAREST; const depthTexture=gl.createTexture; 常数depthTextureSize=512; gl.bindTexturegl.TEXTURE_2D,深度纹理; gl.texImage2D gl.TEXTURE_2D,//目标 0,//mip级别 gl.DEPTH\u组件,//内部格式 深度纹理化,//宽度 depthTextureSize,//高度 0,//边框 gl.DEPTH\u组件,//格式 gl.UNSIGNED_INT,//类型 null;//数据 gl.texParameterigl.TEXTURE_2D,gl.TEXTURE_MAG_过滤器,gl.NEAREST; gl.texParameterigl.TEXTURE_2D,gl.TEXTURE_MIN_过滤器,gl.NEAREST; gl.texParameterigl.TEXTURE_2D、gl.TEXTURE_WRAP_S、gl.CLAMP_TO_EDGE; gl.texParameterigl.TEXTURE_2D、gl.TEXTURE_WRAP_T、gl.CLAMP_至边; const depthFramebuffer=gl.createFramebuffer; gl.bindFramebuffergl.FRAMEBUFFER,depthFramebuffer; gl.framebufferTexture2D gl.FRAMEBUFFER,//目标 gl.DEPTH\u附件,//附件点 gl.TEXTURE_2D,//纹理目标 深度纹理,//纹理 0;//mip级别 函数degToRadd{ 返回d*Math.PI/180; } 常量设置={ 摄影机:6, 摄像:5, posX:2.5, 波西:4.8, posZ:4.3, targetX:2.5, 目标:0, targetZ:3.5, 项目宽度:1, 项目八:1, 观点:没错, 视野:120, }; webglLessonsUI.setupUIdocument.querySelector'ui',settings[ {类型:'slider',键:'cameraX',最小值:-10,最大值:10,更改:渲染,精度:2,步长:0.001,}, {类型:'slider',键:'cameraY',最小值:1,最大值:20,更改:渲染,精度:2,步长:0.001,}, {类型:'slider',键:'posX',min:-10,max:10,更改:render,精度:2,步长:0.001,}, {类型:'slider',键:'posY',最小值:1,最大值:20,更改:render,精度:2,步长:0.001,}, {类型:'slider',键:'posZ',最小值:1,最大值:20,更改:render,精度:2,步长:0.001,}, {类型:'slider',键:'targetX',最小值:-10,最大值:10,更改:渲染,精度:2,步长:0.001,}, {type:'slider',key:'targetY',min:0,max:20,change:render,precision:2,step:0.001,}, {类型:'slider',键:'targetZ',最小值:-10,最大值:20,更改:渲染,精度:2,步长:0.001, }, {类型:'slider',键:'projWidth',最小值:0,最大值:2,更改:渲染,精度:2,步长:0.001,}, {type:'slider',key:'projHeight',min:0,max:2,change:render,precision:2,step:0.001,}, {键入:'checkbox',键:'perspective',更改:render,}, {键入:'slider',键:'fieldOfView',最小值:1,最大值:179,更改:render,}, ]; 常量视野弧度=degToRad60; //每个物体的制服。 警察制服={ u_colorMult:[0.5,0.5,1,1],//浅蓝色 u_颜色:[1,0,0,1], u_纹理:棋盘纹理, u_世界:m4.translation0,0,0, }; 常数球形={ u_colorMult:[1,0.5,0.5,1],//粉红色 u_颜色:[0,0,1,1], u_纹理:棋盘纹理, u_world:m4.translation2,3,4, }; 常数立方形={ u_colorMult:[0.5,1,0.5,1],//浅绿色 u_颜色:[0,0,1,1], u_纹理:棋盘纹理, u_世界:m4.translation3,1,0, }; 函数DrawSceneProjectMatrix、cameraMatrix、textureMatrix、programInfo{ //从摄影机矩阵创建视图矩阵。 常量viewMatrix=m4.inversecameraMatrix; gl.useprograminfo.program; //为球体和平面设置相同的制服 //注意:着色器中没有相应一致性的任何值 //都被忽略了。 webglUtils.setuniformsprogramminfo{ u_视图:viewMatrix, u_投影:投影矩阵, u_textureMatrix:textureMatrix, u_投影纹理:深度纹理, }; //---画球体---- //设置所有需要的属性。 webglUtils.SetBuffers和AttributesGL、programInfo、sphereBufferInfo; //设置球体特有的制服 webglUtils.setUniformsprogramInfo,sphereUniforms; //调用gl.DrawArray或gl.drawElements webglUtils.drawBufferInfogl、sphereBufferInfo; //---画立方体---- //设置所有需要的属性。 webglUtils.SetBuffers和AttributesGL、programInfo、cubeBufferInfo; //为立方体设置唯一的制服 webglUtils.setUniformsprogramInfo,cubeUniforms; //调用gl.DrawArray或gl.drawElements webglUtils.drawBufferInfogl,cubeBufferInfo; //---画飞机---- //设置所有需要的属性。 webglUtils.SetBuffers和AttributesGL、programInfo、planeBufferInfo; //为立方体设置唯一的制服 webglUtils.setuniformsprogramminfo,planeUniforms; //调用gl.DrawArray或gl.drawElements webglUtils.drawBufferInfogl,planeBufferInfo; } //画场景。 函数渲染{ webglUtils.resizeCanvasToDisplaySizegl.canvas; gl.enablegl.CULL_面; gl.enablegl.DEPTH_测试; //首先从光的视角进行绘制 常量lightWorldMatrix=m4.lookAt [settings.posX,settings.posY,settings.posZ],//位置 [settings.targetX,settings.targetY,settings.targetZ],//目标 [0,1,0],//向上 ; const lightProjectionMatrix=settings.perspective ?m4.透视图 degToRadsettings.fieldOfView, settings.projWidth/settings.projHeight, 0.5,//接近 10/远 :m4.正交 -settings.projWidth/2,//左 settings.projWidth/2,//右 -settings.projHeight/2,//底部 settings.projHeight/2,//顶部 0.5,//接近 10、 //远 //绘制到深度纹理 gl.bindFramebuffergl.FRAMEBUFFER,depthFramebuffer; gl.viewport0,0,depthTextureSize,depthTextureSize; gl.cleargl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT; drawScenelightProjectionMatrix、lightWorldMatrix、m4.identity、colorProgramInfo; //现在将场景绘制到画布,将深度纹理投影到场景中 gl.bindframebuffer gl.FRAMEBUFFER,空; gl.viewport0,0,gl.canvas.width,gl.canvas.height; gl.cleargl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT; 设textureMatrix=m4.identity; 纹理矩阵=m4.translatetextureMatrix,0.5,0.5,0.5; textureMatrix=m4.scaletextureMatrix,0.5,0.5,0.5; textureMatrix=m4.multiplytextureMatrix,lightProjectionMatrix; //使用此世界矩阵的逆矩阵生成 //将变换其他位置的矩阵 //相对于这个世界空间。 纹理矩阵=m4.5倍 纹理矩阵, m4.逆ElightWorldMatrix; //计算投影矩阵 const aspect=gl.canvas.clientWidth/gl.canvas.clientHeight; 常量投影矩阵= m4.透视视野弧度,纵横比,2000年1月; //使用“查看”计算相机的矩阵。 常量cameraPosition=[settings.cameraX,settings.cameraY,7]; 常量目标=[0,0,0]; const up=[0,1,0]; const cameraMatrix=m4.lookAtcameraPosition,目标,向上; drawSceneprojectionMatrix、cameraMatrix、, textureMatrix,textureProgramInfo; //---画平截头体--- { 常量viewMatrix=m4.inversecameraMatrix; gl.useProgramcolorProgramInfo.program; //设置所有需要的属性。 webglUtils.SetBuffers和AttributesGL、colorProgramInfo、cubeLinesBufferInfo; //在Z轴上缩放立方体,使其非常长 //表示要投影到的纹理 //无穷大 常数mat=m4.5 lightWorldMatrix,m4.inverselightProjectionMatrix; //设置我们刚刚计算的制服 webglUtils.setuniformscolorprogramminfo{ u_颜色:[0,0,0,1], u_视图:viewMatrix, u_投影:投影矩阵, 世界:马特, }; //调用gl.DrawArray或gl.drawElements webglUtils.drawBufferInfogl、cubeLinesBufferInfo、gl.LINES; } } 提供 } 主要的 @进口urlhttps://webglfundamentals.org/webgl/resources/webgl-tutorials.css; 身体{ 保证金:0; } 帆布{ 宽度:100vw; 高度:100vh; 显示:块; } .gman小部件值{ 最小宽度:5em; } 属性向量4 a_位置; 属性向量2 a_texcoord; 均匀mat4u_投影; 统一的mat4 u_视图; 统一mat4 u_世界; 均匀mat4 u_纹理矩阵; 可变矢量2 v_texcoord; 不同的vec4 v_投射到船外; 真空总管{ //将位置乘以矩阵。 vec4世界位置=u_世界*a_位置; gl_位置=u_投影*u_视图*世界位置; //将纹理坐标传递给片段着色器。 v_texcoord=a_texcoord; v_projectedTexcoord=u_textureMatrix*世界位置; } 精密中泵浮子; //从顶点着色器传入。 可变矢量2 v_texcoord; 不同的vec4 v_投射到船外; 均匀vec4 u_色Mult; 均匀的二维u_纹理; 统一采样器2D u_项目结构; 真空总管{ vec3 projectedTexcoord=v_projectedTexcoord.xyz/v_projectedTexcoord.w; 布尔英朗= projectedTexcoord.x>=0.0&& ProjectedTexoord.x=0.0&& 突出的防御 属性向量4 a_位置; 均匀mat4u_投影; 统一的mat4 u_视图; 统一mat4 u_世界; 真空总管{ //将位置乘以矩阵。 gl_位置=u_投影*u_视图*u_世界*a_位置; } 精密中泵浮子; 均匀的vec4 u_颜色; 真空总管{ gl_FragColor=u_颜色; }
对于Safari,即使示例不使用颜色附件,也需要设置颜色附件

首先创建与深度纹理大小相同的RGBA/无符号_字节颜色附件

  const dummy = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, dummy);
  gl.texImage2D(
      gl.TEXTURE_2D,
      0,
      gl.RGBA,
      depthTextureSize,
      depthTextureSize,
      0,
      gl.RGBA,
      gl.UNSIGNED_BYTE,
      null,
  );
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
然后将其附加到帧缓冲区

  const depthFramebuffer = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, depthFramebuffer);

  gl.framebufferTexture2D(
      gl.FRAMEBUFFER,       // target
      gl.DEPTH_ATTACHMENT,  // attachment point
      gl.TEXTURE_2D,        // texture target
      depthTexture,         // texture
      0);                   // mip level

  // ---- added ----
  gl.framebufferTexture2D(
      gl.FRAMEBUFFER,
      gl.COLOR_ATTACHMENT0,
      gl.TEXTURE_2D,
      dummy,
      0,
  )
不幸的是,这并不是Safari中的一个bug。它反映了不幸的选择

4.4.5帧缓冲区完整性

帧缓冲区完整性

如果帧缓冲区对象目标是Windows系统提供的帧缓冲区,或者如果以下所有条件均为真,则称其为帧缓冲区已完成:

在本小节中,每个规则后面都有一个粗体的错误枚举

附加图像的内部格式组合不会违反依赖于实现的一组限制

不支持帧缓冲区

换句话说,在OpenGL ES 2.0中,无需帧缓冲区附件的组合即可工作

增加了需要3种组合才能工作的要求

6.8帧缓冲区对象附件

当所有附件都是帧缓冲区附件完整、非零且具有相同的宽度和高度时,帧缓冲区对象附件的以下组合必须导致帧缓冲区完整:

颜色\u附件0=RGBA/无符号\u字节纹理 颜色\附件0=RGBA/无符号\字节纹理+深度\附件=深度\组件16渲染缓冲 颜色\附件0=RGBA/无符号\字节纹理+深度\模具\附件=深度\模具渲染缓冲 因此,遵循这两个规范,即使WEBGL_depth_纹理扩展添加了深度纹理,它们中的任何一个都不需要在有或没有任何其他附件的帧缓冲区中用作附件

是的,你读对了。WEBGL_depth_texture extension规范说你可以创建并附加它们。OpenGL ES 2.0规范说它们不需要作为附件使用,或者说任何组合是否有效取决于实现。WEBGL列出了需要使用的3种组合,但这3种组合不包括取消深度纹理,仅深度渲染缓冲

幸运的是,如果您添加RGBA/无符号_字节颜色附件,它们似乎在任何地方都可以工作


一个积极的方面是,OpenGL ES 3.0和WebGL2列出了更多需要使用的组合。如果只有Safari支持WebGL2,而到2020年2月我们还在使用规范琐事时,它仍然不支持WebGL2,那么另一个不幸的OpenGL ES 2.0规范选择,根据规范,一个实现只能支持64像素的纹理拉尔
ess和仍然被认为是一个通过的实现。请注意,我知道没有WebGL实现的最小值小于2048,所以在现实中这很好,但规范中说64是所需的全部。幸运的是,OpenGL ES 3.0将该最小值提高到了2048。无论谁想出了这个,都应该被解雇!我甚至没有意识到拥有这些组合可能是一个要求。。。64像素的想法似乎也有点极端。如果你有这样低的纹理大小限制,我认为这是一个非常特殊的情况,你无论如何都不会关心合规性。同意。在他们的辩护中,OpenGLES2.0是在功能手机还很普及的时候设计的。