WebGL Droste效应

WebGL Droste效应,webgl,Webgl,我正在尝试在立方体的面上使用WebGL实现。视口中只有一个网格,即立方体,其所有面共享相同的纹理。为了实现Droste效果,我更新了每个帧上的纹理,实际上我只是拍摄了一张画布的快照,我正在绘制其WebGL上下文,随着时间的推移,随着快照越来越多地包含越来越多的嵌套过去的帧,这会导致Droste效果 这里有一个我现在正在使用的演示: 有关守则如下: // Set up fragment and vertex shader and attach them to a program, link the

我正在尝试在立方体的面上使用WebGL实现。视口中只有一个网格,即立方体,其所有面共享相同的纹理。为了实现Droste效果,我更新了每个帧上的纹理,实际上我只是拍摄了一张画布的快照,我正在绘制其WebGL上下文,随着时间的推移,随着快照越来越多地包含越来越多的嵌套过去的帧,这会导致Droste效果

这里有一个我现在正在使用的演示:

有关守则如下:

// Set up fragment and vertex shader and attach them to a program, link the program
// Create a vertex buffer, an index buffer and a texture coordinate buffer
// Tesselate the cube's vertices and fill in the index and texture coordinate buffers
const textureCanvas = document.createElement('canvas');
textureCanvas.width = 256;
textureCanvas.height = 256;
const textureContext = textureCanvas.getContext('2d');

// In every `requestAnimationFrame`:
textureContext.drawImage(context.canvas, 0, 0);
const texture = context.createTexture();
context.bindTexture(context.TEXTURE_2D, texture);
context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, context.RGBA, context.UNSIGNED_BYTE, textureCanvas);
context.generateMipmap(context.TEXTURE_2D);
// Clear the viewport completely (depth and color buffers)
// Set up attribute and uniform values, the projection and model view matrices
context.activeTexture(context.TEXTURE0);
context.bindTexture(context.TEXTURE_2D, texture);
context.uniform1i(fragmentShaderTextureSamplerUniformLocation, 0);
context.drawElements(context.TRIANGLES, 36, context.UNSIGNED_SHORT, 0)
以上就是其中的精华,有一个独立于WebGL的画布,它在每个WebGL帧之前在其上绘制WebGL画布,然后使用该画布为给定帧创建纹理,并根据提供给片段着色器的纹理坐标缓冲区和纹理采样器均匀地将纹理应用于立方体的面,片段着色器刚刚完成使用gl_FragColor=texture2DtextureSampler,与您预期的一样使用textureCoordinate

但在这个简单的演示中,这是超慢的30 FPS,只有一个立方体网格,而我的其他演示中,一些具有一个数量级以上的tris仍然在60 FPS requestAnimationFrame上限边缘

另外,在WebGL之外使用外部画布来实现这一点感觉很奇怪,而我觉得单独使用WebGL应该可以实现这一点

我知道WebGL保留了两个缓冲区,一个用于活动帧,一个用于最近绘制的帧,这两个缓冲区与每个帧交换,以实现即时屏幕更新。是否可以点击此后缓冲区并将其用作纹理?您能提供如何实现的示例代码吗?

来自

通常的方法是通过将纹理附加到帧缓冲区来渲染纹理

const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(
    gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0 /* level */) 
现在渲染到纹理

gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.viewport(0, 0, textureWidth, textureHeight);
渲染到画布上

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
要做你想做的事情,你需要2个纹理,因为你不能在同一时间读写同一个纹理,所以你可以画画

