OpenGL模具-排除透明像素 [英] OpenGL Stencil - Exclude transparent pixels
问题描述
我有想要使用模板缓冲区用作蒙版的纹理:
I have this texture that I want to use as as mask using the Stencil buffer :
然后我想在屏幕上绘制图像IMG,该图像应仅在可见上述图像(有颜色)的地方出现,从而排除顶部边缘上方和渐变以下的透明像素.
Then I want to draw an image IMG on the screen which should appear only where the above image is visible (where there's color), thus excluding transparent pixels above the top edge and below the gradient.
问题是,每当我在屏幕上绘制图像IMG时,它都会出现在绘制图像的任何地方,无论像素是否透明.
The problem is whenever I draw my image IMG on the screen it appears everywhere where the image has been drawn, whether the pixels are transparent or not.
所以我考虑过使用ALPHA_TEST
,但是它在最新的OpenGL版本中已经消失了,所以我试图在片段着色器中使用v_color.a == 0
或v_color.r == 0
丢弃片段,但是它什么也没做...
So I thought about using the ALPHA_TEST
but it's gone in the latest OpenGL versions, so I tried to discard fragment with v_color.a == 0
or v_color.r == 0
in my fragment shader but it does nothing...
片段着色器:
#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());
}
结果:
红色箭头表示超出边界的部分,而我想使其消失. 为了方便起见,黄色正方形是剪切区域.
The red arrows show the parts that go outside of the borders and that I want to make disappear. The yellow square is the clipping area, for your convenience.
推荐答案
您要复制的固定功能alpha测试(GL_ALPHA_TEST
)是每个片段测试.它曾经在片段着色器完成后 发生,您可以使用discard
自己实现.
The fixed-function alpha test (GL_ALPHA_TEST
) you are trying to duplicate was a per-fragment test. It used to occur after the fragment shader finished, and you can implement it yourself using discard
.
但是,当前的解决方案使用的是插入的每个顶点的alpha值,这与您试图用作蒙版的纹理无关.您需要测试分配给gl_FragColor
的alpha值才能正确执行此操作:
However, your current solution is using the interpolated per-vertex alpha value, and that has nothing to do with the texture you are trying to use as a mask. You need to test the alpha value assigned to gl_FragColor
to do this properly:
void main() {
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
if (gl_FragColor.a == 0.0) {
discard;
}
}
这是基于着色器的现代等效项:
That is the modern shader-based equivalent of:
glAlphaFunc (GL_NOTEQUAL, 0.0f); // Reject fragments with alpha == 0.0
glEnable (GL_ALPHA_TEST);
这篇关于OpenGL模具-排除透明像素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!