[英]WebGL Stencils, How to use a 2D sprite's transparency as a mask?
if (statuseffect) {
// Clearing the stencil buffer
gl.clearStencil(0);
gl.clear(gl.STENCIL_BUFFER_BIT);
gl.stencilFunc(gl.ALWAYS, 1, 1);
gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);
gl.colorMask(false, false, false, false);
gl.enable(gl.STENCIL_TEST);
// Renders the mask through gl.drawArrays L111
drawImage(statuseffectmask.texture, lerp(-725, 675, this.Transtion_Value), 280, 128 * 4, 32 * 4)
// Telling the stencil now to draw/keep only pixels that equals 1 - which we set earlier
gl.stencilFunc(gl.EQUAL, 1, 1);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
// enabling back the color buffer
gl.colorMask(true, true, true, true);
drawImage(statuseffect.texture, lerp(-725, 675, this.Transtion_Value), 280, 128 * 4, 32 * 4)
gl.disable(gl.STENCIL_TEST);
}
不清楚為什么要為此使用模板。 通常,您只需設置混合並使用透明度來混合。
如果您真的想使用模板,則需要制作一個着色器,如果透明度 (alpha) 小於某個值,則調用discard
,以便僅在精靈不透明的地方設置模板
precision highp float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
uniform float u_alphaTest;
void main() {
vec4 color = texture2D(u_texture, v_texcoord);
if (color.a < u_alphaTest) {
discard; // don't draw this pixel
}
gl_FragColor = color;
}
但問題是已經可以在不使用模板的情況下透明地繪制紋理。
const m4 = twgl.m4; const gl = document.querySelector('canvas').getContext('webgl'); const vs = ` attribute vec4 position; attribute vec2 texcoord; uniform mat4 u_matrix; varying vec2 v_texcoord; void main() { gl_Position = u_matrix * position; v_texcoord = texcoord; } `; const fs = ` precision highp float; varying vec2 v_texcoord; uniform sampler2D u_texture; uniform float u_alphaTest; void main() { vec4 color = texture2D(u_texture, v_texcoord); if (color.a < u_alphaTest) { discard; // don't draw this pixel } gl_FragColor = color; } `; // compile shaders, link program, look up locations const programInfo = twgl.createProgramInfo(gl, [vs, fs]); // make buffers for positions and texcoords for a plane // calls gl.createBuffer, gl.bindBuffer, gl.bufferData const bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl); const texture = makeSpriteTexture(gl, '🌲', 128); function render(time) { time *= 0.001; // convert to seconds gl.useProgram(programInfo.program); // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); const mat = m4.ortho(-150, 150, 75, -75, -1, 1); m4.translate(mat, [Math.cos(time) * 20, Math.sin(time) * 20, 0], mat); m4.scale(mat, [64, 64, 1], mat); // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX twgl.setUniformsAndBindTextures(programInfo, { u_texture: texture, u_alphaTest: 0.5, u_matrix: mat, }); // calls gl.drawArrays or gl.drawElements twgl.drawBufferInfo(gl, bufferInfo); requestAnimationFrame(render); } requestAnimationFrame(render); // just so we don't have to download an image // we'll make our own using a canvas and the 2D API function makeSpriteTexture(gl, str, size) { const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); ctx.font = `${size * 3 / 4}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(str, size / 2, size / 2); const tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D( gl.TEXTURE_2D, 0, // mip level gl.RGBA, // internal format gl.RGBA, // format gl.UNSIGNED_BYTE, // type, canvas); // let's assume we used power of 2 dimensions gl.generateMipmap(gl.TEXTURE_2D); return tex; }
canvas { background: url(https://i.imgur.com/v38pV.jpg) no-repeat center center; background-size: cover; }
<canvas></canvas> <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
否則,如果您現在真的想使用模板,因為代碼丟棄了一些像素,它應該可以工作並且您的代碼是正確的。 請注意下面的代碼不會清除模板,因為它默認為每幀清除
const m4 = twgl.m4; const gl = document.querySelector('canvas').getContext('webgl', {stencil: true}); const vs = ` attribute vec4 position; attribute vec2 texcoord; uniform mat4 u_matrix; varying vec2 v_texcoord; void main() { gl_Position = u_matrix * position; v_texcoord = texcoord; } `; const fs = ` precision highp float; varying vec2 v_texcoord; uniform sampler2D u_texture; uniform float u_alphaTest; void main() { vec4 color = texture2D(u_texture, v_texcoord); if (color.a < u_alphaTest) { discard; // don't draw this pixel } gl_FragColor = color; } `; // compile shaders, link program, look up locations const programInfo = twgl.createProgramInfo(gl, [vs, fs]); // make buffers for positions and texcoords for a plane // calls gl.createBuffer, gl.bindBuffer, gl.bufferData const bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl); const starTexture = makeSpriteTexture(gl, '✱', 128); const bugTexture = makeSpriteTexture(gl, '🐞', 128); function render(time) { time *= 0.001; // convert to seconds gl.useProgram(programInfo.program); // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); const mat = m4.ortho(-150, 150, 75, -75, -1, 1); m4.translate(mat, [Math.cos(time) * 20, 0, 0], mat); m4.scale(mat, [64, 64, 1], mat); gl.enable(gl.STENCIL_TEST); // set the stencil to 1 everwhere we draw a pixel gl.stencilFunc(gl.ALWAYS, 1, 0xFF); gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE); // don't render pixels gl.colorMask(false, false, false, false); // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX twgl.setUniformsAndBindTextures(programInfo, { u_texture: starTexture, u_alphaTest: 0.5, u_matrix: mat, }); // calls gl.drawArrays or gl.drawElements twgl.drawBufferInfo(gl, bufferInfo); // only draw if the stencil = 1 gl.stencilFunc(gl.EQUAL, 1, 0xFF); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); // render pixels gl.colorMask(true, true, true, true); m4.ortho(-150, 150, 75, -75, -1, 1, mat); m4.translate(mat, [0, Math.cos(time * 1.1) * 20, 0], mat); m4.scale(mat, [64, 64, 1], mat); // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX twgl.setUniformsAndBindTextures(programInfo, { u_texture: bugTexture, u_alphaTest: 0, // draw all pixels (but stencil will prevent some) u_matrix: mat, }); // calls gl.drawArrays or gl.drawElements twgl.drawBufferInfo(gl, bufferInfo); requestAnimationFrame(render); } requestAnimationFrame(render); // just so we don't have to download an image // we'll make our own using a canvas and the 2D API function makeSpriteTexture(gl, str, size) { const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); ctx.font = `${size * 3 / 4}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(str, size / 2, size / 2); const tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D( gl.TEXTURE_2D, 0, // mip level gl.RGBA, // internal format gl.RGBA, // format gl.UNSIGNED_BYTE, // type, canvas); // let's assume we used power of 2 dimensions gl.generateMipmap(gl.TEXTURE_2D); return tex; }
canvas { background: url(https://i.imgur.com/v38pV.jpg) no-repeat center center; background-size: cover; }
<canvas></canvas> <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
我還要指出,這也可能更好地使用 alpha 混合來完成,將兩個紋理傳遞到單個着色器並傳入另一個矩陣或其他統一以應用一個紋理的 alpha 或另一個。 這會更靈活,因為您可以混合 0 到 1 的所有值,而與模板一樣,您只能屏蔽 0 或 1 個周期。
我的觀點不是說“不要使用模板”,而是說有時最好也有時不合適。 只有您才能知道針對您的情況選擇哪種解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.