繁体   English   中英

使用 WebGL 支持模板缓冲区

[英]Support for Stencil Buffer with WebGL

Initializing webgl with canvas.getContext("webgl", {stencil: true}) requests a stencil buffer, but not all browsers will actually give you one (for me, Firefox 79.0 on Ubuntu 20.04 LTS doesn't works but Chrome 84.0.4147.89可以。我的显卡是 NVIDIA RTX 2060,我使用的是 nvidia-driver-440-server 驱动程序)。

我想知道受支持的模板缓冲区有多广泛,但我找不到有关支持哪些浏览器的信息。 glStencilOp这样的函数,这是我唯一能找到支持信息的东西,仍然可以使用,它们只是不使用 0 模板位做任何事情。

是否有支持此功能的浏览器列表?

老实说,这听起来像是 firefox 中的错误,尽管鉴于规范允许实现无法在 canvas 上提供模板缓冲区,无论出于何种原因,它在技术上都不是错误。 我会考虑填一个。 使用 Chromium 浏览器测试只是为了检查这是 Firefox 选择不提供模板缓冲区而不是驱动程序问题或其他问题。

您应该能够始终制作DEPTH_STENCIL渲染缓冲区。 没有任何版本的 WebGL 允许实现不支持它。 因此,您可以通过渲染到附加到帧缓冲区的纹理 + 深度模板渲染缓冲区,然后将帧缓冲区颜色纹理渲染到 canvas 来解决该错误。

这是一个测试。 你应该看到一个右下角为绿色的红色方块。 那将在一个紫色方块内的蓝色方块内。

蓝色方块是显示帧缓冲纹理的范围。 如果绿色方块没有被模板缓冲区掩盖,它会渗入蓝色。

紫色方块显示 canvas 的大小,我们正在绘制比完整 canvas 更小的帧缓冲区纹理。 这只是为了表明模板缓冲区在您的机器上工作。 对于您自己的解决方案,您希望绘制一个由顶点组成的四边形,而不是使用如下点,并且您希望使附加到帧缓冲区的纹理和渲染缓冲区与您的 canvas 大小相同。

 "use strict"; function main() { const gl = document.querySelector("canvas").getContext("webgl"); const vs = ` attribute vec4 position; void main() { gl_Position = position; gl_PointSize = 64.0; } `; const fs = ` precision mediump float; uniform sampler2D tex; void main() { gl_FragColor = texture2D(tex, gl_PointCoord.xy); } `; const program = twgl.createProgram(gl, [vs, fs]); const posLoc = gl.getAttribLocation(program, "position"); // Create a texture to render to const targetTextureWidth = 128; const targetTextureHeight = 128; const targetTexture = createTexture(gl); { // define size and format of level 0 const level = 0; const internalFormat = gl.RGBA; const border = 0; const format = gl.RGBA; const type = gl.UNSIGNED_BYTE; const data = null; gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, targetTextureWidth, targetTextureHeight, border, format, type, data); } // Create and bind the framebuffer const fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fb); // attach the texture as the first color attachment const attachmentPoint = gl.COLOR_ATTACHMENT0; const level = 0; gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level); // create a depth-stencil renderbuffer const depthStencilBuffer = gl.createRenderbuffer(); gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer); // make a depth-stencil buffer and the same size as the targetTexture gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, targetTextureWidth, targetTextureHeight); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer); function createTexture(gl, color) { const tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); // set the filtering so we don't need mips gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 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); if (color) { gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(color)); } return tex; } // create a red texture and a green texture const redTex = createTexture(gl, [255, 0, 0, 255]); const greenTex = createTexture(gl, [0, 255, 0, 255]); gl.enable(gl.STENCIL_TEST); gl.useProgram(program); gl.clearColor(0, 0, 1, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.bindTexture(gl.TEXTURE_2D, redTex); gl.stencilFunc( gl.ALWAYS, // the test 1, // reference value 0xFF, // mask ); gl.stencilOp( gl.KEEP, // what to do if the stencil test fails gl.KEEP, // what to do if the depth test fails gl.REPLACE, // what to do if both tests pass ); // draw a 64x64 pixel red rect in middle gl.drawArrays(gl.POINTS, 0, 1); gl.stencilFunc( gl.EQUAL, // the test 1, // reference value 0xFF, // mask ); gl.stencilOp( gl.KEEP, // what to do if the stencil test fails gl.KEEP, // what to do if the depth test fails gl.KEEP, // what to do if both tests pass ); // draw a green 64x64 pixel square in the // upper right corner. The stencil will make // it not go outside the red square gl.vertexAttrib2f(posLoc, 0.5, 0.5); gl.bindTexture(gl.TEXTURE_2D, greenTex); gl.drawArrays(gl.POINTS, 0, 1); // draw the framebuffer's texture to // the canvas. we should see a 32x32 // red square with the bottom right corner // green showing the stencil worked. That will // be surrounded by blue to show the texture // we were rendering to is larger than the // red square. And that will be surrounded // by purple since we're drawing a 64x64 // point on a 128x128 canvas which we clear // purple. gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(1, 0, 1, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.vertexAttrib2f(posLoc, 0.0, 0.0); gl.bindTexture(gl.TEXTURE_2D, targetTexture); gl.drawArrays(gl.POINTS, 0, 1); } main();
 canvas { border: 1px solid black; }
 <script src="https://twgljs.org/dist/4.x/twgl.min.js"></script> <canvas width="128" height="128"></canvas>

如果您将渲染缓冲区格式更改为 DEPTH_COMPONENT16 并将连接点更改为 DEPTH_ATTACHMENT,那么您将看到绿色方块不再被模板遮盖

 "use strict"; function main() { const gl = document.querySelector("canvas").getContext("webgl"); const vs = ` attribute vec4 position; void main() { gl_Position = position; gl_PointSize = 64.0; } `; const fs = ` precision mediump float; uniform sampler2D tex; void main() { gl_FragColor = texture2D(tex, gl_PointCoord.xy); } `; const program = twgl.createProgram(gl, [vs, fs]); const posLoc = gl.getAttribLocation(program, "position"); // Create a texture to render to const targetTextureWidth = 128; const targetTextureHeight = 128; const targetTexture = createTexture(gl); { // define size and format of level 0 const level = 0; const internalFormat = gl.RGBA; const border = 0; const format = gl.RGBA; const type = gl.UNSIGNED_BYTE; const data = null; gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, targetTextureWidth, targetTextureHeight, border, format, type, data); } // Create and bind the framebuffer const fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fb); // attach the texture as the first color attachment const attachmentPoint = gl.COLOR_ATTACHMENT0; const level = 0; gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level); // create a depth-stencil renderbuffer const depthStencilBuffer = gl.createRenderbuffer(); gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer); // make a depth-stencil buffer and the same size as the targetTexture gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, targetTextureWidth, targetTextureHeight); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer); function createTexture(gl, color) { const tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); // set the filtering so we don't need mips gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 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); if (color) { gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(color)); } return tex; } // create a red texture and a green texture const redTex = createTexture(gl, [255, 0, 0, 255]); const greenTex = createTexture(gl, [0, 255, 0, 255]); gl.enable(gl.STENCIL_TEST); gl.useProgram(program); gl.clearColor(0, 0, 1, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.bindTexture(gl.TEXTURE_2D, redTex); gl.stencilFunc( gl.ALWAYS, // the test 1, // reference value 0xFF, // mask ); gl.stencilOp( gl.KEEP, // what to do if the stencil test fails gl.KEEP, // what to do if the depth test fails gl.REPLACE, // what to do if both tests pass ); // draw a 64x64 pixel red rect in middle gl.drawArrays(gl.POINTS, 0, 1); gl.stencilFunc( gl.EQUAL, // the test 1, // reference value 0xFF, // mask ); gl.stencilOp( gl.KEEP, // what to do if the stencil test fails gl.KEEP, // what to do if the depth test fails gl.KEEP, // what to do if both tests pass ); // draw a green 64x64 pixel square in the // upper right corner. The stencil will make // it not go outside the red square gl.vertexAttrib2f(posLoc, 0.5, 0.5); gl.bindTexture(gl.TEXTURE_2D, greenTex); gl.drawArrays(gl.POINTS, 0, 1); // draw the framebuffer's texture to // the canvas. we should see a 32x32 // red square with the bottom right corner // green showing the stencil worked. That will // be surrounded by blue to show the texture // we were rendering to is larger than the // red square. And that will be surrounded // by purple since we're drawing a 64x64 // point on a 128x128 canvas which we clear // purple. gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(1, 0, 1, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.vertexAttrib2f(posLoc, 0.0, 0.0); gl.bindTexture(gl.TEXTURE_2D, targetTexture); gl.drawArrays(gl.POINTS, 0, 1); } main();
 canvas { border: 1px solid black; }
 <script src="https://twgljs.org/dist/4.x/twgl.min.js"></script> <canvas width="128" height="128"></canvas>

您应该能够调用gl.getContextAttributes来检查您是否有模板缓冲区,如果它告诉您在 canvas 上没有模板缓冲区,您可以使用建议的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM