在Android上的OpenGL ES 2.0中使用着色器程序绘制轮廓

时间:2012-12-03 21:45:00

标签: android opengl-es-2.0 textures shader outline

我正在使用OpenGL ES 2.0(在Android上)绘制简单的2D场景,图像很少。我有背景图片和其他一些有alpha通道。

我想仅使用着色器程序在纹理中绘制非透明像素的轮廓。经过一番广泛的搜索后,我找不到示例代码。看起来GLES 2.0仍然不那么受欢迎。

您能提供一些示例代码或指出正确的方向,我可以找到有关如何执行此操作的更多信息吗?

3 个答案:

答案 0 :(得分:2)

要使像素着色器绘制某些东西,需要有几何体。 据我所知,你想在这些图像周围画一个边框, 但生成的最外层片段是基本实现中的图像像素, 所以你要用任何边框透支它们。

如果你想要一个'直线边框',除了绘制图像三角形/四边形(GL_TRIANGLES,GL_QUADS)之外你不能做任何其他事情,并且在另一个调用中可以做大纲(使用GL_LINES),你可以在这里分享一个顶点。单四边形。 考虑一下,许多GPU无法有效地绘制线条)

否则,请参阅以下解决方案:

解决方案1:

  • 绘制与图像+边框一样大的矩形,并调整图像的纹理坐标,使其适当地放置在矩形内。 这样,就不需要额外的几何或绘制调用。
  • 设置纹理边框属性(单个4分量颜色),不需要做额外的片段着色器计算,纹理单元/采样器完成所有工作。

纹理属性:

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_BORDER)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_BORDER)
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,borderColor4f)

我从未对单个通道纹理使用颜色边框,因此需要验证此方法。

解决方案2:

与1类似,但要在片段着色器中进行计算以检查,纹理坐标是否在边框区域内,而不是纹理边框。没有修改,纹理坐标的标量范围从0.0到1.0。

纹理属性可能是:

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP)

片段颜色可以通过以下任何方法确定:

  • 矩形的另一个边框颜色属性,其中选择了纹素或边框颜色(可以是顶点属性,但更可能是均匀或常量)。

  • 将alpha纹理与第二个纹理组合作为整个矩形的背景(如图片框),此处也选择了纹素。

  • 其他一些数学函数

当然,颜色值可以混合用于图像/边框渐变。

编辑:

由于此类轮廓线段的数量,长度和位置会有所不同,甚至可能形成凹形,因此您需要使用几何着色器执行此操作,这在ES 2.0内核中不可用。您可以做的最好的事情是为CPU上的每个图像预先计算一个线路循环。在着色器中进行此类测试效率相当低甚至过度,具体取决于图像大小,实际运行它的硬件等。如果您使用顶点着色器绘制固定数量的线段并对其进行变换,则无法正确执行覆盖所有情况,至少不是没有巨大的努力和GPU工作量。

如果您打算更改相应纹理像素的颜色值,则片段着色器需要为每个相邻像素提取大量且不同数量的纹素,朝向纹理边缘,就像在所有其他实现中一样。这种强力技术通常是递归和迭代算法的替代,对于这些算法,CPU是更好的选择。因此,我建议您通过修改纹理或在片段着色器中生成第二个组合来实现。

基本上,你需要实现一个路径查找算法,它试图“绕过”任意边缘的不透明像素。

答案 1 :(得分:2)

根据a)Qaulity和b)你需要的速度,有几种方法可以做到这一点。常见的搜索字词是:

  • "发光大纲"
  • "开花"
  • "香椿着色器"或"香椿阴影"
  • "边缘检测"
  • "剪影提取"
  • "掩模"

1)传统的方法是使用模板缓冲区并渲染到纹理

  • 清除模板缓冲区(通常每帧执行一次)

    glClear( GL_COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT )
    
  • 渲染到纹理

  • 禁用深度写入

    glDepthMask( 1 );
    
  • 禁用颜色缓冲区写入

    glColorMask( 0, 0, 0, 0 );
    
  • 启用模板缓冲区设置模板以始终传递和替换

    glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
    glStencilFunc( GL_ALWAYS, 1, 1 );
    
  • 将对象绘制到纹理

  • 禁用模板
  • 启用颜色缓冲区写入
  • 启用深度写入
  • 进行N次通过"点击",例如5或7次点击,通过在垂直和水平方向上渲染自身来模糊纹理(另一种选择是缩放绘制纹理图像)向上)
  • 切换到正投影
  • 画&将纹理图像混合回帧缓冲区
  • 恢复透视投影

2)沿着额外的顶点传递,即哪个顶点以正确的缠绕顺序相邻,并动态生成额外的轮廓三角形。

请参阅:http://www.gamasutra.com/view/feature/1644/sponsored_feature_inking_the_.php?print=1

3)使用便宜的边缘检测。在顶点着色器中,检查法线与视图的点积。如果它介于:

之间
-epsilon < 0 < epsilon

然后你有优势。

4)使用cheap-o-rama对象缩放。当然,它对凹面物体不起作用,但根据你的质量需求可能足够好&#34;

  • 切换到&#34; flat&#34;着色器
  • 启用Alpha测试
  • 略微缩放模型
  • 禁用Alpha测试
  • 以正常尺寸绘制模型

参考文献:

相关的SO问题:

答案 2 :(得分:0)

您的Alpha通道可以看作是灰度图像。寻找任何边缘检测/绘图算法。例如Canny边缘检测器(http://en.wikipedia.org/wiki/Canny_edge_detector)。或者也许更好的想法,如果你的图像不是程序性的,那就是预先计算边缘。

如果您的目标是混合各种图像,然后根据该混合的结果应用轮廓,请尝试渲染到纹理,然后在屏幕上再次渲染该纹理并执行边缘检测算法。