防止一个ERG的像素着色器过度绘制

7

背景

使用gluTess从GDI+ DrawString(..)路径构建三角形列表以在Direct3D9中绘制:

tessellated text

然后使用像素着色器(v3.0)填充形状。当使用不透明值绘制时,一切看起来都很好:

opaque output

问题

在某些字体大小下,如果颜色有一个alpha分量(即Argb #55FFFFFF),我们开始看到这些讨厌的镶嵌工艺品,其中三角形可能会略微重叠:

transparent output

在较大的字体大小下,该问题有时不存在:

larger font test

使用英特尔出色的GPA Frame Analyzer Pixel History工具,我们可以看到在出现工件的区域,像素已经从单个Erg"触摸"了3次。

Erg analysis

我正在努力找出如何阻止我的像素着色器多次接触同一像素。

其他与遮盖预防相关的解决方案似乎都关注于z缓冲器策略,然而这个问题更多是关于在单个像素着色器通道中绘制单个2D三角形列表的问题。

我在尝试想出一个解决方案时有点迷茫。我希望HLSL可能有类似于“每个像素只触摸一次”的标志,但我找不到任何类似的东西。我找到的最接近的东西是将BLENDOP设置为MAX而不是ADD。但当在场景中混合其他颜色时,输出是不正确的。

我还设置了SRCBLEND = ONE和DSTBLEND = INVSRCALPHA。唯一一组标志组合会产生正确的输出(虽然有过度绘制工件)。

我已经使用GPAframe analyzer中的SEPARATEALPHABLENDENABLE进行了调整,听起来几乎正是我需要的——将混合设置为MAX,但仅在"alpha"通道上,然而从我所能确定的情况来看,那种设置(以及相应的BLENDOPALPHA)根本不会影响任何东西。

我最后想到的是将文本作为不透明的烘焙到纹理中,然后使用适当的alpha值将该纹理重新绘制到场景中,但是这在这个项目中实际上并不起作用,因为我还支持渐变刷子,其中停止值可能包含alpha,这意味着无论如何都会看到工件,或者如果我们在烘焙到纹理之前剥离了停止值中的alpha,最终输出就完全错误。此外,整个过程将非常昂贵。

任何提示或指针将不胜感激。谢谢你的阅读。

1个回答

1
你看到的问题不应该发生。
如果两个三角形重叠,那是因为你放置顶点的方式使相邻的三角形在绘制时重叠。可能正在发生的是,这两个相邻的三角形共享两个顶点,但每个三角形都有自己的顶点副本,计算出来的位置略微不同。
解决问题的方法不是尝试让像素着色器仅在触摸像素时才进行处理,而是使用索引缓冲区(如果还没有使用),并使每个三角形之间共享实际上共享相同的顶点,而不是使用一个与相邻三角形中使用的顶点稍微不在同一位置的顶点。
如果您无法控制正在使用的镶嵌算法,则可能需要在生成后运行一次遍历顶点缓冲区,以检测并合并相互之间非常接近的顶点。即使没有索引缓冲区,一个朴素的解决方案是:
1. 对于顶点缓冲区中的每个顶点,将其位置与其余顶点的每个位置进行比较。 2. 如果两个顶点彼此之间的距离小于某个很小的容差范围,则用第一个顶点的位置替换第二个顶点的位置。
这将使两个顶点位置匹配,如果它们足够接近,您认为它们是相同的。
现在,您不应该遇到任何重叠三角形的问题。在日常渲染中,两个三角形彼此共享边缘,并且您永远不会出现它们似乎略微重叠的效果。硬件保证样本点始终在直线的一侧或另一侧,而不会同时在两侧,无论该点离直线有多近(即使它在数学上处于直线上,仍然会在一侧或另一侧失败)。

谢谢Adam,是的,重叠实际上是由于我刚刚发现的AA挤出/内插点--如果我将偏移量从0.25减少到0.05,这些伪影就会消失,但外边缘当然会更加锯齿状。我认为你绝对正确,问题需要在数据源中解决,而不是试图让像素着色器清理那些不规则的点。再次感谢。 - Sichbo
在尝试了合并附近顶点后,我发现它实际上只是撤销了反锯齿,因为插入/挤出的边缘变得折叠。同时发现,对于路径反锯齿,边缘插入/插入总会导致重叠,特别是在非常细的1-2像素宽度的字形(想想Segoe UI Light)。我可以通过仅挤出而永远不插入来消除伪影,但结果是明显"肥胖"的文本。想必最终需要一个像素着色器解决方案,或者也许我应该重新考虑我的AA策略,以便不进行任何路径操作。 - Sichbo
你使用什么公差来决定合并?我可以想象出围绕字符的挤出边/边界可能会有非常接近的顶点(但故意分开,以便您实际上看到淡化),但不会太接近以至于它们应该被合并。 字符本身内部的那些顶点可能相互之间相差不到0.0001f,而在边缘上我期望有一个可测量的间隙。是否有足够小的公差值可以消除字体内部的伪影,但又足够大,不会去除AA? - Adam Miles
无论使用什么公差,只要超过2像素(足以破坏形状),这些伪影就会存在。由于我在边界的两侧各挤压/拉伸0.25像素,小于0.50像素的公差几乎没有效果(可能有6个点在8字形上塌陷,但没有一个是过度绘制的原因)。公差更高则会导致抗锯齿崩溃。我发现的一种任意平衡(可能会采用)是挤出0.19999f并向内缩进0.09999f。从300像素粗体字体到12像素细字体,只有偶尔出现一些奇怪的像素。 - Sichbo
这些像素会让我发疯。我将继续尝试,看看能否识别出应该进行挤出的“边缘”与外部边缘相遇的情况。 - Sichbo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接