我最近开始研究适用于Android的OpenGL ES,并正在开发绘图应用程序。我已经实现了一些基础知识,如点精灵,路径平滑和FBO双缓冲。目前我正在使用glBlendFunc,更具体地说,当我使用相同的颜色/ alpha值将两个纹理彼此靠近时,alpha会被添加,因此它会在精灵的交叉处显得更暗。这是一个问题,因为如果很多点靠近在一起,则不会保留笔划不透明度,因为颜色往往更不透明而不是保持相同的不透明度。有没有办法让纹理在交点上具有相同的颜色,即交叉像素具有相同的alpha值,但保留其余像素的alpha值?
以下是我如何完成应用的相关部分:
用于绘制点精灵列表我使用这样的混合:
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
该应用程序使用带纹理的FBO,在此处首先渲染每个笔刷笔触,然后将此纹理渲染到主屏幕。混合函数有:
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
OpenGL ES 2.0不支持alpha掩码;
我的片段着色器看起来像这样:
precision mediump float;
uniform sampler2D uShapeTexture;
uniform sampler2D uFillTexture;
uniform float vFillScale;
varying vec4 vColor;
varying float vShapeRotation;
varying float vFillRotation;
varying vec4 vFillPosition;
vec2 calculateRotation(float rotationValue) {
float mid = 0.5;
return vec2(cos(rotationValue) * (gl_PointCoord.x - mid) + sin(rotationValue) * (gl_PointCoord.y - mid) + mid,
cos(rotationValue) * (gl_PointCoord.y - mid) - sin(rotationValue) * (gl_PointCoord.x - mid) + mid);
}
void main() {
// Calculations.
vec2 rotatedShape = calculateRotation(vShapeRotation);
vec2 rotatedFill = calculateRotation(vFillRotation);
vec2 scaleVector = vec2(vFillScale, vFillScale);
vec2 positionVector = vec2(vFillPosition[0], vFillPosition[1]);
// Obtain colors.
vec4 colorShape = texture2D(uShapeTexture, rotatedShape);
vec4 colorFill = texture2D(uFillTexture, (rotatedFill * scaleVector) + positionVector);
gl_FragColor = colorShape * colorFill * vColor;
}
我的顶点着色器是这样的:
attribute vec4 aPosition;
attribute vec4 aColor;
attribute vec4 aJitter;
attribute float aShapeRotation;
attribute float aFillRotation;
attribute vec4 aFillPosition;
attribute float aPointSize;
varying vec4 vColor;
varying float vShapeRotation;
varying float vFillRotation;
varying vec4 vFillPosition;
uniform mat4 uMVPMatrix;
void main() {
// Sey position and size.
gl_Position = uMVPMatrix * (aPosition + aJitter);
gl_PointSize = aPointSize;
// Pass values to fragment shader.
vColor = aColor;
vShapeRotation = aShapeRotation;
vFillRotation = aFillRotation;
vFillPosition = aFillPosition;
}
我尝试过使用glBlendFunc参数但我找不到合适的组合来绘制我想要的东西。我附上了一些图片,展示了我想要实现的目标以及我目前拥有的内容。有什么建议吗?
解决方案
由于@ Rabbid76,最后设法通过几行来正常工作。首先,在绘制到FBO之前,我必须配置深度测试功能:
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LESS);
// Drawing code for FBO.
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
然后在我的片段着色器中,我必须确保所有具有alpha<掩码中的1被丢弃如下:
...
vec4 colorMask = texture2D(uMaskTexture, gl_PointCoord);
if (colorMask.a < 1.0)
discard;
else
gl_FragColor = calculatedColor;
结果是(闪烁是由于Android模拟器和gif捕获工具):
答案 0 :(得分:1)
如果设置glBlendFunc
使用函数(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
并使用
glBlendEquation使用等式GL_FUNC_ADD
,然后是目标颜色
计算如下:
C_dest = C_src * A_src + C_dest * (1-A_src)
如果您将C_dest = 1
与C_src = 0.5
和A_src = 0.5
混为一谈,那么:
C_dest = 0.75 = 1 * 0.5 + 0.5 * 0.5
如果您重复混合相同颜色C_src = 0.5
和A_src = 0.5
,则目标颜色变为更暗:
C_dest = 0.625 = 0.75 * 0.5 + 0.5 * 0.5
由于新目标颜色始终是原始目标颜色和源颜色的函数,因此在混合2次时颜色不能保持等同,因为目标颜色在第一次混合后已经更改({{1除外) }})。
您必须避免将任何片段混合两次。如果所有片段都绘制到相同的深度(2D),那么您可以使用深度测试:
GL_ZERO
或者可以使用模板测试。例如,模板测试可以设置为仅在模板缓冲区等于0时通过。 每次要写入片段时,模板缓冲区都会递增:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );
// do the drawing with the color
glDisable( GL_DEPTH_TEST );
请注意,您可以discard不应绘制的片段。 如果精灵纹理中的片段的alpha通道为0,则应将其丢弃。
注意,如果丢弃片段,则不会写入颜色,深度和模板缓冲区。
片段着色器还可以访问
glClear( GL_STENCIL_BUFFER_BIT ); glEnable( GL_STENCIL_TEST ); glStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); glStencilFunc( GL_EQUAL, 0, 255 ); // do the drawing with the color glDisable( GL_STENCIL_TEST );
命令。执行时,此命令会导致丢弃片段的输出值。因此,片段不会继续进行下一个流水线阶段,并且任何片段着色器输出都会丢失。
片段着色器
discard
答案 1 :(得分:0)
使用OpenGL ES 2.0中的固定功能混合无法做到这一点,因为你想要的并不是实际的alpha混合。你想要的是一个逻辑操作(例如max(src,dst)),这与OpenGL ES混合的工作方式有很大不同。
如果你想用像素精确边进行路径/笔触/填充渲染,你可能会使用模板蒙版和模板测试,但在这种情况下你不能做透明 - 只是布尔运算符。