当前位置: 首页 > Web前端 > HTML5

JavaScriptWebGLFrameBufferObject

时间:2023-04-05 00:24:46 HTML5

介绍看HowIbuiltawindmapwithWebGL的时候,里面用到了framebuffer,于是查了资料,自己试了一下。来源我的GitHub帧缓冲对象WebGL具有将渲染结果作为纹理使用的能力,这就是帧缓冲对象(framebufferobject)。默认情况下,WebGL最终的绘制结果保存在颜色缓冲区中,可以使用帧缓冲区对象代替颜色缓冲区,如下图所示,帧缓冲区中绘制的对象并没有直接显示在画布,所以这种技术也被称为离屏绘图。示例为了验证以上功能,本示例将在帧缓冲区中绘制一张图片,然后再次绘制为纹理进行显示。基于使用图片示例的逻辑,主要有以下几个方面的变化:dataframebuffer对象的绘制数据和framebuffer中正常绘制一样,只是不显示,所以必须有相应的绘图区域大小、顶点坐标和纹理坐标。offscreenWidth:200,//离屏绘制的宽度offscreenHeight:150,//离屏绘制的高度//部分代码省略//为帧缓冲区绘制的顶点和纹理坐标this.offScreenBuffer=this.initBuffersForFramebuffer(gl);//部分代码省略initBuffersForFramebuffer:function(gl){constvertices=newFloat32Array([0.5,0.5,-0.5,0.5,-0.5,-0.5,0.5,-0.5,]);//矩形常量索引=newUint16Array([0,1,2,0,2,3]);consttexCoords=newFloat32Array([1.0,1.0,//右上角0.0,1.0,//左上角0.0,0.0,//左下角1.0,0.0,//右下角]);常量对象={};obj.verticesBuffer=this.createBuffer(gl,gl.ARRAY_BUFFER,vertices);obj.indexBuffer=this.createBuffer(gl,gl.ELEMENT_ARRAY_BUFFER,索引);对象。texCoordsBuffer=this.createBuffer(gl,gl.ARRAY_BUFFER,texCoords);返回对象;},createBuffer:function(gl,type,data){constbuffer=gl.createBuffer();gl.bindBuffer(类型,缓冲区);gl.bufferData(类型,数据,gl.STATIC_DRAW);gl.bindBuffer(类型,空);返回缓冲区;}//部分代码省略了vertexshader和fragmentshader可以新定义,这里为了方便共享一组framebuffer对象,想在framebuffer中绘制,需要创建相应的framebuffer对象。//帧缓冲对象this.framebufferObj=this.createFramebufferObject(gl);//部分代码省略createFramebufferObject:function(gl){letframebuffer=gl.createFramebuffer();让纹理=gl.createTexture();gl.bindTexture(gl.TEXTURE_2D,纹理);gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,this.offscreenWidth,this.offscreenHeight,0,gl.RGBA,gl.UNSIGNED_BYTE,null);//反转图像Y轴方向gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,true);//纹理坐标水平填充gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);//纹理坐标垂直填充tgl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);//纹理放大处理gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);//纹理缩小处理gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);framebuffer.texture=纹理;//保存纹理对象//关联缓冲对象gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer);gl.帧缓冲Texture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,纹理,0);//检查配置是否正确vare=gl.checkFramebufferStatus(gl.FRAMEBUFFER);如果(gl.FRAMEBUFFER_COMPLETE!==e){控制台。log("帧缓冲对象不完整:"+e.toString());返回;}gl.bindFramebuffer(gl.FRAMEBUFFER,null);gl.bindTexture(gl.TEXTURE_2D,null);返回帧缓冲区;}//部分代码省略了创建framebuffer对象的createFramebuffer函数,删除对象的函数为deleteFramebuffer,创建后需要为framebuffer的color-associated对象指定一个texture对象。实例创建的纹理对象有几个特点:1.纹理的宽高绘图区域的宽高相同;2、使用texImage2D时,最后一个参数为null,即预留一块空白区域存放纹理对象;3.将创建好的纹理对象放在framebuffer对象上,也就是这行代码framebuffer。纹理=纹理。bindFramebuffer函数将framebuffer绑定到target,然后使用framebufferTexture2D将之前创建的纹理对象绑定到framebuffer的颜色关联对象gl.COLOR_ATTACHMENT0。checkFramebufferStatus检查帧缓冲区对象是否配置正确。绘图时的主要区别是有一个切换过程://省略了一些代码draw:function(){constgl=this.gl;constframeBuffer=this.framebufferObj;这个.canvasObj.clear();const程序=这个。着色器程序;gl.useProgram(program.program);//这使得绘图目标成为帧缓冲区gl.bindFramebuffer(gl.FRAMEBUFFER,frameBuffer);gl.viewport(0,0,this.offscreenWidth,this.offscreenHeight);this.drawOffFrame(程序,this.imgTexture);//取消绑定帧缓冲区,绘制目标变为颜色缓冲区gl.bindFramebuffer(gl.FRAMEBUFFER,null);gl.viewport(0,0,gl.canvas.width,gl.canvas.height);this.drawScreen(程序,frameBuffer.texture);},//部分代码省略,先使用bindFramebuffer将绘制的target做成framebuffer,需要指定对应的view口。绘制帧缓冲区后,解除绑定并返回到正常的默认颜色缓冲区。还需要指定对应的viewport,比较特殊的是使用buffer对象的texture,表示是从framebuffer中获取的。绘制结果。观察和思考网上找到的相关例子,感觉比较复杂。在尝试简化的过程中,有以下几点观察和思考。framebuffer.texture是已有的属性还是人为添加的?创建framebuffer对象时有这样的逻辑:framebuffer.texture=texture,那么framebuffer对象本身有texture属性吗?打印日志发现这个属性在刚创建的时候是不存在的,所以推测应该是人为添加的。framebuffer.texture什么时候有内容?framebuffer对象初始化的时候,存储的texture是空白的,但是从最终的结果来看,framebuffer绘制完成后,texture是有内容的,那么framebuffer.texture属性什么时候有内容呢?在绘制逻辑中,纹理相关的语句为:gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,纹理);gl.uniform1i(program.uSampler,0);gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0);推测gl.drawElements方法的绘制结果保存在帧缓冲区的颜色关联对象中,而帧缓冲区的颜色关联对象与初始化时创建的空白纹理对象,以及framebuffer相关联。texture指向的是同一个空白纹理对象,所以你最终得到了内容。为什么最终显示没有填满整个画布?最后绘制可显示内容的时候,可以发现顶点对应的是整个画布,纹理坐标对应的是整个完整的纹理,但是为什么没有覆盖整个画布呢?最终绘制可显示内容时使用的纹理来自帧缓冲区的绘制结果,帧缓冲区的顶点对应整个缓冲区的一半。如果把整个framebuffer的绘制结果看成一张贴图,可视区域按照最终绘制的比例缩放绘制,那么最终绘制不满,就是预期的正确结果。这是一个填充画布的例子,只需调整缓冲区顶点以对应整个缓冲区大小。参考WebGLProgrammingGuideFramebufferExampleWebGLFramebuffersWebGLdisplayframebuffer?