OpenGL Stencil - 排除透明像素
OpenGL Stencil - Exclude transparent pixels
我有这个纹理,我想使用 Stencil 缓冲区将其用作蒙版:
然后我想在屏幕上绘制一个图像 IMG,它应该只出现在上面的图像可见的地方(有颜色的地方),因此不包括顶部边缘上方和渐变下方的透明像素。
问题是每当我在屏幕上绘制图像 IMG 时,无论像素是否透明,它都会出现在图像绘制的任何地方。
所以我考虑过使用 ALPHA_TEST
但它在最新的 OpenGL 版本中消失了,所以我试图在我的片段着色器中丢弃带有 v_color.a == 0
或 v_color.r == 0
的片段,但它确实如此没什么...
片段着色器:
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
uniform sampler2D u_texture;
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
void main() {
if (v_color.a == 0) {
discard;
}
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
}
顶点着色器:
attribute vec4 a_color;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = a_color;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
代码:
@Override
public void draw(final DrawConfig drawConfig) {
this.applyTransform(drawConfig.getBatch(), this.computeTransform());
// Draw mask to stencil buffer
drawConfig.end();
final GL20 gl = Gdx.gl;
gl.glEnable(GL20.GL_STENCIL_TEST);
gl.glStencilFunc(GL20.GL_ALWAYS, 1, 0xFF);
gl.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);
gl.glStencilMask(0xFF);
gl.glColorMask(false, false, false, false);
gl.glClearStencil(0);
gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
drawConfig.begin(Mode.BATCH);
this.mBackground.setClippingEnabled(true); // The clipping area is a rectangle centered in the screen
this.mBackground.draw(drawConfig);
this.mBottomBorder.draw(drawConfig); // Border below the rectangle area
this.mTopBorder.draw(drawConfig); // Border above the rectangle area
drawConfig.end();
gl.glColorMask(true, true, true, true);
gl.glStencilFunc(GL20.GL_EQUAL, 1, 0xFF);
gl.glStencilMask(0x00);
// Draw elements
this.mBackground.setClippingEnabled(false); // No clipping area, use stencil test
this.mBackground.draw(drawConfig);
this.mBottomBorder.draw(drawConfig);
this.mTopBorder.draw(drawConfig);
drawConfig.end();
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
this.resetTransform(drawConfig.getBatch());
}
结果:
红色箭头表示超出边界的部分,我想让它们消失。
黄色方块是裁剪区,方便大家使用。
您尝试复制的固定功能 alpha 测试 (GL_ALPHA_TEST
) 是针对每个片段的测试。它曾经在 片段着色器完成后 发生,您可以使用 discard
.
自己实现它
但是,您当前的解决方案是使用插值的每顶点 alpha 值,这与您尝试用作遮罩的纹理无关。您需要测试分配给 gl_FragColor
的 alpha 值才能正确执行此操作:
void main() {
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
if (gl_FragColor.a == 0.0) {
discard;
}
}
这是基于现代着色器的等价物:
glAlphaFunc (GL_NOTEQUAL, 0.0f); // Reject fragments with alpha == 0.0
glEnable (GL_ALPHA_TEST);
我有这个纹理,我想使用 Stencil 缓冲区将其用作蒙版:
然后我想在屏幕上绘制一个图像 IMG,它应该只出现在上面的图像可见的地方(有颜色的地方),因此不包括顶部边缘上方和渐变下方的透明像素。
问题是每当我在屏幕上绘制图像 IMG 时,无论像素是否透明,它都会出现在图像绘制的任何地方。
所以我考虑过使用 ALPHA_TEST
但它在最新的 OpenGL 版本中消失了,所以我试图在我的片段着色器中丢弃带有 v_color.a == 0
或 v_color.r == 0
的片段,但它确实如此没什么...
片段着色器:
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
uniform sampler2D u_texture;
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
void main() {
if (v_color.a == 0) {
discard;
}
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
}
顶点着色器:
attribute vec4 a_color;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = a_color;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
代码:
@Override
public void draw(final DrawConfig drawConfig) {
this.applyTransform(drawConfig.getBatch(), this.computeTransform());
// Draw mask to stencil buffer
drawConfig.end();
final GL20 gl = Gdx.gl;
gl.glEnable(GL20.GL_STENCIL_TEST);
gl.glStencilFunc(GL20.GL_ALWAYS, 1, 0xFF);
gl.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);
gl.glStencilMask(0xFF);
gl.glColorMask(false, false, false, false);
gl.glClearStencil(0);
gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
drawConfig.begin(Mode.BATCH);
this.mBackground.setClippingEnabled(true); // The clipping area is a rectangle centered in the screen
this.mBackground.draw(drawConfig);
this.mBottomBorder.draw(drawConfig); // Border below the rectangle area
this.mTopBorder.draw(drawConfig); // Border above the rectangle area
drawConfig.end();
gl.glColorMask(true, true, true, true);
gl.glStencilFunc(GL20.GL_EQUAL, 1, 0xFF);
gl.glStencilMask(0x00);
// Draw elements
this.mBackground.setClippingEnabled(false); // No clipping area, use stencil test
this.mBackground.draw(drawConfig);
this.mBottomBorder.draw(drawConfig);
this.mTopBorder.draw(drawConfig);
drawConfig.end();
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
this.resetTransform(drawConfig.getBatch());
}
结果:
红色箭头表示超出边界的部分,我想让它们消失。 黄色方块是裁剪区,方便大家使用。
您尝试复制的固定功能 alpha 测试 (GL_ALPHA_TEST
) 是针对每个片段的测试。它曾经在 片段着色器完成后 发生,您可以使用 discard
.
但是,您当前的解决方案是使用插值的每顶点 alpha 值,这与您尝试用作遮罩的纹理无关。您需要测试分配给 gl_FragColor
的 alpha 值才能正确执行此操作:
void main() {
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
if (gl_FragColor.a == 0.0) {
discard;
}
}
这是基于现代着色器的等价物:
glAlphaFunc (GL_NOTEQUAL, 0.0f); // Reject fragments with alpha == 0.0
glEnable (GL_ALPHA_TEST);