javascript - WebGL Stencils,如何使用 2D 精灵的透明度作为蒙版?
问题描述
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);
}
解决方案
目前尚不清楚您为什么要为此使用模板。通常你只需设置混合并使用透明度来混合。
如果您真的想使用模板,则需要制作一个着色器,discard
如果透明度(alpha)小于某个值,则调用该着色器,以便仅在精灵不透明的情况下设置模板
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 个句点。
我的意思不是说“不要使用模板”,而是说有时它是最好的,有时不是。只有您可以知道您的情况选择哪种解决方案。
推荐阅读
- node.js - 如何从节点从谷歌驱动器下载文件并将进度流发送到客户端?
- json - 未处理的异常:类型 '_InternalLinkedHashMap
' 不是类型 'String' 的子类型 - python - curl工作时无法通过Windows 10上的python连接到本地主机服务器
- tensorflow - 如何通过字典为 Keras 或 Tensorflow 中的不平衡类设置类权重?
- python - 将dict转换为数据框
- sql - 将多个选择语句插入到临时表中
- python - YOLOv3-tiny 和 DarkNet - 2 类,但仅分类 1
- r - Shiny - 反应式过滤器功能问题
- dart - 使用intellij和dart,src下的目录不能正确识别dart文件
- c++ - (C ++)如何将当前减法减去前一个减法?