将图像绘制到纹理 将上一帧纹理B绘制到纹理A 使用TextureA到TextureB绘制立方体 在画布上绘制纹理B 严格使用; 主要功能{ 常数m4=twgl.m4; const gl=document.querySelector'canvas'.getContext'webgl' 常数vs=` 属性向量4位置; 属性向量2 texcoord; 均匀mat4u_矩阵; 可变矢量2 v_texcoord; 真空总管{ gl_位置=u_矩阵*位置; v_texcoord=texcoord; } `; 常数fs=` 精密中泵浮子; 可变矢量2 v_texcoord; 均匀取样器2D u_tex; 真空总管{ gl_FragColor=纹理2 du_tex,v_texcoord; } `; //编译着色器、链接程序、查找位置 const programInfo=twgl.createProgramInfogl[vs,fs]; //gl.createBuffer,gl.bufferData用于多维数据集的位置和坐标 const cubeBufferInfo=twgl.primitives.createCubeBufferInfogl,1; //gl.createBuffer、gl.bufferData用于四元组的位置和texcoords const quadBufferInfo=twgl.primitives.createXYQuadBufferInfogl,2; //所有用于设置纹理的正常内容 常量imageTexture=twgl.createTexturegl{ src:'https://i.imgur.com/ZKMnXce.png', }; 函数makeFramebufferAndTexturegl,宽度,高度{ const framebuffer=gl.createFramebuffer; gl.bindframebuffer gl.FRAMEBUFFER,FRAMEBUFFER; 常量纹理=gl.createTexture; gl.bindTexturegl.TEXTURE_2D,纹理; gl.texImage2Dgl.TEXTURE_2D, 0,//级别 gl.RGBA,//内部格式 宽度 身高 0,//边框 gl.RGBA,//格式 gl.UNSIGNED_字节,//类型 null,//数据不需要任何数据 ; gl.texParameterigl.TEXTURE_2D,gl.TEXTURE_MIN_过滤器,gl.LINEAR; gl.framebufferTexture2D gl.FRAMEBUFFER,gl.COLOR\u附件0, gl.TEXTURE_2D,纹理,0/*级别*/; //注意:根据所渲染的内容,您可能需要进行atttach //深度渲染缓冲区或深度纹理。请参阅链接文章 返回{ 帧缓冲区, 纹理 宽度 身高 }; } 函数bindFramebufferAndSetViewportgl,fbi{ gl.bindframebuffer gl.FRAMEBUFFER,fbi?fbi.FRAMEBUFFER:空; const{width,height}=fbi | | gl.canvas; gl.viewport0,0,宽度,高度; } 设fbiA=makeFramebufferAndTexturegl,512; 设fbiB=makeFramebufferAndTexturegl,512; 函数drawImageAndPreviousFrameToTextureB{ bindFramebufferAndSetViewportgl,fbiB; //调用gl.bindBuffer、gl.enableVertexAttributeArray、gl.VertexAttributePointer //对于每个属性 twgl.setBuffersAndAttributesgl、programInfo、quadBufferInfo; //调用gl.activeTexture、gl.bindTexture、gl.uniform twgl.setUniformsprogramInfo{ u_tex:imageTexture, u_矩阵:m4.identity, }; //调用gl.DrawArray或gl.drawElements twgl.drawBufferInfo,quadBufferInfo; // ----- //将上一个立方体纹理绘制到当前立方体纹理中 { twgl.set UniformsprogramInfo{ u_tex:fbiA.texture, u_矩阵:m4.缩放[0.8,0.8,1], }; twgl.drawBufferInfo,quadBufferInfo; } } 函数DrawTexturedCubeToTextureTime{ // ----- //使用立方体上的srcFB.texture将立方体绘制到新dstFB bindFramebufferAndSetViewportgl,fbiA; gl.cleargl.COLOR\u缓冲位; twgl.setBuffers和AttributesGL、programInfo、cubeBufferInfo; { 常数fov=60*Math.PI/180; 常量纵横比=fbiA.width/fbiA.height; 常数近=0.1; 常数far=100; 设mat=m4。透视视野、方位、近、远; mat=m4.translatemat[0,0,-2]; mat=m4.rotateXmat,时间; mat=m4.0转mat,时间*0.7; twgl.setUniformsprogramInfo{ u_tex:fbiB.texture, u_矩阵:mat, }; } twgl.drawBufferInfogl,cubeBufferInfo; } 函数drawTextureAToCanvas{ // ---- //将dstFB.texture绘制到画布 bindFramebufferAndSetViewportgl,null; twgl.setBuffersAndAttributesgl、programInfo、quadBufferInfo; { const aspect=gl.canvas.clientWidth/gl.canvas.clientHeight; 常数near=-1; 常数far=1; 设mat=m4。正交纵横比,纵横比,-1,1,近,远; twgl.setUniformsprogramInfo{ u_tex:fbiA.texture, u_矩阵:mat, }; } twgl.drawBufferInfo,quadBufferInfo; } 函数渲染时间{ 时间*=0.001;//转换为秒; twgl.resizeCanvasToDisplaySizegl.canvas; gl.enablegl.DEPTH_测试; gl.enablegl.CULL_面; //只有一个着色器程序,所以让我们在这里设置它 gl.useprograminfo.program; drawImageAndPreviousFrameToTextureB; DrawTexturedCubeToTextureTime; 拉丝纹理; requestAnimationFramerender; } requestAnimationFramerender; } 主要的 正文{页边距:0;} 画布{宽度:100vw;高度:100vh;显示:块;} 看见