【问题标题】:Preventing pixelshader overdraw for a single ERG防止单个 ERG 的像素着色器过度绘制
【发布时间】:2014-01-10 07:49:06
【问题描述】:

背景

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

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

问题

在某些字体大小下,如果颜色具有 alpha 分量(即 Argb #55FFFFFF),我们会开始看到这些令人讨厌的曲面细分工件,其中三角形可能会略微重叠:

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

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

我正在尝试弄清楚如何阻止像素着色器多次触摸同一个像素。

其他与防止过度绘制相关的解决方案似乎都是关于 zbuffer 策略的,但是这个问题更多地与在单个像素着色器通道中绘制单个 2D 三角形列表有关。

我在试图提出解决方案时有点不知所措。我希望 HLSL 可能有某种“每个像素只触摸一次”标志,但我一直找不到类似的东西。我发现最接近的是将 BLENDOP 设置为 MAX 而不是 ADD。但是当混合场景中的其他颜色时,输出不正确。

我也有 SRCBLEND = ONE,DSTBLEND = INVSRCALPHA。产生正确输出的唯一标志组合(尽管有过度绘制工件。)

我在 GPA 帧分析器中使用过 SEPARATEALPHABLENDENABLE,听起来几乎正是我在这里所需要的——将混合设置为 MAX,但仅在“alpha”通道上,但是从我能做到的确定,该设置(和相应的 BLENDOPALPHA)完全没有影响。

我想到的最后一件事是将不透明的文本烘焙到纹理上,然后应用适当的 alpha 值将该纹理重新绘制到场景中,但这实际上在这个项目中不起作用,因为我还支持渐变画笔,其中停止值可能包含 alpha,这意味着如果我们在烘焙到纹理之前将 alpha 从停止值中剥离,则仍然可以看到伪影,或者最终输出完全错误。此外,整个努力的代价也非常高昂。

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

【问题讨论】:

    标签: direct3d hlsl pixel-shader tessellation


    【解决方案1】:

    您看到的问题不应该发生。

    如果您的两个三角形重叠,那是因为您放置顶点的​​方式是在绘制相邻三角形时,它们会重叠。可能发生的情况是这两个相邻的三角形共享两个顶点,但每个三角形都有自己的每个顶点的副本,这些副本被计算在一个非常非常细微的不同位置。

    问题的解决方案不是尝试让像素着色器仅在使用索引缓冲区(如果您还没有使用索引缓冲区)时才接触像素,并且每个三角形之间的共享顶点实际上共享相同的顶点并且不要使用与相邻三角形使用的位置稍有不同的位置。

    如果您无法控制所使用的曲面细分算法,您可能必须在生成顶点缓冲区后对其进行遍历,以检测和合并在彼此之间的一些非常小的容差范围内的顶点。即使没有索引缓冲区,一个简单的解决方案是这样的:

    1. 对于顶点缓冲区中的每个顶点,将其位置与其余顶点缓冲区中的每个其他顶点进行比较。

    2. 如果两个顶点与另一个顶点的公差处于较小的公差范围内,请将第二个顶点的位置替换为您与之比较的那个顶点的位置。

    这应该具有配对两个顶点的位置的效果,如果它们足够接近以至于您认为它们是相同的。

    您现在应该对重叠三角形没有任何问题。在日常渲染中,两个三角形一直彼此共享边缘,你永远不会得到它们看起来几乎重叠的效果。硬件保证一个采样点要么在线条的一侧,要么在另一侧,但绝不会同时在两者之间,无论该点离线条有多近(即使它在数学上 on这条线,它仍然在一侧或另一侧失败)。

    【讨论】:

    • 谢谢亚当,是的,重叠实际上来自我刚刚发现的 AA 挤压/侵入点——如果我将偏移量从 0.25 减少到 0.05,工件就消失了,但在外面边缘当然更加锯齿状。我认为你是绝对正确的,因为问题需要在数据源中解决,而不是试图让像素着色器清理马虎点,可以这么说。再次感谢伙计。
    • 在玩过这个(合并附近的顶点)之后,我发现它所做的只是撤消抗锯齿,因为侵入/挤出的边缘会塌陷。还发现路径抗锯齿的边缘插入/插入总是会导致重叠,当它下降到非常薄的 1-2 像素宽的字体字形时(想想 Segoe UI Light。)我可以通过只挤出而不是插入来消除伪影,但结果是明显“更胖”的文本。估计像素着色器解决方案毕竟是合适的,或者我应该重新考虑我的 AA 策略,这样就不会发生路径操作。
    • 您使用什么容差来决定合并?我可以想象角色周围的突出边缘/边框可能如何使顶点非常靠近(但故意相距足够远以至于您确实看到了淡入淡出),但又不会太近以至于它们应该合并。角色本身内部的距离可能在 0.0001f 之内,而我希望边缘上有一个可测量的间隙。是否有一个容差值足够小,可以消除字体内部的伪影,但又足够大,不会删除 AA?
    • 使用什么容差似乎无关紧要,多达 2px(足以破坏形状)并且工件实际上仍然存在。由于我在边界的任一侧挤压/侵入 0.25px,任何小于 0.50px 的容差都几乎没有作用(图 8 上可能有 6 个点塌陷,这不是透支的原因。)更高,它会塌陷 AA。我发现(可能只是滚动)的任意平衡是挤压 0.19999f 并侵入 0.09999f。从 300 像素的胖字体到 12 像素的瘦字体,一切看起来都还不错,只是弹出了奇怪的 gimpy 像素。
    • 那些像素会让我发疯。我将继续玩弄,看看我是否无法识别与外边缘相交的“边缘”,无论如何,外边缘应该是唯一的挤压候选者。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 2018-02-28
    • 2013-01-03
    • 1970-01-01
    • 2019-10-23
    • 2012-12-14
    • 1970-01-01
    相关资源
    最近更新 更多