OpenGL 3.1 中已弃用 glLineStipple
glLineStipple
在最新的 OpenGL API 中已被弃用。 它被替换成什么? 如果不更换,怎样才能达到类似的效果呢? (我当然不想使用兼容性配置文件......)
glLineStipple
has been deprecated in the latest OpenGL APIs.
What is it replaced with?
If not replaced, how can I get a similar effect?
(I don't want to use a compatibility profile of course...)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
抱歉,它还没有被任何东西取代。我想到的第一个模拟它的想法是几何着色器。您向几何着色器提供一条线,计算其屏幕空间长度,并基于此在其起始顶点和结束顶点之间生成可变数量的子线。
编辑:也许您还可以使用带有 alpha(或红色)通道的 1D 纹理,将图案编码为 0.0(无线)或 1.0(线),然后让线纹理坐标从 0 到1 并在片段chader 中进行简单的alpha 测试,丢弃alpha 低于某个阈值的片段。您可以方便几何着色器生成线条 texCoords,否则每条线条都需要不同的顶点。通过这种方式,您还可以使 texCoord 依赖于线条的屏幕空间长度。
如果您绘制三角形(使用多边形模式
GL_LINE
),整个事情就会变得更加困难。然后,您必须在几何着色器中自己进行三角形线转换,放入三角形并输出直线(这也可能是将来弃用多边形模式的一个原因,如果还没有的话)。编辑:虽然我相信这个问题已经被放弃,但我已经为第二种方法制作了一个简单的着色器三元组。这只是一个最小的解决方案,您可以随意添加自定义功能。我没有测试它,因为我缺乏必要的硬件,但你应该明白这一点:
顶点着色器是一个简单的传递。
在几何着色器中,我们取一条线并计算其屏幕空间长度(以像素为单位)。然后,我们将其除以点画图案纹理的大小,当模拟对
glLineStipple(factor, pattern)
的调用时,该大小将为factor*16
。这被视为第二条线端点的一维纹理坐标。请注意,该纹理坐标必须进行线性插值(
noperspective
插值说明符)。通常的透视正确插值会导致点画图案在线条的较远部分“挤压在一起”,而我们正在明确地使用屏幕空间值。片段着色器现在仅使用图案纹理中的值执行简单的 alpha 测试,其中 1 表示线条,0 表示无线条。因此,要模拟固定功能点画,您将拥有 16 像素 1 分量 1D 纹理,而不是 16 位图案。不要忘记将模式的环绕模式设置为
GL_REPEAT
,关于过滤模式我不太确定,但我认为GL_NEAREST
将是一个好主意。但正如前面所说,如果你想使用glPolygonMode 渲染三角形,这种方式就行不通了。相反,您必须调整几何着色器以接受三角形并为每个三角形生成 3 条线。
编辑: 事实上,OpenGL 3 对着色器中整数运算的直接支持使我们能够完全放弃整个 1D 纹理方法,并直接使用实际的位模式进行工作。因此,几何着色器稍微改变以输出实际的屏幕大小图案坐标,而不进行标准化:
在片段着色器中,我们只将位模式作为无符号整数(尽管与glLineStipple相比是32位) > 的 16 位值)和图案的拉伸因子,只需将纹理坐标(好吧,实际上不再有纹理,但没关系)模 32 来获取它在图案上的位置(那些显式的 uint 很烦人,但我的 GLSL 编译器说
int
和uint
之间的隐式转换是邪恶的):Sorry, it hasn't been replaced with anything. The first idea coming to my mind for emulating it would be the geometry shader. You feed the geometry shader with a line, compute its screen space length and based on that you generate a variable number of sub lines between its start and end vertex.
EDIT: Perhaps you could also use a 1D texture with the alpha (or red) channel encoding the pattern as 0.0 (no line) or 1.0 (line) and then have the lines texture coordinate go from 0 to 1 and in the fragment chader you make a simple alpha test, discarding fragments with alpha below some threshold. You can facilitate the geometry shader to generate your line texCoords, as otherwise you need different vertices for every line. This way you can also make the texCoord dependent on the screen space length of the line.
The whole thing get's more difficult if you draw triangles (using polygon mode
GL_LINE
). Then you have to do the triangle-line transformation yourself in the geometry shader, putting in triangles and putting out lines (that could also be a reason for deprecating polygon mode in the future, if it hasn't already).EDIT: Although I believe this question abandomned, I have made a simple shader triple for the second approach. It's just a minimal solution, feel free to add custom features yourself. I haven't tested it because I lack the neccessary hardware, but you should get the point:
The vertex shader is a simple pass through.
In the geometry shader we take a line and compute its screen space length in pixels. We then devide this by the size of the stipple pattern texture, which would be
factor*16
when emulating a call toglLineStipple(factor, pattern)
. This is taken as 1D texture coordinate of the second line end point.Note that this texture coordinate has to be interpolated linearly (
noperspective
interpolation specifier). The usual perpective-correct interpolation would cause the stipple pattern to "squeeze together" on farther away parts of the line, whereas we are explicitly working with screen-space values.The fragment shader now just performs a simple alpha test using the value from the pattern texture, which contains a 1 for line and a 0 for no line. So to emulate the fixed function stipple you would have a 16 pixel 1-component 1D texture instead of a 16bit pattern. Don't forget to set the pattern's wrapping mode to
GL_REPEAT
, about the filtering mode I'm not that sure, but I supposeGL_NEAREST
would be a good idea.But as said earlier, if you want to render triangles using
glPolygonMode
, it won't work this way. Instead you have to adapt the geometry shader to accept triangles and generate 3 lines for each triangle.EDIT: In fact OpenGL 3's direct support for integer operations in shaders allows us to completely drop this whole 1D-texture approach and work straight-forward with an actual bit-pattern. Thus the geometry shader is slightly changed to put out the actual screen-size pattern coordinate, without normalization:
In the fragment shader we then just take a bit pattern as unsigned integer (though 32-bit in contrast to
glLineStipple
's 16-bit value) and the stretch factor of the pattern and just take the texture coordinate (well, no texture anymore actually, but nevermind) modulo 32 to get it's position on the pattern (those explicituint
s are annoying, but my GLSL compiler says implicit conversions betweenint
anduint
are evil):要回答这个问题,我们首先要调查一下
glLineStipple
实际上确实如此。请参见图像,其中左侧的四边形是使用基元类型 由 4 个独立的线段绘制的
GL_LINES
。右侧的圆是由连续的多边形线绘制的,使用基本类型
GL_LINE_STRIP
。< /a>
使用线段时,点画图案从每个线段开始。该模式在每个基元处重新启动。
使用线条时,点画图案将无缝应用于整个多边形。超越顶点坐标无缝连续的图案。
请注意,图案的长度在对角线上被拉伸。这可能是实施的关键。
对于单独的线段,这根本不是很复杂,但是对于线带,事情会变得有点复杂。如果不知道线条的所有图元,则无法在着色器程序中计算线条的长度。即使所有原语都是已知的(例如 SSBO),那么计算也必须在循环中完成。
另请参阅带有 OpenGL 核心配置文件的虚线。
无论如何,没有必要实现几何着色器。诀窍是知道片段着色器中线段的起点。通过使用
flat
插值即可轻松实现预选赛。顶点着色器必须将标准化的设备坐标传递给片段着色器。一次使用默认插值,一次不使用(平坦)插值。这导致在片段阴影中,第一个输入参数包含线条上实际位置的 NDC 坐标,后面包含线条起点的 NDC 坐标。
除了变化的输入之外,片段着色器还具有统一的变量。
u_resolution
包含视口的宽度和高度。u_factor
和u_pattern
是根据glLineStipple
。因此,可以计算从开始到实际片段的线的长度:
并且可以通过
放弃
命令。片段着色器:
与使用几何着色器相比,此实现更容易、更短。自 GLSL 起支持
flat
插值限定符1.30 和 GLSL ES 3.00。在此版本中不支持几何着色器。查看使用上述着色器生成的线条渲染。
< /a>
着色器给出正确的结果线段,但对于线带失败,因为点画图案在每个顶点坐标处重新启动。
这个问题甚至不能通过几何着色器来解决。这部分问题仍然没有解决。
对于以下简单的演示程序,我使用了 GLFW API 来创建窗口,GLEW 用于加载 OpenGL 和 GLM -OpenGL Mathematics 用于数学计算。我不提供函数
CreateProgram
的代码,它只是从顶点着色器和片段着色器源代码创建一个程序对象:另请参阅
OpenGL3 中的虚线?
OpenGL ES - 虚线
To answer this question, we've to investigate first, what
glLineStipple
actually does.See the image, where the quad at the left is drawn by 4 separated line segments using the primitive type
GL_LINES
.The circle at the right is drawn by a consecutive polygon line, using the primitive type
GL_LINE_STRIP
.When using line segments, the stipple pattern started at each segment. The pattern is restarted at each primitive.
When using a line strip, then the stipple pattern is applied seamless to the entire polygon. A pattern seamlessly continuous beyond vertex coordinates.
Be aware that the length of the pattern is stretched at the diagonals. This is possibly the key to the implementation.
For separate line segments, this is not very complicated at all, but for line strips things get a bit more complicated. The length of the line cannot be calculated in the shader program, without knowing all the primitives of the line. Even if all the primitives would be known (e.g. SSBO), then the calculation would have to be done in a loop.
See also Dashed lines with OpenGL core profile.
Anyway, it is not necessary to implement a geometry shader. The trick is to know the start of the line segment in the fragment shader. This easy by using a
flat
interpolation qualifier.The vertex shader has to pass the normalized device coordinate to the fragment shader. Once with default interpolation and once with no (flat) interpolation. This causes that in the fragment shade, the first input parameter contains the NDC coordinate of the actual position on the line and the later the NDC coordinate of the start of the line.
Additionally the varying inputs, the fragment shader has uniform variables.
u_resolution
contains the width and the height of the viewport.u_factor
andu_pattern
are the multiplier and the 16 bit pattern according to the parameters ofglLineStipple
.So the length of the line from the start to the actual fragment can be calculated:
And fragment on the gap can be discarded, by the
discard
command.Fragment shader:
This implementation is much easier and shorter, then using geometry shaders. The
flat
interpolation qualifier is supported since GLSL 1.30 and GLSL ES 3.00. In this version geometry shaders are not supported.See the line rendering which was generated with the above shader.
The shader gives a proper result line segments, but fails for line strips, since the stipple pattern is restarted at each vertex coordinate.
The issue can't even be solved by a geometry shader. This part of the question remains still unresolved.
For the following simple demo program I've used the GLFW API for creating a window, GLEW for loading OpenGL and GLM -OpenGL Mathematics for the math. I don't provide the code for the function
CreateProgram
, which just creates a program object, from the vertex shader and fragment shader source code:See also
Dashed line in OpenGL3?
OpenGL ES - Dashed Lines
由于我花了一些功夫(没有双关语)才把它做好,我认为如果我分享我基于 Christian Rau 版本的一组点画着色器的实现,这可能对其他人有用。
为了控制图案密度,片段着色器需要视口每单位长度的图案数
nPatterns
- 而不是设置一个因子。还包括可选的剪切平面功能。剩下的主要是注释和清理。
免费用于所有意图和目的。
顶点着色器:
几何着色器:
片段着色器:
Since I struggled a bit (no pun intended) to get it right, I thought it could be useful to others if I shared my implementation of a set of stippling shaders based on Christian Rau's version.
To control pattern density, the fragment shader requires the number of patterns
nPatterns
per unit length of the viewport - instead of setting a factor. Also included is an optional clipping plane feature.The rest is mainly commenting and cleaning.
Free to use to all intents and purposes.
The vertex shader:
The geometry shader:
The fragment shader: