Direct3D 线条粗细

17

如何在使用Direct3D绘制线列表时更改线条的粗细?

这篇文章指出线宽不受支持,并提供了一种解决方法。还有其他选项吗?

顺便问一下,着色器是否允许绘制带有虚线图案的线条?

4个回答

17
您可以使用几何着色器,将线段作为输入,输出一个四边形(由两个三角形组成的三角形带),以使四边形的宽度在屏幕空间中保持恒定,并符合所需的线条粗细。它完美地运行(因为我已经在 CAD 3D 引擎中实现过)。
如果几何着色器不可行,则解决方法可能是使用顶点着色器,但这将需要对您的 VB 进行一些重新设计。请记住,VS 必须了解线段的整体情况,因此您将最终为每个 VB 元素存储 p 和 p+1,再加上重复索引/顶点的成本(取决于使用的拓扑和是否将您的线路呈现为索引原语)。
如果性能不是问题,那么在 CPU 上进行扩展可能是正确的选择。
关于破折号样式:您也可以使用几何着色器来模拟 glLineStipple 的行为。如果您具有 GL_LINES 拓扑,也就是孤立的线段,则该图案在每个新的线段处重新开始。因此,您只需要在几何着色器中计算您的线段的屏幕空间水平起始位置(或垂直起始位置,具体取决于方向),并将此额外信息传递给像素着色器。然后,像素着色器将负责根据因子和模式值(DirectX 10/11 整数和按位指令使其变得容易)丢弃片段。
同样,这很有效,并且您可以将其与上述第一种技术中的模拟宽线条相结合使用。
现在,如果您具有 GL_LINE_STRIP 拓扑,则该模式在每个新原语(因此对于每个新绘制调用)重新启动。由于需要了解每个线段之前已呈现的像素数量,因此情况变得更加复杂。
您可以使用 DirectX 10 流输出功能,在临时 VB 中渲染您的线带(该 VB 的每个元素对应于每个段的屏幕空间长度)。然后,您必须对此 VB 进行并行前缀和(又称扫描),以累积每个线段长度值。最后,您可以像使用GL_LINES一样渲染线条带,但是在PS中使用这些额外的扫描VB信息。

17

线条粗细当前在Direct3D和任何现有的GPU中都不受支持。我所知道的没有一个GPU可以正确地画出直线(它们都使用退化多边形伪造线 - 也就是第二个和第三个顶点位于同一位置,因此三角形基本上会变成单条线)。

虽然在OpenGL中可以设置线宽,但OpenGL驱动程序创建了外围四边形(也不被任何当前的GPU支持,并且用每个四边形的两个三角形来模拟),将“线条”画在屏幕上。

因此,可能没有办法避免为此目的创建外围四边形。正如Stringer Bell在他的回答中所解释的那样,有几种方法可以实现这一点。

对我而言,最简单的方法是创建一个顶点缓冲区,其中每个顶点都出现两次,其法线指向“右”或“左”,具体取决于它们是线的右侧还是左侧边缘。然后,非常简单的顶点着色器可以通过将顶点位置改变其法向量的倍数来执行挤压操作。这样,您可以快速更改线宽,而无需在CPU上重新计算几何图形。如果想根据对象的大小或距离调整线宽,这可能会有用。


这是一个有趣的解决方案。你是否成功地实现了与 glLineWidth 调用(按像素计)相同的结果? - elmattic
我没有逐像素检查它,但对于未纹理化的抗锯齿线条,结果应该足够相似。使用纹理(例如模拟OpenGL的点画图案)几乎肯定会让你陷入痛苦的世界,因为纹理坐标的非线性插值(即使与大多数相同的Geforce部分相比,NVidia Quadros处理得非常糟糕)。着色器需要执行大量额外的工作才能正确获取纹理坐标。 - Pepor
你可以自己尝试一下,只需使用一些“屏幕像素线长”的纹理坐标,并使用具有点画图案的纹理绘制线条。你会惊讶于图案是多么不平均。至少我很惊讶。;-) - Pepor

2

{{link1:"ID3DXLine接口实现使用纹理三角形进行线绘制。"}}


0
关于您的虚线模式(stippling)问题,最好的方法可能是将线渲染为细小的四边形,并在像素着色器中应用纹理,其中纹理包含您的虚线模式。您需要将采样器地址模式设置为wrap,类似于:
SamplerState wrapTriLinear {
    AddressU = WRAP;
    AddressV = WRAP;
}

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